blob: 6a38fac893cc03ff03c8d64bf56d4267452015c5 [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"
28#include "SkPDFStream.h"
ctguil@chromium.org9db86bb2011-03-04 21:43:27 +000029#include "SkPDFUtils.h"
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +000030#include "SkRect.h"
31#include "SkString.h"
vandebo@chromium.org28be72b2010-11-11 21:37:00 +000032#include "SkTextFormatParams.h"
33#include "SkTypeface.h"
34#include "SkTypes.h"
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +000035
36// Utility functions
37
38namespace {
39
40SkString toPDFColor(SkColor color) {
41 SkASSERT(SkColorGetA(color) == 0xFF); // We handle alpha elsewhere.
42 SkScalar colorMax = SkIntToScalar(0xFF);
43 SkString result;
vandebo@chromium.org094316b2011-03-04 03:15:13 +000044 SkPDFScalar::Append(
45 SkScalarDiv(SkIntToScalar(SkColorGetR(color)), colorMax), &result);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +000046 result.append(" ");
vandebo@chromium.org094316b2011-03-04 03:15:13 +000047 SkPDFScalar::Append(
48 SkScalarDiv(SkIntToScalar(SkColorGetG(color)), colorMax), &result);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +000049 result.append(" ");
vandebo@chromium.org094316b2011-03-04 03:15:13 +000050 SkPDFScalar::Append(
51 SkScalarDiv(SkIntToScalar(SkColorGetB(color)), colorMax), &result);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +000052 result.append(" ");
53 return result;
54}
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) {
117 SkPDFDevice::OriginTransform flip = SkPDFDevice::kFlip_OriginTransform;
118 if (isForLayer) {
119 flip = SkPDFDevice::kNoFlip_OriginTransform;
120 }
121 return SkNEW_ARGS(SkPDFDevice, (width, height, flip));
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000122}
123
reed@android.comf2b98d62010-12-20 18:26:13 +0000124static inline SkBitmap makeABitmap(int width, int height) {
125 SkBitmap bitmap;
126 bitmap.setConfig(SkBitmap::kNo_Config, width, height);
127 return bitmap;
128}
129
vandebo@chromium.orgf60a0012011-02-24 23:14:04 +0000130SkPDFDevice::SkPDFDevice(int width, int height, OriginTransform flipOrigin)
reed@google.com07700442010-12-20 19:46:07 +0000131 : SkDevice(NULL, makeABitmap(width, height), false),
vandebo@chromium.org48543272011-02-08 19:28:07 +0000132 fWidth(width),
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000133 fHeight(height),
vandebo@chromium.orgf60a0012011-02-24 23:14:04 +0000134 fFlipOrigin(flipOrigin),
reed@google.com07700442010-12-20 19:46:07 +0000135 fGraphicStackIndex(0) {
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000136 fGraphicStack[0].fColor = SK_ColorBLACK;
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000137 fGraphicStack[0].fTextSize = SK_ScalarNaN; // This has no default value.
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000138 fGraphicStack[0].fTextScaleX = SK_Scalar1;
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000139 fGraphicStack[0].fTextFill = SkPaint::kFill_Style;
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000140 fGraphicStack[0].fFont = NULL;
141 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) {
146 fContent.printf("1 0 0 -1 0 %d cm\n", fHeight);
147 }
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000148}
149
150SkPDFDevice::~SkPDFDevice() {
151 fGraphicStateResources.unrefAll();
152 fXObjectResources.unrefAll();
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000153 fFontResources.unrefAll();
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000154}
155
156void SkPDFDevice::setMatrixClip(const SkMatrix& matrix,
reed@google.com46799cd2011-02-22 20:56:26 +0000157 const SkRegion& region,
158 const SkClipStack&) {
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000159 // See the comment in the header file above GraphicStackEntry.
160 if (region != fGraphicStack[fGraphicStackIndex].fClip) {
161 while (fGraphicStackIndex > 0)
162 popGS();
163 pushGS();
164
165 SkPath clipPath;
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000166 if (region.getBoundaryPath(&clipPath)) {
ctguil@chromium.org9db86bb2011-03-04 21:43:27 +0000167 SkPDFUtils::EmitPath(clipPath, &fContent);
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000168
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000169 SkPath::FillType clipFill = clipPath.getFillType();
170 NOT_IMPLEMENTED(clipFill == SkPath::kInverseEvenOdd_FillType,
171 false);
172 NOT_IMPLEMENTED(clipFill == SkPath::kInverseWinding_FillType,
173 false);
174 if (clipFill == SkPath::kEvenOdd_FillType)
175 fContent.append("W* n ");
176 else
177 fContent.append("W n ");
178 }
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000179
180 fGraphicStack[fGraphicStackIndex].fClip = region;
181 }
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000182 setTransform(matrix);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000183}
184
185void SkPDFDevice::drawPaint(const SkDraw& d, const SkPaint& paint) {
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000186 SkMatrix identityTransform;
187 identityTransform.reset();
188 SkMatrix curTransform = setTransform(identityTransform);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000189
190 SkPaint newPaint = paint;
191 newPaint.setStyle(SkPaint::kFill_Style);
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000192 updateGSFromPaint(newPaint, false);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000193
reed@google.coma6d59f62011-03-07 21:29:21 +0000194 SkRect all = SkRect::MakeWH(SkIntToScalar(this->width()),
195 SkIntToScalar(this->height()));
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000196 drawRect(d, all, newPaint);
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000197 setTransform(curTransform);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000198}
199
200void SkPDFDevice::drawPoints(const SkDraw& d, SkCanvas::PointMode mode,
201 size_t count, const SkPoint* points,
202 const SkPaint& paint) {
203 if (count == 0)
204 return;
205
206 switch (mode) {
207 case SkCanvas::kPolygon_PointMode:
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000208 updateGSFromPaint(paint, false);
ctguil@chromium.org9db86bb2011-03-04 21:43:27 +0000209 SkPDFUtils::MoveTo(points[0].fX, points[0].fY, &fContent);
210 for (size_t i = 1; i < count; i++) {
211 SkPDFUtils::AppendLine(points[i].fX, points[i].fY, &fContent);
212 }
213 SkPDFUtils::StrokePath(&fContent);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000214 break;
215 case SkCanvas::kLines_PointMode:
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000216 updateGSFromPaint(paint, false);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000217 for (size_t i = 0; i < count/2; i++) {
ctguil@chromium.org9db86bb2011-03-04 21:43:27 +0000218 SkPDFUtils::MoveTo(points[i * 2].fX, points[i * 2].fY,
219 &fContent);
220 SkPDFUtils::AppendLine(points[i * 2 + 1].fX,
221 points[i * 2 + 1].fY, &fContent);
222 SkPDFUtils::StrokePath(&fContent);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000223 }
224 break;
225 case SkCanvas::kPoints_PointMode:
226 if (paint.getStrokeCap() == SkPaint::kRound_Cap) {
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000227 updateGSFromPaint(paint, false);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000228 for (size_t i = 0; i < count; i++) {
ctguil@chromium.org9db86bb2011-03-04 21:43:27 +0000229 SkPDFUtils::MoveTo(points[i].fX, points[i].fY, &fContent);
230 SkPDFUtils::StrokePath(&fContent);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000231 }
232 } else {
233 // PDF won't draw a single point with square/butt caps because
234 // the orientation is ambiguous. Draw a rectangle instead.
235 SkPaint newPaint = paint;
236 newPaint.setStyle(SkPaint::kFill_Style);
237 SkScalar strokeWidth = paint.getStrokeWidth();
238 SkScalar halfStroke = strokeWidth * SK_ScalarHalf;
239 for (size_t i = 0; i < count; i++) {
240 SkRect r = SkRect::MakeXYWH(points[i].fX, points[i].fY,
241 0, 0);
242 r.inset(-halfStroke, -halfStroke);
243 drawRect(d, r, newPaint);
244 }
245 }
246 break;
247 default:
248 SkASSERT(false);
249 }
250}
251
252void SkPDFDevice::drawRect(const SkDraw& d, const SkRect& r,
253 const SkPaint& paint) {
254 if (paint.getPathEffect()) {
vandebo@chromium.org7d71f7f2010-10-26 19:51:44 +0000255 // Create a path for the rectangle and apply the path effect to it.
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000256 SkPath path;
257 path.addRect(r);
258 paint.getFillPath(path, &path);
259
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000260 SkPaint noEffectPaint(paint);
261 SkSafeUnref(noEffectPaint.setPathEffect(NULL));
vandebo@chromium.org02cc5aa2011-01-25 22:06:29 +0000262 drawPath(d, path, noEffectPaint, NULL, true);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000263 return;
264 }
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000265 updateGSFromPaint(paint, false);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000266
267 // Skia has 0,0 at top left, pdf at bottom left. Do the right thing.
268 SkScalar bottom = r.fBottom < r.fTop ? r.fBottom : r.fTop;
ctguil@chromium.org9db86bb2011-03-04 21:43:27 +0000269 SkPDFUtils::AppendRectangle(r.fLeft, bottom, r.width(), r.height(),
270 &fContent);
271 SkPDFUtils::PaintPath(paint.getStyle(), SkPath::kWinding_FillType,
272 &fContent);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000273}
274
vandebo@chromium.org7d71f7f2010-10-26 19:51:44 +0000275void SkPDFDevice::drawPath(const SkDraw& d, const SkPath& path,
vandebo@chromium.org02cc5aa2011-01-25 22:06:29 +0000276 const SkPaint& paint, const SkMatrix* prePathMatrix,
277 bool pathIsMutable) {
vandebo@chromium.orgbefebb82011-01-29 01:38:50 +0000278 NOT_IMPLEMENTED(prePathMatrix != NULL, true);
vandebo@chromium.org02cc5aa2011-01-25 22:06:29 +0000279
vandebo@chromium.org7d71f7f2010-10-26 19:51:44 +0000280 if (paint.getPathEffect()) {
281 // Apply the path effect to path and draw it that way.
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000282 SkPath noEffectPath;
283 paint.getFillPath(path, &noEffectPath);
vandebo@chromium.org7d71f7f2010-10-26 19:51:44 +0000284
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000285 SkPaint noEffectPaint(paint);
286 SkSafeUnref(noEffectPaint.setPathEffect(NULL));
vandebo@chromium.org02cc5aa2011-01-25 22:06:29 +0000287 drawPath(d, noEffectPath, noEffectPaint, NULL, true);
vandebo@chromium.org7d71f7f2010-10-26 19:51:44 +0000288 return;
289 }
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000290 updateGSFromPaint(paint, false);
vandebo@chromium.org7d71f7f2010-10-26 19:51:44 +0000291
ctguil@chromium.org9db86bb2011-03-04 21:43:27 +0000292 SkPDFUtils::EmitPath(path, &fContent);
293 SkPDFUtils::PaintPath(paint.getStyle(), path.getFillType(), &fContent);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000294}
295
296void SkPDFDevice::drawBitmap(const SkDraw&, const SkBitmap& bitmap,
reed@android.comf2b98d62010-12-20 18:26:13 +0000297 const SkIRect* srcRect,
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000298 const SkMatrix& matrix, const SkPaint& paint) {
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000299 SkMatrix transform = matrix;
300 transform.postConcat(fGraphicStack[fGraphicStackIndex].fTransform);
vandebo@chromium.orgbefebb82011-01-29 01:38:50 +0000301 internalDrawBitmap(transform, bitmap, srcRect, paint);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000302}
303
304void SkPDFDevice::drawSprite(const SkDraw&, const SkBitmap& bitmap,
305 int x, int y, const SkPaint& paint) {
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000306 SkMatrix matrix;
reed@google.coma6d59f62011-03-07 21:29:21 +0000307 matrix.setTranslate(SkIntToScalar(x), SkIntToScalar(y));
vandebo@chromium.orgbefebb82011-01-29 01:38:50 +0000308 internalDrawBitmap(matrix, bitmap, NULL, paint);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000309}
310
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000311void SkPDFDevice::drawText(const SkDraw& d, const void* text, size_t len,
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000312 SkScalar x, SkScalar y, const SkPaint& paint) {
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000313 SkPaint textPaint = calculateTextPaint(paint);
314 updateGSFromPaint(textPaint, true);
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000315
vandebo@chromium.org01294102011-02-28 19:52:18 +0000316 // We want the text in glyph id encoding and a writable buffer, so we end
317 // up making a copy either way.
318 size_t numGlyphs = paint.textToGlyphs(text, len, NULL);
319 uint16_t* glyphIDs =
320 (uint16_t*)sk_malloc_flags(numGlyphs * 2,
321 SK_MALLOC_TEMP | SK_MALLOC_THROW);
322 SkAutoFree autoFreeGlyphIDs(glyphIDs);
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000323 if (paint.getTextEncoding() != SkPaint::kGlyphID_TextEncoding) {
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000324 paint.textToGlyphs(text, len, glyphIDs);
325 textPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
326 } else {
327 SkASSERT((len & 1) == 0);
vandebo@chromium.org01294102011-02-28 19:52:18 +0000328 SkASSERT(len / 2 == numGlyphs);
329 memcpy(glyphIDs, text, len);
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000330 }
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000331
332 SkScalar width;
333 SkScalar* widthPtr = NULL;
334 if (textPaint.isUnderlineText() || textPaint.isStrikeThruText())
335 widthPtr = &width;
336
337 SkDrawCacheProc glyphCacheProc = textPaint.getDrawCacheProc();
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000338 alignText(glyphCacheProc, textPaint, glyphIDs, numGlyphs, &x, &y, widthPtr);
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000339 fContent.append("BT\n");
340 setTextTransform(x, y, textPaint.getTextSkewX());
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000341 size_t consumedGlyphCount = 0;
342 while (numGlyphs > consumedGlyphCount) {
343 updateFont(textPaint, glyphIDs[consumedGlyphCount]);
344 SkPDFFont* font = fGraphicStack[fGraphicStackIndex].fFont;
vandebo@chromium.org01294102011-02-28 19:52:18 +0000345 size_t availableGlyphs =
346 font->glyphsToPDFFontEncoding(glyphIDs + consumedGlyphCount,
347 numGlyphs - consumedGlyphCount);
348 fContent.append(SkPDFString::formatString(glyphIDs + consumedGlyphCount,
349 availableGlyphs,
350 font->multiByteGlyphs()));
351 consumedGlyphCount += availableGlyphs;
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000352 fContent.append(" Tj\n");
353 }
354 fContent.append("ET\n");
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000355
356 // Draw underline and/or strikethrough if the paint has them.
357 // drawPosText() and drawTextOnPath() don't draw underline or strikethrough
358 // because the raster versions don't. Use paint instead of textPaint
359 // because we may have changed strokeWidth to do fakeBold text.
360 if (paint.isUnderlineText() || paint.isStrikeThruText()) {
361 SkScalar textSize = paint.getTextSize();
362 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
363
364 if (paint.isUnderlineText()) {
365 SkScalar top = SkScalarMulAdd(textSize, kStdUnderline_Offset, y);
366 SkRect r = SkRect::MakeXYWH(x, top - height, width, height);
367 drawRect(d, r, paint);
368 }
369 if (paint.isStrikeThruText()) {
370 SkScalar top = SkScalarMulAdd(textSize, kStdStrikeThru_Offset, y);
371 SkRect r = SkRect::MakeXYWH(x, top - height, width, height);
372 drawRect(d, r, paint);
373 }
374 }
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000375}
376
377void SkPDFDevice::drawPosText(const SkDraw&, const void* text, size_t len,
378 const SkScalar pos[], SkScalar constY,
379 int scalarsPerPos, const SkPaint& paint) {
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000380 SkASSERT(1 == scalarsPerPos || 2 == scalarsPerPos);
381 SkPaint textPaint = calculateTextPaint(paint);
382 updateGSFromPaint(textPaint, true);
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000383
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000384 // Make sure we have a glyph id encoding.
385 SkAutoFree glyphStorage;
386 uint16_t* glyphIDs;
387 size_t numGlyphs;
388 if (paint.getTextEncoding() != SkPaint::kGlyphID_TextEncoding) {
389 numGlyphs = paint.textToGlyphs(text, len, NULL);
390 glyphIDs = (uint16_t*)sk_malloc_flags(numGlyphs * 2,
391 SK_MALLOC_TEMP | SK_MALLOC_THROW);
392 glyphStorage.set(glyphIDs);
393 paint.textToGlyphs(text, len, glyphIDs);
394 textPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
395 } else {
396 SkASSERT((len & 1) == 0);
397 numGlyphs = len / 2;
398 glyphIDs = (uint16_t*)text;
399 }
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000400
401 SkDrawCacheProc glyphCacheProc = textPaint.getDrawCacheProc();
402 fContent.append("BT\n");
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000403 updateFont(textPaint, glyphIDs[0]);
404 for (size_t i = 0; i < numGlyphs; i++) {
405 SkPDFFont* font = fGraphicStack[fGraphicStackIndex].fFont;
vandebo@chromium.org01294102011-02-28 19:52:18 +0000406 uint16_t encodedValue = glyphIDs[i];
407 if (font->glyphsToPDFFontEncoding(&encodedValue, 1) != 1) {
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000408 updateFont(textPaint, glyphIDs[i]);
409 i--;
410 continue;
411 }
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000412 SkScalar x = pos[i * scalarsPerPos];
413 SkScalar y = scalarsPerPos == 1 ? constY : pos[i * scalarsPerPos + 1];
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000414 alignText(glyphCacheProc, textPaint, glyphIDs + i, 1, &x, &y, NULL);
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000415 setTextTransform(x, y, textPaint.getTextSkewX());
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000416 fContent.append(SkPDFString::formatString(&encodedValue, 1,
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000417 font->multiByteGlyphs()));
418 fContent.append(" Tj\n");
419 }
420 fContent.append("ET\n");
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000421}
422
423void SkPDFDevice::drawTextOnPath(const SkDraw&, const void* text, size_t len,
424 const SkPath& path, const SkMatrix* matrix,
425 const SkPaint& paint) {
vandebo@chromium.orga5180862010-10-26 19:48:49 +0000426 NOT_IMPLEMENTED("drawTextOnPath", true);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000427}
428
429void SkPDFDevice::drawVertices(const SkDraw&, SkCanvas::VertexMode,
430 int vertexCount, const SkPoint verts[],
431 const SkPoint texs[], const SkColor colors[],
432 SkXfermode* xmode, const uint16_t indices[],
433 int indexCount, const SkPaint& paint) {
vandebo@chromium.orga5180862010-10-26 19:48:49 +0000434 NOT_IMPLEMENTED("drawVerticies", true);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000435}
436
vandebo@chromium.orgeb6c7592010-10-26 19:54:45 +0000437void SkPDFDevice::drawDevice(const SkDraw& d, SkDevice* device, int x, int y,
438 const SkPaint& paint) {
439 if ((device->getDeviceCapabilities() & kVector_Capability) == 0) {
440 // If we somehow get a raster device, do what our parent would do.
441 SkDevice::drawDevice(d, device, x, y, paint);
442 return;
443 }
vandebo@chromium.orgeb6c7592010-10-26 19:54:45 +0000444 // Assume that a vector capable device means that it's a PDF Device.
vandebo@chromium.orgeb6c7592010-10-26 19:54:45 +0000445 SkPDFDevice* pdfDevice = static_cast<SkPDFDevice*>(device);
446
vandebo@chromium.org1aef2ed2011-02-03 21:46:10 +0000447 SkMatrix matrix;
reed@google.coma6d59f62011-03-07 21:29:21 +0000448 matrix.setTranslate(SkIntToScalar(x), SkIntToScalar(y));
vandebo@chromium.org1aef2ed2011-02-03 21:46:10 +0000449 SkMatrix curTransform = setTransform(matrix);
vandebo@chromium.org48543272011-02-08 19:28:07 +0000450 updateGSFromPaint(paint, false);
vandebo@chromium.org1aef2ed2011-02-03 21:46:10 +0000451
452 SkPDFFormXObject* xobject = new SkPDFFormXObject(pdfDevice);
vandebo@chromium.orgeb6c7592010-10-26 19:54:45 +0000453 fXObjectResources.push(xobject); // Transfer reference.
454 fContent.append("/X");
455 fContent.appendS32(fXObjectResources.count() - 1);
456 fContent.append(" Do\n");
vandebo@chromium.org1aef2ed2011-02-03 21:46:10 +0000457 setTransform(curTransform);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000458}
459
460const SkRefPtr<SkPDFDict>& SkPDFDevice::getResourceDict() {
461 if (fResourceDict.get() == NULL) {
462 fResourceDict = new SkPDFDict;
463 fResourceDict->unref(); // SkRefPtr and new both took a reference.
464
465 if (fGraphicStateResources.count()) {
466 SkRefPtr<SkPDFDict> extGState = new SkPDFDict();
467 extGState->unref(); // SkRefPtr and new both took a reference.
468 for (int i = 0; i < fGraphicStateResources.count(); i++) {
469 SkString nameString("G");
470 nameString.appendS32(i);
vandebo@chromium.orgf7c15762011-02-01 22:19:44 +0000471 extGState->insert(
472 nameString.c_str(),
473 new SkPDFObjRef(fGraphicStateResources[i]))->unref();
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000474 }
475 fResourceDict->insert("ExtGState", extGState.get());
476 }
477
478 if (fXObjectResources.count()) {
479 SkRefPtr<SkPDFDict> xObjects = new SkPDFDict();
480 xObjects->unref(); // SkRefPtr and new both took a reference.
481 for (int i = 0; i < fXObjectResources.count(); i++) {
482 SkString nameString("X");
483 nameString.appendS32(i);
vandebo@chromium.orgf7c15762011-02-01 22:19:44 +0000484 xObjects->insert(
485 nameString.c_str(),
486 new SkPDFObjRef(fXObjectResources[i]))->unref();
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000487 }
488 fResourceDict->insert("XObject", xObjects.get());
489 }
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000490
491 if (fFontResources.count()) {
492 SkRefPtr<SkPDFDict> fonts = new SkPDFDict();
493 fonts->unref(); // SkRefPtr and new both took a reference.
494 for (int i = 0; i < fFontResources.count(); i++) {
495 SkString nameString("F");
496 nameString.appendS32(i);
vandebo@chromium.orgf7c15762011-02-01 22:19:44 +0000497 fonts->insert(nameString.c_str(),
498 new SkPDFObjRef(fFontResources[i]))->unref();
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000499 }
500 fResourceDict->insert("Font", fonts.get());
501 }
502
503 // For compatibility, add all proc sets (only used for output to PS
504 // devices).
505 const char procs[][7] = {"PDF", "Text", "ImageB", "ImageC", "ImageI"};
506 SkRefPtr<SkPDFArray> procSets = new SkPDFArray();
507 procSets->unref(); // SkRefPtr and new both took a reference.
508 procSets->reserve(SK_ARRAY_COUNT(procs));
vandebo@chromium.orgf7c15762011-02-01 22:19:44 +0000509 for (size_t i = 0; i < SK_ARRAY_COUNT(procs); i++)
510 procSets->append(new SkPDFName(procs[i]))->unref();
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000511 fResourceDict->insert("ProcSet", procSets.get());
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000512 }
513 return fResourceDict;
514}
515
vandebo@chromium.orga5180862010-10-26 19:48:49 +0000516void SkPDFDevice::getResources(SkTDArray<SkPDFObject*>* resourceList) const {
517 resourceList->setReserve(resourceList->count() +
518 fGraphicStateResources.count() +
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000519 fXObjectResources.count() +
520 fFontResources.count());
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000521 for (int i = 0; i < fGraphicStateResources.count(); i++) {
vandebo@chromium.orga5180862010-10-26 19:48:49 +0000522 resourceList->push(fGraphicStateResources[i]);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000523 fGraphicStateResources[i]->ref();
vandebo@chromium.orga5180862010-10-26 19:48:49 +0000524 fGraphicStateResources[i]->getResources(resourceList);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000525 }
526 for (int i = 0; i < fXObjectResources.count(); i++) {
vandebo@chromium.orga5180862010-10-26 19:48:49 +0000527 resourceList->push(fXObjectResources[i]);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000528 fXObjectResources[i]->ref();
vandebo@chromium.orga5180862010-10-26 19:48:49 +0000529 fXObjectResources[i]->getResources(resourceList);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000530 }
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000531 for (int i = 0; i < fFontResources.count(); i++) {
532 resourceList->push(fFontResources[i]);
533 fFontResources[i]->ref();
534 fFontResources[i]->getResources(resourceList);
535 }
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000536}
537
vandebo@chromium.orga5180862010-10-26 19:48:49 +0000538SkRefPtr<SkPDFArray> SkPDFDevice::getMediaBox() const {
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000539 SkRefPtr<SkPDFInt> zero = new SkPDFInt(0);
540 zero->unref(); // SkRefPtr and new both took a reference.
vandebo@chromium.orgf7c15762011-02-01 22:19:44 +0000541
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000542 SkRefPtr<SkPDFArray> mediaBox = new SkPDFArray();
543 mediaBox->unref(); // SkRefPtr and new both took a reference.
544 mediaBox->reserve(4);
545 mediaBox->append(zero.get());
546 mediaBox->append(zero.get());
vandebo@chromium.orgf7c15762011-02-01 22:19:44 +0000547 mediaBox->append(new SkPDFInt(fWidth))->unref();
548 mediaBox->append(new SkPDFInt(fHeight))->unref();
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000549 return mediaBox;
550}
551
vandebo@chromium.orgc2a9b7f2011-02-24 23:22:30 +0000552SkStream* SkPDFDevice::content() const {
553 size_t offset = fContent.size();
554 char* data = (char*)sk_malloc_throw(offset + fGraphicStackIndex * 2);
555 memcpy(data, fContent.c_str(), offset);
556 for (int i = 0; i < fGraphicStackIndex; i++) {
557 data[offset++] = 'Q';
558 data[offset++] = '\n';
559 }
560 SkMemoryStream* result = new SkMemoryStream;
561 result->setMemoryOwned(data, offset);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000562 return result;
563}
564
565// Private
566
567// TODO(vandebo) handle these cases.
vandebo@chromium.orga5180862010-10-26 19:48:49 +0000568#define PAINTCHECK(x,y) NOT_IMPLEMENTED(newPaint.x() y, false)
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000569
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000570void SkPDFDevice::updateGSFromPaint(const SkPaint& newPaint, bool forText) {
vandebo@chromium.org48543272011-02-08 19:28:07 +0000571 SkASSERT(newPaint.getPathEffect() == NULL);
572
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000573 PAINTCHECK(getMaskFilter, != NULL);
574 PAINTCHECK(getShader, != NULL);
575 PAINTCHECK(getColorFilter, != NULL);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000576
577 SkRefPtr<SkPDFGraphicState> newGraphicState =
578 SkPDFGraphicState::getGraphicStateForPaint(newPaint);
579 newGraphicState->unref(); // getGraphicState and SkRefPtr both took a ref.
580 // newGraphicState has been canonicalized so we can directly compare
581 // pointers.
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000582 if (fGraphicStack[fGraphicStackIndex].fGraphicState !=
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000583 newGraphicState.get()) {
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000584 int resourceIndex = fGraphicStateResources.find(newGraphicState.get());
585 if (resourceIndex < 0) {
586 resourceIndex = fGraphicStateResources.count();
587 fGraphicStateResources.push(newGraphicState.get());
588 newGraphicState->ref();
589 }
590 fContent.append("/G");
591 fContent.appendS32(resourceIndex);
592 fContent.append(" gs\n");
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000593 fGraphicStack[fGraphicStackIndex].fGraphicState = newGraphicState.get();
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000594 }
595
596 SkColor newColor = newPaint.getColor();
597 newColor = SkColorSetA(newColor, 0xFF);
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000598 if (fGraphicStack[fGraphicStackIndex].fColor != newColor) {
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000599 SkString colorString = toPDFColor(newColor);
600 fContent.append(colorString);
601 fContent.append("RG ");
602 fContent.append(colorString);
603 fContent.append("rg\n");
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000604 fGraphicStack[fGraphicStackIndex].fColor = newColor;
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000605 }
606
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000607 if (forText) {
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000608 if (fGraphicStack[fGraphicStackIndex].fTextScaleX !=
609 newPaint.getTextScaleX()) {
610 SkScalar scale = newPaint.getTextScaleX();
611 SkScalar pdfScale = SkScalarMul(scale, SkIntToScalar(100));
vandebo@chromium.org094316b2011-03-04 03:15:13 +0000612 SkPDFScalar::Append(pdfScale, &fContent);
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000613 fContent.append(" Tz\n");
614 fGraphicStack[fGraphicStackIndex].fTextScaleX = scale;
615 }
616
617 if (fGraphicStack[fGraphicStackIndex].fTextFill !=
618 newPaint.getStyle()) {
619 SK_COMPILE_ASSERT(SkPaint::kFill_Style == 0, enum_must_match_value);
620 SK_COMPILE_ASSERT(SkPaint::kStroke_Style == 1,
621 enum_must_match_value);
622 SK_COMPILE_ASSERT(SkPaint::kStrokeAndFill_Style == 2,
623 enum_must_match_value);
624 fContent.appendS32(newPaint.getStyle());
625 fContent.append(" Tr\n");
626 fGraphicStack[fGraphicStackIndex].fTextFill = newPaint.getStyle();
627 }
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000628 }
629}
630
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000631void SkPDFDevice::updateFont(const SkPaint& paint, uint16_t glyphID) {
ctguil@chromium.org9db86bb2011-03-04 21:43:27 +0000632 SkTypeface* typeface = paint.getTypeface();
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000633 if (fGraphicStack[fGraphicStackIndex].fTextSize != paint.getTextSize() ||
634 fGraphicStack[fGraphicStackIndex].fFont == NULL ||
ctguil@chromium.org9db86bb2011-03-04 21:43:27 +0000635 fGraphicStack[fGraphicStackIndex].fFont->typeface() != typeface ||
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000636 !fGraphicStack[fGraphicStackIndex].fFont->hasGlyph(glyphID)) {
ctguil@chromium.org9db86bb2011-03-04 21:43:27 +0000637 int fontIndex = getFontResourceIndex(typeface, glyphID);
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000638 fContent.append("/F");
639 fContent.appendS32(fontIndex);
640 fContent.append(" ");
vandebo@chromium.org094316b2011-03-04 03:15:13 +0000641 SkPDFScalar::Append(paint.getTextSize(), &fContent);
vandebo@chromium.org6bcf9952011-03-05 00:24:50 +0000642 fContent.append(" Tf\n");
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000643 fGraphicStack[fGraphicStackIndex].fTextSize = paint.getTextSize();
644 fGraphicStack[fGraphicStackIndex].fFont = fFontResources[fontIndex];
645 }
646}
647
ctguil@chromium.org9db86bb2011-03-04 21:43:27 +0000648int SkPDFDevice::getFontResourceIndex(SkTypeface* typeface, uint16_t glyphID) {
649 SkRefPtr<SkPDFFont> newFont = SkPDFFont::getFontResource(typeface, glyphID);
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000650 newFont->unref(); // getFontResource and SkRefPtr both took a ref.
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000651 int resourceIndex = fFontResources.find(newFont.get());
652 if (resourceIndex < 0) {
653 resourceIndex = fFontResources.count();
654 fFontResources.push(newFont.get());
655 newFont->ref();
656 }
657 return resourceIndex;
658}
659
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000660void SkPDFDevice::pushGS() {
661 SkASSERT(fGraphicStackIndex < 2);
662 fContent.append("q\n");
663 fGraphicStackIndex++;
664 fGraphicStack[fGraphicStackIndex] = fGraphicStack[fGraphicStackIndex - 1];
665}
666
667void SkPDFDevice::popGS() {
668 SkASSERT(fGraphicStackIndex > 0);
669 fContent.append("Q\n");
670 fGraphicStackIndex--;
671}
672
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000673void SkPDFDevice::setTextTransform(SkScalar x, SkScalar y, SkScalar textSkewX) {
674 // Flip the text about the x-axis to account for origin swap and include
675 // the passed parameters.
676 fContent.append("1 0 ");
vandebo@chromium.org094316b2011-03-04 03:15:13 +0000677 SkPDFScalar::Append(0 - textSkewX, &fContent);
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000678 fContent.append(" -1 ");
vandebo@chromium.org094316b2011-03-04 03:15:13 +0000679 SkPDFScalar::Append(x, &fContent);
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000680 fContent.append(" ");
vandebo@chromium.org094316b2011-03-04 03:15:13 +0000681 SkPDFScalar::Append(y, &fContent);
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000682 fContent.append(" Tm\n");
683}
684
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000685void SkPDFDevice::internalDrawBitmap(const SkMatrix& matrix,
686 const SkBitmap& bitmap,
vandebo@chromium.orgbefebb82011-01-29 01:38:50 +0000687 const SkIRect* srcRect,
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000688 const SkPaint& paint) {
vandebo@chromium.orgbefebb82011-01-29 01:38:50 +0000689 SkIRect subset = SkIRect::MakeWH(bitmap.width(), bitmap.height());
690 if (srcRect && !subset.intersect(*srcRect))
691 return;
692
vandebo@chromium.org1cfa2c42011-01-31 19:35:43 +0000693 SkPDFImage* image = SkPDFImage::CreateImage(bitmap, subset, paint);
694 if (!image)
695 return;
696
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000697 SkMatrix scaled;
698 // Adjust for origin flip.
699 scaled.setScale(1, -1);
700 scaled.postTranslate(0, 1);
701 // Scale the image up from 1x1 to WxH.
reed@google.coma6d59f62011-03-07 21:29:21 +0000702 scaled.postScale(SkIntToScalar(subset.width()),
703 SkIntToScalar(subset.height()));
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000704 scaled.postConcat(matrix);
705 SkMatrix curTransform = setTransform(scaled);
vandebo@chromium.org48543272011-02-08 19:28:07 +0000706 updateGSFromPaint(paint, false);
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000707
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000708 fXObjectResources.push(image); // Transfer reference.
709 fContent.append("/X");
710 fContent.appendS32(fXObjectResources.count() - 1);
711 fContent.append(" Do\n");
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000712 setTransform(curTransform);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000713}
714
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000715SkMatrix SkPDFDevice::setTransform(const SkMatrix& m) {
716 SkMatrix old = fGraphicStack[fGraphicStackIndex].fTransform;
717 if (old == m)
718 return old;
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000719
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000720 if (old.getType() != SkMatrix::kIdentity_Mask) {
721 SkASSERT(fGraphicStackIndex > 0);
722 SkASSERT(fGraphicStack[fGraphicStackIndex - 1].fTransform.getType() ==
723 SkMatrix::kIdentity_Mask);
724 SkASSERT(fGraphicStack[fGraphicStackIndex].fClip ==
725 fGraphicStack[fGraphicStackIndex - 1].fClip);
726 popGS();
727 }
728 if (m.getType() == SkMatrix::kIdentity_Mask)
729 return old;
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000730
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000731 if (fGraphicStackIndex == 0 || fGraphicStack[fGraphicStackIndex].fClip !=
732 fGraphicStack[fGraphicStackIndex - 1].fClip)
733 pushGS();
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000734
vandebo@chromium.orgddbbd802010-10-26 19:45:06 +0000735 SkScalar transform[6];
736 SkAssertResult(m.pdfTransform(transform));
737 for (size_t i = 0; i < SK_ARRAY_COUNT(transform); i++) {
vandebo@chromium.org094316b2011-03-04 03:15:13 +0000738 SkPDFScalar::Append(transform[i], &fContent);
vandebo@chromium.orgddbbd802010-10-26 19:45:06 +0000739 fContent.append(" ");
740 }
741 fContent.append("cm\n");
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000742 fGraphicStack[fGraphicStackIndex].fTransform = m;
743
744 return old;
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000745}