blob: 3665dd8781018a718e6b520135590f6420e333d9 [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) {
116 SkPDFDevice::OriginTransform flip = SkPDFDevice::kFlip_OriginTransform;
117 if (isForLayer) {
118 flip = SkPDFDevice::kNoFlip_OriginTransform;
119 }
120 return SkNEW_ARGS(SkPDFDevice, (width, height, flip));
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000121}
122
reed@android.comf2b98d62010-12-20 18:26:13 +0000123static inline SkBitmap makeABitmap(int width, int height) {
124 SkBitmap bitmap;
125 bitmap.setConfig(SkBitmap::kNo_Config, width, height);
126 return bitmap;
127}
128
vandebo@chromium.orgf60a0012011-02-24 23:14:04 +0000129SkPDFDevice::SkPDFDevice(int width, int height, OriginTransform flipOrigin)
reed@google.com07700442010-12-20 19:46:07 +0000130 : SkDevice(NULL, makeABitmap(width, height), false),
vandebo@chromium.org48543272011-02-08 19:28:07 +0000131 fWidth(width),
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000132 fHeight(height),
vandebo@chromium.orgf60a0012011-02-24 23:14:04 +0000133 fFlipOrigin(flipOrigin),
reed@google.com07700442010-12-20 19:46:07 +0000134 fGraphicStackIndex(0) {
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000135 fGraphicStack[0].fColor = SK_ColorBLACK;
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000136 fGraphicStack[0].fTextSize = SK_ScalarNaN; // This has no default value.
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000137 fGraphicStack[0].fTextScaleX = SK_Scalar1;
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000138 fGraphicStack[0].fTextFill = SkPaint::kFill_Style;
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000139 fGraphicStack[0].fFont = NULL;
vandebo@chromium.orgda912d62011-03-08 18:31:02 +0000140 fGraphicStack[0].fShader = NULL;
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000141 fGraphicStack[0].fGraphicState = NULL;
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000142 fGraphicStack[0].fClip.setRect(0,0, width, height);
143 fGraphicStack[0].fTransform.reset();
vandebo@chromium.orgf60a0012011-02-24 23:14:04 +0000144
145 if (flipOrigin == kFlip_OriginTransform) {
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000146 fContent.writeText("1 0 0 -1 0 ");
147 fContent.writeDecAsText(fHeight);
148 fContent.writeText(" cm\n");
vandebo@chromium.orgf60a0012011-02-24 23:14:04 +0000149 }
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000150}
151
152SkPDFDevice::~SkPDFDevice() {
153 fGraphicStateResources.unrefAll();
154 fXObjectResources.unrefAll();
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000155 fFontResources.unrefAll();
vandebo@chromium.orgda912d62011-03-08 18:31:02 +0000156 fShaderResources.unrefAll();
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000157}
158
159void SkPDFDevice::setMatrixClip(const SkMatrix& matrix,
reed@google.com46799cd2011-02-22 20:56:26 +0000160 const SkRegion& region,
161 const SkClipStack&) {
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000162 // See the comment in the header file above GraphicStackEntry.
163 if (region != fGraphicStack[fGraphicStackIndex].fClip) {
164 while (fGraphicStackIndex > 0)
165 popGS();
166 pushGS();
167
168 SkPath clipPath;
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000169 if (region.getBoundaryPath(&clipPath)) {
ctguil@chromium.org9db86bb2011-03-04 21:43:27 +0000170 SkPDFUtils::EmitPath(clipPath, &fContent);
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000171
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000172 SkPath::FillType clipFill = clipPath.getFillType();
173 NOT_IMPLEMENTED(clipFill == SkPath::kInverseEvenOdd_FillType,
174 false);
175 NOT_IMPLEMENTED(clipFill == SkPath::kInverseWinding_FillType,
176 false);
177 if (clipFill == SkPath::kEvenOdd_FillType)
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000178 fContent.writeText("W* n ");
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000179 else
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000180 fContent.writeText("W n ");
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000181 }
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000182
183 fGraphicStack[fGraphicStackIndex].fClip = region;
184 }
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000185 setTransform(matrix);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000186}
187
188void SkPDFDevice::drawPaint(const SkDraw& d, const SkPaint& paint) {
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000189 SkMatrix identityTransform;
190 identityTransform.reset();
191 SkMatrix curTransform = setTransform(identityTransform);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000192
193 SkPaint newPaint = paint;
194 newPaint.setStyle(SkPaint::kFill_Style);
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000195 updateGSFromPaint(newPaint, false);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000196
reed@google.coma6d59f62011-03-07 21:29:21 +0000197 SkRect all = SkRect::MakeWH(SkIntToScalar(this->width()),
198 SkIntToScalar(this->height()));
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000199 drawRect(d, all, newPaint);
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000200 setTransform(curTransform);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000201}
202
203void SkPDFDevice::drawPoints(const SkDraw& d, SkCanvas::PointMode mode,
204 size_t count, const SkPoint* points,
205 const SkPaint& paint) {
206 if (count == 0)
207 return;
208
209 switch (mode) {
210 case SkCanvas::kPolygon_PointMode:
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000211 updateGSFromPaint(paint, false);
ctguil@chromium.org9db86bb2011-03-04 21:43:27 +0000212 SkPDFUtils::MoveTo(points[0].fX, points[0].fY, &fContent);
213 for (size_t i = 1; i < count; i++) {
214 SkPDFUtils::AppendLine(points[i].fX, points[i].fY, &fContent);
215 }
216 SkPDFUtils::StrokePath(&fContent);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000217 break;
218 case SkCanvas::kLines_PointMode:
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000219 updateGSFromPaint(paint, false);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000220 for (size_t i = 0; i < count/2; i++) {
ctguil@chromium.org9db86bb2011-03-04 21:43:27 +0000221 SkPDFUtils::MoveTo(points[i * 2].fX, points[i * 2].fY,
222 &fContent);
223 SkPDFUtils::AppendLine(points[i * 2 + 1].fX,
224 points[i * 2 + 1].fY, &fContent);
225 SkPDFUtils::StrokePath(&fContent);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000226 }
227 break;
228 case SkCanvas::kPoints_PointMode:
229 if (paint.getStrokeCap() == SkPaint::kRound_Cap) {
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000230 updateGSFromPaint(paint, false);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000231 for (size_t i = 0; i < count; i++) {
ctguil@chromium.org9db86bb2011-03-04 21:43:27 +0000232 SkPDFUtils::MoveTo(points[i].fX, points[i].fY, &fContent);
233 SkPDFUtils::StrokePath(&fContent);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000234 }
235 } else {
236 // PDF won't draw a single point with square/butt caps because
237 // the orientation is ambiguous. Draw a rectangle instead.
238 SkPaint newPaint = paint;
239 newPaint.setStyle(SkPaint::kFill_Style);
240 SkScalar strokeWidth = paint.getStrokeWidth();
241 SkScalar halfStroke = strokeWidth * SK_ScalarHalf;
242 for (size_t i = 0; i < count; i++) {
243 SkRect r = SkRect::MakeXYWH(points[i].fX, points[i].fY,
244 0, 0);
245 r.inset(-halfStroke, -halfStroke);
246 drawRect(d, r, newPaint);
247 }
248 }
249 break;
250 default:
251 SkASSERT(false);
252 }
253}
254
255void SkPDFDevice::drawRect(const SkDraw& d, const SkRect& r,
256 const SkPaint& paint) {
257 if (paint.getPathEffect()) {
vandebo@chromium.org7d71f7f2010-10-26 19:51:44 +0000258 // Create a path for the rectangle and apply the path effect to it.
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000259 SkPath path;
260 path.addRect(r);
261 paint.getFillPath(path, &path);
262
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000263 SkPaint noEffectPaint(paint);
264 SkSafeUnref(noEffectPaint.setPathEffect(NULL));
vandebo@chromium.org02cc5aa2011-01-25 22:06:29 +0000265 drawPath(d, path, noEffectPaint, NULL, true);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000266 return;
267 }
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000268 updateGSFromPaint(paint, false);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000269
270 // Skia has 0,0 at top left, pdf at bottom left. Do the right thing.
271 SkScalar bottom = r.fBottom < r.fTop ? r.fBottom : r.fTop;
ctguil@chromium.org9db86bb2011-03-04 21:43:27 +0000272 SkPDFUtils::AppendRectangle(r.fLeft, bottom, r.width(), r.height(),
273 &fContent);
274 SkPDFUtils::PaintPath(paint.getStyle(), SkPath::kWinding_FillType,
275 &fContent);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000276}
277
vandebo@chromium.org7d71f7f2010-10-26 19:51:44 +0000278void SkPDFDevice::drawPath(const SkDraw& d, const SkPath& path,
vandebo@chromium.org02cc5aa2011-01-25 22:06:29 +0000279 const SkPaint& paint, const SkMatrix* prePathMatrix,
280 bool pathIsMutable) {
vandebo@chromium.orgbefebb82011-01-29 01:38:50 +0000281 NOT_IMPLEMENTED(prePathMatrix != NULL, true);
vandebo@chromium.org02cc5aa2011-01-25 22:06:29 +0000282
vandebo@chromium.org7d71f7f2010-10-26 19:51:44 +0000283 if (paint.getPathEffect()) {
284 // Apply the path effect to path and draw it that way.
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000285 SkPath noEffectPath;
286 paint.getFillPath(path, &noEffectPath);
vandebo@chromium.org7d71f7f2010-10-26 19:51:44 +0000287
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000288 SkPaint noEffectPaint(paint);
289 SkSafeUnref(noEffectPaint.setPathEffect(NULL));
vandebo@chromium.org02cc5aa2011-01-25 22:06:29 +0000290 drawPath(d, noEffectPath, noEffectPaint, NULL, true);
vandebo@chromium.org7d71f7f2010-10-26 19:51:44 +0000291 return;
292 }
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000293 updateGSFromPaint(paint, false);
vandebo@chromium.org7d71f7f2010-10-26 19:51:44 +0000294
ctguil@chromium.org9db86bb2011-03-04 21:43:27 +0000295 SkPDFUtils::EmitPath(path, &fContent);
296 SkPDFUtils::PaintPath(paint.getStyle(), path.getFillType(), &fContent);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000297}
298
299void SkPDFDevice::drawBitmap(const SkDraw&, const SkBitmap& bitmap,
reed@android.comf2b98d62010-12-20 18:26:13 +0000300 const SkIRect* srcRect,
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000301 const SkMatrix& matrix, const SkPaint& paint) {
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000302 SkMatrix transform = matrix;
303 transform.postConcat(fGraphicStack[fGraphicStackIndex].fTransform);
vandebo@chromium.orgbefebb82011-01-29 01:38:50 +0000304 internalDrawBitmap(transform, bitmap, srcRect, paint);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000305}
306
307void SkPDFDevice::drawSprite(const SkDraw&, const SkBitmap& bitmap,
308 int x, int y, const SkPaint& paint) {
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000309 SkMatrix matrix;
reed@google.coma6d59f62011-03-07 21:29:21 +0000310 matrix.setTranslate(SkIntToScalar(x), SkIntToScalar(y));
vandebo@chromium.orgbefebb82011-01-29 01:38:50 +0000311 internalDrawBitmap(matrix, bitmap, NULL, paint);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000312}
313
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000314void SkPDFDevice::drawText(const SkDraw& d, const void* text, size_t len,
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000315 SkScalar x, SkScalar y, const SkPaint& paint) {
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000316 SkPaint textPaint = calculateTextPaint(paint);
317 updateGSFromPaint(textPaint, true);
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000318
vandebo@chromium.org01294102011-02-28 19:52:18 +0000319 // We want the text in glyph id encoding and a writable buffer, so we end
320 // up making a copy either way.
321 size_t numGlyphs = paint.textToGlyphs(text, len, NULL);
322 uint16_t* glyphIDs =
323 (uint16_t*)sk_malloc_flags(numGlyphs * 2,
324 SK_MALLOC_TEMP | SK_MALLOC_THROW);
325 SkAutoFree autoFreeGlyphIDs(glyphIDs);
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000326 if (paint.getTextEncoding() != SkPaint::kGlyphID_TextEncoding) {
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000327 paint.textToGlyphs(text, len, glyphIDs);
328 textPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
329 } else {
330 SkASSERT((len & 1) == 0);
vandebo@chromium.org01294102011-02-28 19:52:18 +0000331 SkASSERT(len / 2 == numGlyphs);
332 memcpy(glyphIDs, text, len);
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000333 }
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000334
335 SkScalar width;
336 SkScalar* widthPtr = NULL;
337 if (textPaint.isUnderlineText() || textPaint.isStrikeThruText())
338 widthPtr = &width;
339
340 SkDrawCacheProc glyphCacheProc = textPaint.getDrawCacheProc();
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000341 alignText(glyphCacheProc, textPaint, glyphIDs, numGlyphs, &x, &y, widthPtr);
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000342 fContent.writeText("BT\n");
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000343 setTextTransform(x, y, textPaint.getTextSkewX());
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000344 size_t consumedGlyphCount = 0;
345 while (numGlyphs > consumedGlyphCount) {
346 updateFont(textPaint, glyphIDs[consumedGlyphCount]);
347 SkPDFFont* font = fGraphicStack[fGraphicStackIndex].fFont;
vandebo@chromium.org01294102011-02-28 19:52:18 +0000348 size_t availableGlyphs =
349 font->glyphsToPDFFontEncoding(glyphIDs + consumedGlyphCount,
350 numGlyphs - consumedGlyphCount);
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000351 SkString encodedString =
352 SkPDFString::formatString(glyphIDs + consumedGlyphCount,
353 availableGlyphs, font->multiByteGlyphs());
354 fContent.writeText(encodedString.c_str());
vandebo@chromium.org01294102011-02-28 19:52:18 +0000355 consumedGlyphCount += availableGlyphs;
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000356 fContent.writeText(" Tj\n");
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000357 }
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000358 fContent.writeText("ET\n");
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000359
360 // Draw underline and/or strikethrough if the paint has them.
361 // drawPosText() and drawTextOnPath() don't draw underline or strikethrough
362 // because the raster versions don't. Use paint instead of textPaint
363 // because we may have changed strokeWidth to do fakeBold text.
364 if (paint.isUnderlineText() || paint.isStrikeThruText()) {
365 SkScalar textSize = paint.getTextSize();
366 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
367
368 if (paint.isUnderlineText()) {
369 SkScalar top = SkScalarMulAdd(textSize, kStdUnderline_Offset, y);
370 SkRect r = SkRect::MakeXYWH(x, top - height, width, height);
371 drawRect(d, r, paint);
372 }
373 if (paint.isStrikeThruText()) {
374 SkScalar top = SkScalarMulAdd(textSize, kStdStrikeThru_Offset, y);
375 SkRect r = SkRect::MakeXYWH(x, top - height, width, height);
376 drawRect(d, r, paint);
377 }
378 }
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000379}
380
381void SkPDFDevice::drawPosText(const SkDraw&, const void* text, size_t len,
382 const SkScalar pos[], SkScalar constY,
383 int scalarsPerPos, const SkPaint& paint) {
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000384 SkASSERT(1 == scalarsPerPos || 2 == scalarsPerPos);
385 SkPaint textPaint = calculateTextPaint(paint);
386 updateGSFromPaint(textPaint, true);
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000387
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000388 // Make sure we have a glyph id encoding.
389 SkAutoFree glyphStorage;
390 uint16_t* glyphIDs;
391 size_t numGlyphs;
392 if (paint.getTextEncoding() != SkPaint::kGlyphID_TextEncoding) {
393 numGlyphs = paint.textToGlyphs(text, len, NULL);
394 glyphIDs = (uint16_t*)sk_malloc_flags(numGlyphs * 2,
395 SK_MALLOC_TEMP | SK_MALLOC_THROW);
396 glyphStorage.set(glyphIDs);
397 paint.textToGlyphs(text, len, glyphIDs);
398 textPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
399 } else {
400 SkASSERT((len & 1) == 0);
401 numGlyphs = len / 2;
402 glyphIDs = (uint16_t*)text;
403 }
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000404
405 SkDrawCacheProc glyphCacheProc = textPaint.getDrawCacheProc();
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000406 fContent.writeText("BT\n");
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000407 updateFont(textPaint, glyphIDs[0]);
408 for (size_t i = 0; i < numGlyphs; i++) {
409 SkPDFFont* font = fGraphicStack[fGraphicStackIndex].fFont;
vandebo@chromium.org01294102011-02-28 19:52:18 +0000410 uint16_t encodedValue = glyphIDs[i];
411 if (font->glyphsToPDFFontEncoding(&encodedValue, 1) != 1) {
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000412 updateFont(textPaint, glyphIDs[i]);
413 i--;
414 continue;
415 }
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000416 SkScalar x = pos[i * scalarsPerPos];
417 SkScalar y = scalarsPerPos == 1 ? constY : pos[i * scalarsPerPos + 1];
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000418 alignText(glyphCacheProc, textPaint, glyphIDs + i, 1, &x, &y, NULL);
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000419 setTextTransform(x, y, textPaint.getTextSkewX());
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000420 SkString encodedString =
421 SkPDFString::formatString(&encodedValue, 1,
422 font->multiByteGlyphs());
423 fContent.writeText(encodedString.c_str());
424 fContent.writeText(" Tj\n");
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000425 }
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000426 fContent.writeText("ET\n");
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000427}
428
429void SkPDFDevice::drawTextOnPath(const SkDraw&, const void* text, size_t len,
430 const SkPath& path, const SkMatrix* matrix,
431 const SkPaint& paint) {
vandebo@chromium.orga5180862010-10-26 19:48:49 +0000432 NOT_IMPLEMENTED("drawTextOnPath", true);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000433}
434
435void SkPDFDevice::drawVertices(const SkDraw&, SkCanvas::VertexMode,
436 int vertexCount, const SkPoint verts[],
437 const SkPoint texs[], const SkColor colors[],
438 SkXfermode* xmode, const uint16_t indices[],
439 int indexCount, const SkPaint& paint) {
vandebo@chromium.orga5180862010-10-26 19:48:49 +0000440 NOT_IMPLEMENTED("drawVerticies", true);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000441}
442
vandebo@chromium.orgeb6c7592010-10-26 19:54:45 +0000443void SkPDFDevice::drawDevice(const SkDraw& d, SkDevice* device, int x, int y,
444 const SkPaint& paint) {
445 if ((device->getDeviceCapabilities() & kVector_Capability) == 0) {
446 // If we somehow get a raster device, do what our parent would do.
447 SkDevice::drawDevice(d, device, x, y, paint);
448 return;
449 }
vandebo@chromium.orgeb6c7592010-10-26 19:54:45 +0000450 // Assume that a vector capable device means that it's a PDF Device.
vandebo@chromium.orgeb6c7592010-10-26 19:54:45 +0000451 SkPDFDevice* pdfDevice = static_cast<SkPDFDevice*>(device);
452
vandebo@chromium.org1aef2ed2011-02-03 21:46:10 +0000453 SkMatrix matrix;
reed@google.coma6d59f62011-03-07 21:29:21 +0000454 matrix.setTranslate(SkIntToScalar(x), SkIntToScalar(y));
vandebo@chromium.org1aef2ed2011-02-03 21:46:10 +0000455 SkMatrix curTransform = setTransform(matrix);
vandebo@chromium.org48543272011-02-08 19:28:07 +0000456 updateGSFromPaint(paint, false);
vandebo@chromium.org1aef2ed2011-02-03 21:46:10 +0000457
458 SkPDFFormXObject* xobject = new SkPDFFormXObject(pdfDevice);
vandebo@chromium.orgeb6c7592010-10-26 19:54:45 +0000459 fXObjectResources.push(xobject); // Transfer reference.
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000460 fContent.writeText("/X");
461 fContent.writeDecAsText(fXObjectResources.count() - 1);
462 fContent.writeText(" Do\n");
vandebo@chromium.org1aef2ed2011-02-03 21:46:10 +0000463 setTransform(curTransform);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000464}
465
466const SkRefPtr<SkPDFDict>& SkPDFDevice::getResourceDict() {
467 if (fResourceDict.get() == NULL) {
468 fResourceDict = new SkPDFDict;
469 fResourceDict->unref(); // SkRefPtr and new both took a reference.
470
471 if (fGraphicStateResources.count()) {
472 SkRefPtr<SkPDFDict> extGState = new SkPDFDict();
473 extGState->unref(); // SkRefPtr and new both took a reference.
474 for (int i = 0; i < fGraphicStateResources.count(); i++) {
475 SkString nameString("G");
476 nameString.appendS32(i);
vandebo@chromium.orgf7c15762011-02-01 22:19:44 +0000477 extGState->insert(
478 nameString.c_str(),
479 new SkPDFObjRef(fGraphicStateResources[i]))->unref();
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000480 }
481 fResourceDict->insert("ExtGState", extGState.get());
482 }
483
484 if (fXObjectResources.count()) {
485 SkRefPtr<SkPDFDict> xObjects = new SkPDFDict();
486 xObjects->unref(); // SkRefPtr and new both took a reference.
487 for (int i = 0; i < fXObjectResources.count(); i++) {
488 SkString nameString("X");
489 nameString.appendS32(i);
vandebo@chromium.orgf7c15762011-02-01 22:19:44 +0000490 xObjects->insert(
491 nameString.c_str(),
492 new SkPDFObjRef(fXObjectResources[i]))->unref();
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000493 }
494 fResourceDict->insert("XObject", xObjects.get());
495 }
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000496
497 if (fFontResources.count()) {
498 SkRefPtr<SkPDFDict> fonts = new SkPDFDict();
499 fonts->unref(); // SkRefPtr and new both took a reference.
500 for (int i = 0; i < fFontResources.count(); i++) {
501 SkString nameString("F");
502 nameString.appendS32(i);
vandebo@chromium.orgf7c15762011-02-01 22:19:44 +0000503 fonts->insert(nameString.c_str(),
504 new SkPDFObjRef(fFontResources[i]))->unref();
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000505 }
506 fResourceDict->insert("Font", fonts.get());
507 }
508
vandebo@chromium.orgda912d62011-03-08 18:31:02 +0000509 if (fShaderResources.count()) {
510 SkRefPtr<SkPDFDict> patterns = new SkPDFDict();
511 patterns->unref(); // SkRefPtr and new both took a reference.
512 for (int i = 0; i < fShaderResources.count(); i++) {
513 SkString nameString("P");
514 nameString.appendS32(i);
515 patterns->insert(nameString.c_str(),
516 new SkPDFObjRef(fShaderResources[i]))->unref();
517 }
518 fResourceDict->insert("Pattern", patterns.get());
519 }
520
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000521 // For compatibility, add all proc sets (only used for output to PS
522 // devices).
523 const char procs[][7] = {"PDF", "Text", "ImageB", "ImageC", "ImageI"};
524 SkRefPtr<SkPDFArray> procSets = new SkPDFArray();
525 procSets->unref(); // SkRefPtr and new both took a reference.
526 procSets->reserve(SK_ARRAY_COUNT(procs));
vandebo@chromium.orgf7c15762011-02-01 22:19:44 +0000527 for (size_t i = 0; i < SK_ARRAY_COUNT(procs); i++)
528 procSets->append(new SkPDFName(procs[i]))->unref();
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000529 fResourceDict->insert("ProcSet", procSets.get());
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000530 }
531 return fResourceDict;
532}
533
vandebo@chromium.orga5180862010-10-26 19:48:49 +0000534void SkPDFDevice::getResources(SkTDArray<SkPDFObject*>* resourceList) const {
535 resourceList->setReserve(resourceList->count() +
536 fGraphicStateResources.count() +
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000537 fXObjectResources.count() +
vandebo@chromium.orgda912d62011-03-08 18:31:02 +0000538 fFontResources.count() +
539 fShaderResources.count());
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000540 for (int i = 0; i < fGraphicStateResources.count(); i++) {
vandebo@chromium.orga5180862010-10-26 19:48:49 +0000541 resourceList->push(fGraphicStateResources[i]);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000542 fGraphicStateResources[i]->ref();
vandebo@chromium.orga5180862010-10-26 19:48:49 +0000543 fGraphicStateResources[i]->getResources(resourceList);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000544 }
545 for (int i = 0; i < fXObjectResources.count(); i++) {
vandebo@chromium.orga5180862010-10-26 19:48:49 +0000546 resourceList->push(fXObjectResources[i]);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000547 fXObjectResources[i]->ref();
vandebo@chromium.orga5180862010-10-26 19:48:49 +0000548 fXObjectResources[i]->getResources(resourceList);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000549 }
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000550 for (int i = 0; i < fFontResources.count(); i++) {
551 resourceList->push(fFontResources[i]);
552 fFontResources[i]->ref();
553 fFontResources[i]->getResources(resourceList);
554 }
vandebo@chromium.orgda912d62011-03-08 18:31:02 +0000555 for (int i = 0; i < fShaderResources.count(); i++) {
556 resourceList->push(fShaderResources[i]);
557 fShaderResources[i]->ref();
558 fShaderResources[i]->getResources(resourceList);
559 }
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000560}
561
vandebo@chromium.orga5180862010-10-26 19:48:49 +0000562SkRefPtr<SkPDFArray> SkPDFDevice::getMediaBox() const {
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000563 SkRefPtr<SkPDFInt> zero = new SkPDFInt(0);
564 zero->unref(); // SkRefPtr and new both took a reference.
vandebo@chromium.orgf7c15762011-02-01 22:19:44 +0000565
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000566 SkRefPtr<SkPDFArray> mediaBox = new SkPDFArray();
567 mediaBox->unref(); // SkRefPtr and new both took a reference.
568 mediaBox->reserve(4);
569 mediaBox->append(zero.get());
570 mediaBox->append(zero.get());
vandebo@chromium.orgf7c15762011-02-01 22:19:44 +0000571 mediaBox->append(new SkPDFInt(fWidth))->unref();
572 mediaBox->append(new SkPDFInt(fHeight))->unref();
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000573 return mediaBox;
574}
575
vandebo@chromium.orgc2a9b7f2011-02-24 23:22:30 +0000576SkStream* SkPDFDevice::content() const {
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000577 size_t offset = fContent.getOffset();
vandebo@chromium.orgc2a9b7f2011-02-24 23:22:30 +0000578 char* data = (char*)sk_malloc_throw(offset + fGraphicStackIndex * 2);
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000579 fContent.copyTo(data);
vandebo@chromium.orgc2a9b7f2011-02-24 23:22:30 +0000580 for (int i = 0; i < fGraphicStackIndex; i++) {
581 data[offset++] = 'Q';
582 data[offset++] = '\n';
583 }
584 SkMemoryStream* result = new SkMemoryStream;
585 result->setMemoryOwned(data, offset);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000586 return result;
587}
588
vandebo@chromium.orgda912d62011-03-08 18:31:02 +0000589void SkPDFDevice::updateGSFromPaint(const SkPaint& paint, bool forText) {
590 SkASSERT(paint.getPathEffect() == NULL);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000591
vandebo@chromium.orgda912d62011-03-08 18:31:02 +0000592 NOT_IMPLEMENTED(paint.getMaskFilter() != NULL, false);
593 NOT_IMPLEMENTED(paint.getColorFilter() != NULL, false);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000594
vandebo@chromium.orgda912d62011-03-08 18:31:02 +0000595 SkPaint newPaint = paint;
vandebo@chromium.org48543272011-02-08 19:28:07 +0000596
vandebo@chromium.orgda912d62011-03-08 18:31:02 +0000597 // PDF treats a shader as a color, so we only set one or the other.
598 SkRefPtr<SkPDFShader> pdfShader;
599 const SkShader* shader = newPaint.getShader();
600 if (shader) {
601 // PDF positions patterns relative to the initial transform, so
602 // we need to apply the current transform to the shader parameters.
603 SkMatrix transform = fGraphicStack[fGraphicStackIndex].fTransform;
604 if (fFlipOrigin == kFlip_OriginTransform) {
605 transform.postScale(1, -1);
606 transform.postTranslate(0, fHeight);
607 }
608
609 // PDF doesn't support kClamp_TileMode, so we simulate it by making
610 // a pattern the size of the drawing service.
611 SkIRect bounds = fGraphicStack[fGraphicStackIndex].fClip.getBounds();
612 pdfShader = SkPDFShader::getPDFShader(*shader, transform, bounds);
613 SkSafeUnref(pdfShader.get()); // getShader and SkRefPtr both took a ref
614
615 // A color shader is treated as an invalid shader so we don't have
616 // to set a shader just for a color.
617 if (pdfShader.get() == NULL) {
618 newPaint.setColor(0);
619
620 // Check for a color shader.
621 SkShader::GradientInfo gradientInfo;
622 SkColor gradientColor;
623 gradientInfo.fColors = &gradientColor;
624 gradientInfo.fColorOffsets = NULL;
625 gradientInfo.fColorCount = 1;
626 if (shader->asAGradient(&gradientInfo) ==
627 SkShader::kColor_GradientType) {
628 newPaint.setColor(gradientColor);
629 }
630 }
631 }
632
633 if (pdfShader) {
634 // pdfShader has been canonicalized so we can directly compare
635 // pointers.
636 if (fGraphicStack[fGraphicStackIndex].fShader != pdfShader.get()) {
637 int resourceIndex = fShaderResources.find(pdfShader.get());
638 if (resourceIndex < 0) {
639 resourceIndex = fShaderResources.count();
640 fShaderResources.push(pdfShader.get());
641 pdfShader->ref();
642 }
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000643 fContent.writeText("/Pattern CS /Pattern cs /P");
644 fContent.writeDecAsText(resourceIndex);
645 fContent.writeText(" SCN /P");
646 fContent.writeDecAsText(resourceIndex);
647 fContent.writeText(" scn\n");
vandebo@chromium.orgda912d62011-03-08 18:31:02 +0000648 fGraphicStack[fGraphicStackIndex].fShader = pdfShader.get();
649 }
650 } else {
651 SkColor newColor = newPaint.getColor();
652 newColor = SkColorSetA(newColor, 0xFF);
653 if (fGraphicStack[fGraphicStackIndex].fShader ||
654 fGraphicStack[fGraphicStackIndex].fColor != newColor) {
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000655 emitPDFColor(newColor, &fContent);
656 fContent.writeText("RG ");
657 emitPDFColor(newColor, &fContent);
658 fContent.writeText("rg\n");
vandebo@chromium.orgda912d62011-03-08 18:31:02 +0000659 fGraphicStack[fGraphicStackIndex].fColor = newColor;
660 fGraphicStack[fGraphicStackIndex].fShader = NULL;
661 }
662 }
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000663
664 SkRefPtr<SkPDFGraphicState> newGraphicState =
665 SkPDFGraphicState::getGraphicStateForPaint(newPaint);
666 newGraphicState->unref(); // getGraphicState and SkRefPtr both took a ref.
667 // newGraphicState has been canonicalized so we can directly compare
668 // pointers.
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000669 if (fGraphicStack[fGraphicStackIndex].fGraphicState !=
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000670 newGraphicState.get()) {
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000671 int resourceIndex = fGraphicStateResources.find(newGraphicState.get());
672 if (resourceIndex < 0) {
673 resourceIndex = fGraphicStateResources.count();
674 fGraphicStateResources.push(newGraphicState.get());
675 newGraphicState->ref();
676 }
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000677 fContent.writeText("/G");
678 fContent.writeDecAsText(resourceIndex);
679 fContent.writeText(" gs\n");
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000680 fGraphicStack[fGraphicStackIndex].fGraphicState = newGraphicState.get();
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000681 }
682
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000683 if (forText) {
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000684 if (fGraphicStack[fGraphicStackIndex].fTextScaleX !=
685 newPaint.getTextScaleX()) {
686 SkScalar scale = newPaint.getTextScaleX();
687 SkScalar pdfScale = SkScalarMul(scale, SkIntToScalar(100));
vandebo@chromium.org094316b2011-03-04 03:15:13 +0000688 SkPDFScalar::Append(pdfScale, &fContent);
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000689 fContent.writeText(" Tz\n");
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000690 fGraphicStack[fGraphicStackIndex].fTextScaleX = scale;
691 }
692
693 if (fGraphicStack[fGraphicStackIndex].fTextFill !=
694 newPaint.getStyle()) {
695 SK_COMPILE_ASSERT(SkPaint::kFill_Style == 0, enum_must_match_value);
696 SK_COMPILE_ASSERT(SkPaint::kStroke_Style == 1,
697 enum_must_match_value);
698 SK_COMPILE_ASSERT(SkPaint::kStrokeAndFill_Style == 2,
699 enum_must_match_value);
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000700 fContent.writeDecAsText(newPaint.getStyle());
701 fContent.writeText(" Tr\n");
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000702 fGraphicStack[fGraphicStackIndex].fTextFill = newPaint.getStyle();
703 }
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000704 }
705}
706
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000707void SkPDFDevice::updateFont(const SkPaint& paint, uint16_t glyphID) {
ctguil@chromium.org9db86bb2011-03-04 21:43:27 +0000708 SkTypeface* typeface = paint.getTypeface();
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000709 if (fGraphicStack[fGraphicStackIndex].fTextSize != paint.getTextSize() ||
710 fGraphicStack[fGraphicStackIndex].fFont == NULL ||
ctguil@chromium.org9db86bb2011-03-04 21:43:27 +0000711 fGraphicStack[fGraphicStackIndex].fFont->typeface() != typeface ||
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000712 !fGraphicStack[fGraphicStackIndex].fFont->hasGlyph(glyphID)) {
ctguil@chromium.org9db86bb2011-03-04 21:43:27 +0000713 int fontIndex = getFontResourceIndex(typeface, glyphID);
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000714 fContent.writeText("/F");
715 fContent.writeDecAsText(fontIndex);
716 fContent.writeText(" ");
vandebo@chromium.org094316b2011-03-04 03:15:13 +0000717 SkPDFScalar::Append(paint.getTextSize(), &fContent);
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000718 fContent.writeText(" Tf\n");
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000719 fGraphicStack[fGraphicStackIndex].fTextSize = paint.getTextSize();
720 fGraphicStack[fGraphicStackIndex].fFont = fFontResources[fontIndex];
721 }
722}
723
ctguil@chromium.org9db86bb2011-03-04 21:43:27 +0000724int SkPDFDevice::getFontResourceIndex(SkTypeface* typeface, uint16_t glyphID) {
725 SkRefPtr<SkPDFFont> newFont = SkPDFFont::getFontResource(typeface, glyphID);
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000726 newFont->unref(); // getFontResource and SkRefPtr both took a ref.
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000727 int resourceIndex = fFontResources.find(newFont.get());
728 if (resourceIndex < 0) {
729 resourceIndex = fFontResources.count();
730 fFontResources.push(newFont.get());
731 newFont->ref();
732 }
733 return resourceIndex;
734}
735
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000736void SkPDFDevice::pushGS() {
737 SkASSERT(fGraphicStackIndex < 2);
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000738 fContent.writeText("q\n");
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000739 fGraphicStackIndex++;
740 fGraphicStack[fGraphicStackIndex] = fGraphicStack[fGraphicStackIndex - 1];
741}
742
743void SkPDFDevice::popGS() {
744 SkASSERT(fGraphicStackIndex > 0);
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000745 fContent.writeText("Q\n");
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000746 fGraphicStackIndex--;
747}
748
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000749void SkPDFDevice::setTextTransform(SkScalar x, SkScalar y, SkScalar textSkewX) {
750 // Flip the text about the x-axis to account for origin swap and include
751 // the passed parameters.
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000752 fContent.writeText("1 0 ");
vandebo@chromium.org094316b2011-03-04 03:15:13 +0000753 SkPDFScalar::Append(0 - textSkewX, &fContent);
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000754 fContent.writeText(" -1 ");
vandebo@chromium.org094316b2011-03-04 03:15:13 +0000755 SkPDFScalar::Append(x, &fContent);
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000756 fContent.writeText(" ");
vandebo@chromium.org094316b2011-03-04 03:15:13 +0000757 SkPDFScalar::Append(y, &fContent);
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000758 fContent.writeText(" Tm\n");
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000759}
760
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000761void SkPDFDevice::internalDrawBitmap(const SkMatrix& matrix,
762 const SkBitmap& bitmap,
vandebo@chromium.orgbefebb82011-01-29 01:38:50 +0000763 const SkIRect* srcRect,
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000764 const SkPaint& paint) {
vandebo@chromium.orgbefebb82011-01-29 01:38:50 +0000765 SkIRect subset = SkIRect::MakeWH(bitmap.width(), bitmap.height());
766 if (srcRect && !subset.intersect(*srcRect))
767 return;
768
vandebo@chromium.org1cfa2c42011-01-31 19:35:43 +0000769 SkPDFImage* image = SkPDFImage::CreateImage(bitmap, subset, paint);
770 if (!image)
771 return;
772
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000773 SkMatrix scaled;
774 // Adjust for origin flip.
775 scaled.setScale(1, -1);
776 scaled.postTranslate(0, 1);
777 // Scale the image up from 1x1 to WxH.
reed@google.coma6d59f62011-03-07 21:29:21 +0000778 scaled.postScale(SkIntToScalar(subset.width()),
779 SkIntToScalar(subset.height()));
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000780 scaled.postConcat(matrix);
781 SkMatrix curTransform = setTransform(scaled);
vandebo@chromium.org48543272011-02-08 19:28:07 +0000782 updateGSFromPaint(paint, false);
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000783
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000784 fXObjectResources.push(image); // Transfer reference.
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000785 fContent.writeText("/X");
786 fContent.writeDecAsText(fXObjectResources.count() - 1);
787 fContent.writeText(" Do\n");
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000788 setTransform(curTransform);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000789}
790
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000791SkMatrix SkPDFDevice::setTransform(const SkMatrix& m) {
792 SkMatrix old = fGraphicStack[fGraphicStackIndex].fTransform;
793 if (old == m)
794 return old;
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000795
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000796 if (old.getType() != SkMatrix::kIdentity_Mask) {
797 SkASSERT(fGraphicStackIndex > 0);
798 SkASSERT(fGraphicStack[fGraphicStackIndex - 1].fTransform.getType() ==
799 SkMatrix::kIdentity_Mask);
800 SkASSERT(fGraphicStack[fGraphicStackIndex].fClip ==
801 fGraphicStack[fGraphicStackIndex - 1].fClip);
802 popGS();
803 }
804 if (m.getType() == SkMatrix::kIdentity_Mask)
805 return old;
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000806
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000807 if (fGraphicStackIndex == 0 || fGraphicStack[fGraphicStackIndex].fClip !=
808 fGraphicStack[fGraphicStackIndex - 1].fClip)
809 pushGS();
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000810
vandebo@chromium.orgddbbd802010-10-26 19:45:06 +0000811 SkScalar transform[6];
812 SkAssertResult(m.pdfTransform(transform));
813 for (size_t i = 0; i < SK_ARRAY_COUNT(transform); i++) {
vandebo@chromium.org094316b2011-03-04 03:15:13 +0000814 SkPDFScalar::Append(transform[i], &fContent);
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000815 fContent.writeText(" ");
vandebo@chromium.orgddbbd802010-10-26 19:45:06 +0000816 }
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000817 fContent.writeText("cm\n");
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000818 fGraphicStack[fGraphicStackIndex].fTransform = m;
819
820 return old;
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000821}