blob: 4cbdf9c41d77ec129fc15ecf6aac72e153a52a8c [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"
29#include "SkRect.h"
30#include "SkString.h"
vandebo@chromium.org28be72b2010-11-11 21:37:00 +000031#include "SkTextFormatParams.h"
32#include "SkTypeface.h"
33#include "SkTypes.h"
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +000034
vandebo@chromium.orga5180862010-10-26 19:48:49 +000035#define NOT_IMPLEMENTED(condition, assert) \
36 do { \
37 if (condition) { \
38 fprintf(stderr, "NOT_IMPLEMENTED: " #condition "\n"); \
39 SkASSERT(!assert); \
40 } \
41 } while(0)
42
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +000043// Utility functions
44
45namespace {
46
47SkString toPDFColor(SkColor color) {
48 SkASSERT(SkColorGetA(color) == 0xFF); // We handle alpha elsewhere.
49 SkScalar colorMax = SkIntToScalar(0xFF);
50 SkString result;
vandebo@chromium.orga5180862010-10-26 19:48:49 +000051 result.appendScalar(SkScalarDiv(SkIntToScalar(SkColorGetR(color)),
52 colorMax));
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +000053 result.append(" ");
vandebo@chromium.orga5180862010-10-26 19:48:49 +000054 result.appendScalar(SkScalarDiv(SkIntToScalar(SkColorGetG(color)),
55 colorMax));
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +000056 result.append(" ");
vandebo@chromium.orga5180862010-10-26 19:48:49 +000057 result.appendScalar(SkScalarDiv(SkIntToScalar(SkColorGetB(color)),
58 colorMax));
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +000059 result.append(" ");
60 return result;
61}
62
vandebo@chromium.org28be72b2010-11-11 21:37:00 +000063SkPaint calculateTextPaint(const SkPaint& paint) {
64 SkPaint result = paint;
65 if (result.isFakeBoldText()) {
66 SkScalar fakeBoldScale = SkScalarInterpFunc(result.getTextSize(),
67 kStdFakeBoldInterpKeys,
68 kStdFakeBoldInterpValues,
69 kStdFakeBoldInterpLength);
70 SkScalar width = SkScalarMul(result.getTextSize(), fakeBoldScale);
71 if (result.getStyle() == SkPaint::kFill_Style)
72 result.setStyle(SkPaint::kStrokeAndFill_Style);
73 else
74 width += result.getStrokeWidth();
75 result.setStrokeWidth(width);
76 }
77 return result;
78}
79
80// Stolen from measure_text in SkDraw.cpp and then tweaked.
81void alignText(SkDrawCacheProc glyphCacheProc, const SkPaint& paint,
82 const uint16_t* glyphs, size_t len, SkScalar* x, SkScalar* y,
83 SkScalar* width) {
84 if (paint.getTextAlign() == SkPaint::kLeft_Align && width == NULL)
85 return;
86
87 SkMatrix ident;
88 ident.reset();
89 SkAutoGlyphCache autoCache(paint, &ident);
90 SkGlyphCache* cache = autoCache.getCache();
91
92 const char* start = (char*)glyphs;
93 const char* stop = (char*)(glyphs + len);
94 SkFixed xAdv = 0, yAdv = 0;
95
96 // TODO(vandebo) This probably needs to take kerning into account.
97 while (start < stop) {
98 const SkGlyph& glyph = glyphCacheProc(cache, &start, 0, 0);
99 xAdv += glyph.fAdvanceX;
100 yAdv += glyph.fAdvanceY;
101 };
102 if (width)
103 *width = SkFixedToScalar(xAdv);
104 if (paint.getTextAlign() == SkPaint::kLeft_Align)
105 return;
106
107 SkScalar xAdj = SkFixedToScalar(xAdv);
108 SkScalar yAdj = SkFixedToScalar(yAdv);
109 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
110 xAdj = SkScalarHalf(xAdj);
111 yAdj = SkScalarHalf(yAdj);
112 }
113 *x = *x - xAdj;
114 *y = *y - yAdj;
115}
116
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000117} // namespace
118
119////////////////////////////////////////////////////////////////////////////////
120
reed@android.comf2b98d62010-12-20 18:26:13 +0000121SkDevice* SkPDFDeviceFactory::newDevice(SkCanvas*, SkBitmap::Config config,
122 int width, int height, bool isOpaque,
123 bool /*isForLayer*/) {
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000124 return SkNEW_ARGS(SkPDFDevice, (width, height));
125}
126
reed@android.comf2b98d62010-12-20 18:26:13 +0000127static inline SkBitmap makeABitmap(int width, int height) {
128 SkBitmap bitmap;
129 bitmap.setConfig(SkBitmap::kNo_Config, width, height);
130 return bitmap;
131}
132
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000133SkPDFDevice::SkPDFDevice(int width, int height)
reed@google.com07700442010-12-20 19:46:07 +0000134 : SkDevice(NULL, makeABitmap(width, height), false),
135 fWidth(width),
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000136 fHeight(height),
reed@google.com07700442010-12-20 19:46:07 +0000137 fGraphicStackIndex(0) {
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000138 fGraphicStack[0].fColor = SK_ColorBLACK;
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000139 fGraphicStack[0].fTextSize = SK_ScalarNaN; // This has no default value.
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000140 fGraphicStack[0].fTextScaleX = SK_Scalar1;
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000141 fGraphicStack[0].fTextFill = SkPaint::kFill_Style;
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000142 fGraphicStack[0].fFont = NULL;
143 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.org9b49dc02010-10-20 22:23:29 +0000146}
147
148SkPDFDevice::~SkPDFDevice() {
149 fGraphicStateResources.unrefAll();
150 fXObjectResources.unrefAll();
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000151 fFontResources.unrefAll();
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000152}
153
154void SkPDFDevice::setMatrixClip(const SkMatrix& matrix,
155 const SkRegion& region) {
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000156 // See the comment in the header file above GraphicStackEntry.
157 if (region != fGraphicStack[fGraphicStackIndex].fClip) {
158 while (fGraphicStackIndex > 0)
159 popGS();
160 pushGS();
161
162 SkPath clipPath;
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000163 if (region.getBoundaryPath(&clipPath)) {
164 emitPath(clipPath);
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000165
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000166 SkPath::FillType clipFill = clipPath.getFillType();
167 NOT_IMPLEMENTED(clipFill == SkPath::kInverseEvenOdd_FillType,
168 false);
169 NOT_IMPLEMENTED(clipFill == SkPath::kInverseWinding_FillType,
170 false);
171 if (clipFill == SkPath::kEvenOdd_FillType)
172 fContent.append("W* n ");
173 else
174 fContent.append("W n ");
175 }
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000176
177 fGraphicStack[fGraphicStackIndex].fClip = region;
178 }
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000179 setTransform(matrix);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000180}
181
182void SkPDFDevice::drawPaint(const SkDraw& d, const SkPaint& paint) {
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000183 SkMatrix identityTransform;
184 identityTransform.reset();
185 SkMatrix curTransform = setTransform(identityTransform);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000186
187 SkPaint newPaint = paint;
188 newPaint.setStyle(SkPaint::kFill_Style);
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000189 updateGSFromPaint(newPaint, false);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000190
191 SkRect all = SkRect::MakeWH(width() + 1, height() + 1);
192 drawRect(d, all, newPaint);
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000193 setTransform(curTransform);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000194}
195
196void SkPDFDevice::drawPoints(const SkDraw& d, SkCanvas::PointMode mode,
197 size_t count, const SkPoint* points,
198 const SkPaint& paint) {
199 if (count == 0)
200 return;
201
202 switch (mode) {
203 case SkCanvas::kPolygon_PointMode:
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000204 updateGSFromPaint(paint, false);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000205 moveTo(points[0].fX, points[0].fY);
206 for (size_t i = 1; i < count; i++)
207 appendLine(points[i].fX, points[i].fY);
208 strokePath();
209 break;
210 case SkCanvas::kLines_PointMode:
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000211 updateGSFromPaint(paint, false);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000212 for (size_t i = 0; i < count/2; i++) {
213 moveTo(points[i * 2].fX, points[i * 2].fY);
214 appendLine(points[i * 2 + 1].fX, points[i * 2 + 1].fY);
215 strokePath();
216 }
217 break;
218 case SkCanvas::kPoints_PointMode:
219 if (paint.getStrokeCap() == SkPaint::kRound_Cap) {
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000220 updateGSFromPaint(paint, false);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000221 for (size_t i = 0; i < count; i++) {
222 moveTo(points[i].fX, points[i].fY);
223 strokePath();
224 }
225 } else {
226 // PDF won't draw a single point with square/butt caps because
227 // the orientation is ambiguous. Draw a rectangle instead.
228 SkPaint newPaint = paint;
229 newPaint.setStyle(SkPaint::kFill_Style);
230 SkScalar strokeWidth = paint.getStrokeWidth();
231 SkScalar halfStroke = strokeWidth * SK_ScalarHalf;
232 for (size_t i = 0; i < count; i++) {
233 SkRect r = SkRect::MakeXYWH(points[i].fX, points[i].fY,
234 0, 0);
235 r.inset(-halfStroke, -halfStroke);
236 drawRect(d, r, newPaint);
237 }
238 }
239 break;
240 default:
241 SkASSERT(false);
242 }
243}
244
245void SkPDFDevice::drawRect(const SkDraw& d, const SkRect& r,
246 const SkPaint& paint) {
247 if (paint.getPathEffect()) {
vandebo@chromium.org7d71f7f2010-10-26 19:51:44 +0000248 // Create a path for the rectangle and apply the path effect to it.
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000249 SkPath path;
250 path.addRect(r);
251 paint.getFillPath(path, &path);
252
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000253 SkPaint noEffectPaint(paint);
254 SkSafeUnref(noEffectPaint.setPathEffect(NULL));
vandebo@chromium.org02cc5aa2011-01-25 22:06:29 +0000255 drawPath(d, path, noEffectPaint, NULL, true);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000256 return;
257 }
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000258 updateGSFromPaint(paint, false);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000259
260 // Skia has 0,0 at top left, pdf at bottom left. Do the right thing.
261 SkScalar bottom = r.fBottom < r.fTop ? r.fBottom : r.fTop;
262 appendRectangle(r.fLeft, bottom, r.width(), r.height());
vandebo@chromium.orga5180862010-10-26 19:48:49 +0000263 paintPath(paint.getStyle(), SkPath::kWinding_FillType);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000264}
265
vandebo@chromium.org7d71f7f2010-10-26 19:51:44 +0000266void SkPDFDevice::drawPath(const SkDraw& d, const SkPath& path,
vandebo@chromium.org02cc5aa2011-01-25 22:06:29 +0000267 const SkPaint& paint, const SkMatrix* prePathMatrix,
268 bool pathIsMutable) {
269 NOT_IMPLEMENTED("drawPath with prePathMatrix", (prePathMatrix != NULL));
270
vandebo@chromium.org7d71f7f2010-10-26 19:51:44 +0000271 if (paint.getPathEffect()) {
272 // Apply the path effect to path and draw it that way.
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000273 SkPath noEffectPath;
274 paint.getFillPath(path, &noEffectPath);
vandebo@chromium.org7d71f7f2010-10-26 19:51:44 +0000275
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000276 SkPaint noEffectPaint(paint);
277 SkSafeUnref(noEffectPaint.setPathEffect(NULL));
vandebo@chromium.org02cc5aa2011-01-25 22:06:29 +0000278 drawPath(d, noEffectPath, noEffectPaint, NULL, true);
vandebo@chromium.org7d71f7f2010-10-26 19:51:44 +0000279 return;
280 }
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000281 updateGSFromPaint(paint, false);
vandebo@chromium.org7d71f7f2010-10-26 19:51:44 +0000282
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000283 emitPath(path);
vandebo@chromium.org7d71f7f2010-10-26 19:51:44 +0000284 paintPath(paint.getStyle(), path.getFillType());
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000285}
286
287void SkPDFDevice::drawBitmap(const SkDraw&, const SkBitmap& bitmap,
reed@android.comf2b98d62010-12-20 18:26:13 +0000288 const SkIRect* srcRect,
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000289 const SkMatrix& matrix, const SkPaint& paint) {
reed@android.comf2b98d62010-12-20 18:26:13 +0000290 // TODO: respect srcRect if present
291
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000292 SkMatrix transform = matrix;
293 transform.postConcat(fGraphicStack[fGraphicStackIndex].fTransform);
294 internalDrawBitmap(transform, bitmap, paint);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000295}
296
297void SkPDFDevice::drawSprite(const SkDraw&, const SkBitmap& bitmap,
298 int x, int y, const SkPaint& paint) {
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000299 SkMatrix matrix;
300 matrix.setTranslate(x, y);
301 internalDrawBitmap(matrix, bitmap, paint);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000302}
303
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000304void SkPDFDevice::drawText(const SkDraw& d, const void* text, size_t len,
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000305 SkScalar x, SkScalar y, const SkPaint& paint) {
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000306 SkPaint textPaint = calculateTextPaint(paint);
307 updateGSFromPaint(textPaint, true);
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000308
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000309 // Make sure we have a glyph id encoding.
310 SkAutoFree glyphStorage;
311 uint16_t* glyphIDs;
312 size_t numGlyphs;
313 if (paint.getTextEncoding() != SkPaint::kGlyphID_TextEncoding) {
314 numGlyphs = paint.textToGlyphs(text, len, NULL);
315 glyphIDs = (uint16_t*)sk_malloc_flags(numGlyphs * 2,
316 SK_MALLOC_TEMP | SK_MALLOC_THROW);
317 glyphStorage.set(glyphIDs);
318 paint.textToGlyphs(text, len, glyphIDs);
319 textPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
320 } else {
321 SkASSERT((len & 1) == 0);
322 numGlyphs = len / 2;
323 glyphIDs = (uint16_t*)text;
324 }
325 SkAutoFree encodedStorage(
326 sk_malloc_flags(numGlyphs * 2, SK_MALLOC_TEMP | SK_MALLOC_THROW));
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000327
328 SkScalar width;
329 SkScalar* widthPtr = NULL;
330 if (textPaint.isUnderlineText() || textPaint.isStrikeThruText())
331 widthPtr = &width;
332
333 SkDrawCacheProc glyphCacheProc = textPaint.getDrawCacheProc();
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000334 alignText(glyphCacheProc, textPaint, glyphIDs, numGlyphs, &x, &y, widthPtr);
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000335 fContent.append("BT\n");
336 setTextTransform(x, y, textPaint.getTextSkewX());
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000337 size_t consumedGlyphCount = 0;
338 while (numGlyphs > consumedGlyphCount) {
339 updateFont(textPaint, glyphIDs[consumedGlyphCount]);
340 SkPDFFont* font = fGraphicStack[fGraphicStackIndex].fFont;
341 size_t encodedLength = numGlyphs * 2;
342 consumedGlyphCount += font->glyphsToPDFFontEncoding(
343 glyphIDs + consumedGlyphCount, numGlyphs - consumedGlyphCount,
344 encodedStorage.get(), &encodedLength);
345 if (font->multiByteGlyphs())
346 encodedLength /= 2;
347 fContent.append(
348 SkPDFString::formatString((const uint16_t*)encodedStorage.get(),
349 encodedLength,
350 font->multiByteGlyphs()));
351 fContent.append(" Tj\n");
352 }
353 fContent.append("ET\n");
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000354
355 // Draw underline and/or strikethrough if the paint has them.
356 // drawPosText() and drawTextOnPath() don't draw underline or strikethrough
357 // because the raster versions don't. Use paint instead of textPaint
358 // because we may have changed strokeWidth to do fakeBold text.
359 if (paint.isUnderlineText() || paint.isStrikeThruText()) {
360 SkScalar textSize = paint.getTextSize();
361 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
362
363 if (paint.isUnderlineText()) {
364 SkScalar top = SkScalarMulAdd(textSize, kStdUnderline_Offset, y);
365 SkRect r = SkRect::MakeXYWH(x, top - height, width, height);
366 drawRect(d, r, paint);
367 }
368 if (paint.isStrikeThruText()) {
369 SkScalar top = SkScalarMulAdd(textSize, kStdStrikeThru_Offset, y);
370 SkRect r = SkRect::MakeXYWH(x, top - height, width, height);
371 drawRect(d, r, paint);
372 }
373 }
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000374}
375
376void SkPDFDevice::drawPosText(const SkDraw&, const void* text, size_t len,
377 const SkScalar pos[], SkScalar constY,
378 int scalarsPerPos, const SkPaint& paint) {
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000379 SkASSERT(1 == scalarsPerPos || 2 == scalarsPerPos);
380 SkPaint textPaint = calculateTextPaint(paint);
381 updateGSFromPaint(textPaint, true);
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000382
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000383 // Make sure we have a glyph id encoding.
384 SkAutoFree glyphStorage;
385 uint16_t* glyphIDs;
386 size_t numGlyphs;
387 if (paint.getTextEncoding() != SkPaint::kGlyphID_TextEncoding) {
388 numGlyphs = paint.textToGlyphs(text, len, NULL);
389 glyphIDs = (uint16_t*)sk_malloc_flags(numGlyphs * 2,
390 SK_MALLOC_TEMP | SK_MALLOC_THROW);
391 glyphStorage.set(glyphIDs);
392 paint.textToGlyphs(text, len, glyphIDs);
393 textPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
394 } else {
395 SkASSERT((len & 1) == 0);
396 numGlyphs = len / 2;
397 glyphIDs = (uint16_t*)text;
398 }
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000399
400 SkDrawCacheProc glyphCacheProc = textPaint.getDrawCacheProc();
401 fContent.append("BT\n");
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000402 updateFont(textPaint, glyphIDs[0]);
403 for (size_t i = 0; i < numGlyphs; i++) {
404 SkPDFFont* font = fGraphicStack[fGraphicStackIndex].fFont;
405 uint16_t encodedValue;
406 size_t encodedLength = 2;
407 if (font->glyphsToPDFFontEncoding(glyphIDs + i, 1, &encodedValue,
408 &encodedLength) == 0) {
409 updateFont(textPaint, glyphIDs[i]);
410 i--;
411 continue;
412 }
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000413 SkScalar x = pos[i * scalarsPerPos];
414 SkScalar y = scalarsPerPos == 1 ? constY : pos[i * scalarsPerPos + 1];
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000415 alignText(glyphCacheProc, textPaint, glyphIDs + i, 1, &x, &y, NULL);
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000416 setTextTransform(x, y, textPaint.getTextSkewX());
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000417 fContent.append(SkPDFString::formatString(&encodedValue, 1,
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000418 font->multiByteGlyphs()));
419 fContent.append(" Tj\n");
420 }
421 fContent.append("ET\n");
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000422}
423
424void SkPDFDevice::drawTextOnPath(const SkDraw&, const void* text, size_t len,
425 const SkPath& path, const SkMatrix* matrix,
426 const SkPaint& paint) {
vandebo@chromium.orga5180862010-10-26 19:48:49 +0000427 NOT_IMPLEMENTED("drawTextOnPath", true);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000428}
429
430void SkPDFDevice::drawVertices(const SkDraw&, SkCanvas::VertexMode,
431 int vertexCount, const SkPoint verts[],
432 const SkPoint texs[], const SkColor colors[],
433 SkXfermode* xmode, const uint16_t indices[],
434 int indexCount, const SkPaint& paint) {
vandebo@chromium.orga5180862010-10-26 19:48:49 +0000435 NOT_IMPLEMENTED("drawVerticies", true);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000436}
437
vandebo@chromium.orgeb6c7592010-10-26 19:54:45 +0000438void SkPDFDevice::drawDevice(const SkDraw& d, SkDevice* device, int x, int y,
439 const SkPaint& paint) {
440 if ((device->getDeviceCapabilities() & kVector_Capability) == 0) {
441 // If we somehow get a raster device, do what our parent would do.
442 SkDevice::drawDevice(d, device, x, y, paint);
443 return;
444 }
445
446 // Assume that a vector capable device means that it's a PDF Device.
447 // TODO(vandebo) handle the paint (alpha and compositing mode).
448 SkMatrix matrix;
449 matrix.setTranslate(x, y);
450 SkPDFDevice* pdfDevice = static_cast<SkPDFDevice*>(device);
451
452 SkPDFFormXObject* xobject = new SkPDFFormXObject(pdfDevice, matrix);
453 fXObjectResources.push(xobject); // Transfer reference.
454 fContent.append("/X");
455 fContent.appendS32(fXObjectResources.count() - 1);
456 fContent.append(" Do\n");
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000457}
458
459const SkRefPtr<SkPDFDict>& SkPDFDevice::getResourceDict() {
460 if (fResourceDict.get() == NULL) {
461 fResourceDict = new SkPDFDict;
462 fResourceDict->unref(); // SkRefPtr and new both took a reference.
463
464 if (fGraphicStateResources.count()) {
465 SkRefPtr<SkPDFDict> extGState = new SkPDFDict();
466 extGState->unref(); // SkRefPtr and new both took a reference.
467 for (int i = 0; i < fGraphicStateResources.count(); i++) {
468 SkString nameString("G");
469 nameString.appendS32(i);
470 SkRefPtr<SkPDFName> name = new SkPDFName(nameString);
471 name->unref(); // SkRefPtr and new both took a reference.
472 SkRefPtr<SkPDFObjRef> gsRef =
473 new SkPDFObjRef(fGraphicStateResources[i]);
474 gsRef->unref(); // SkRefPtr and new both took a reference.
475 extGState->insert(name.get(), gsRef.get());
476 }
477 fResourceDict->insert("ExtGState", extGState.get());
478 }
479
480 if (fXObjectResources.count()) {
481 SkRefPtr<SkPDFDict> xObjects = new SkPDFDict();
482 xObjects->unref(); // SkRefPtr and new both took a reference.
483 for (int i = 0; i < fXObjectResources.count(); i++) {
484 SkString nameString("X");
485 nameString.appendS32(i);
486 SkRefPtr<SkPDFName> name = new SkPDFName(nameString);
487 name->unref(); // SkRefPtr and new both took a reference.
488 SkRefPtr<SkPDFObjRef> xObjRef =
489 new SkPDFObjRef(fXObjectResources[i]);
490 xObjRef->unref(); // SkRefPtr and new both took a reference.
491 xObjects->insert(name.get(), xObjRef.get());
492 }
493 fResourceDict->insert("XObject", xObjects.get());
494 }
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000495
496 if (fFontResources.count()) {
497 SkRefPtr<SkPDFDict> fonts = new SkPDFDict();
498 fonts->unref(); // SkRefPtr and new both took a reference.
499 for (int i = 0; i < fFontResources.count(); i++) {
500 SkString nameString("F");
501 nameString.appendS32(i);
502 SkRefPtr<SkPDFName> name = new SkPDFName(nameString);
503 name->unref(); // SkRefPtr and new both took a reference.
504 SkRefPtr<SkPDFObjRef> fontRef =
505 new SkPDFObjRef(fFontResources[i]);
506 fontRef->unref(); // SkRefPtr and new both took a reference.
507 fonts->insert(name.get(), fontRef.get());
508 }
509 fResourceDict->insert("Font", fonts.get());
510 }
511
512 // For compatibility, add all proc sets (only used for output to PS
513 // devices).
514 const char procs[][7] = {"PDF", "Text", "ImageB", "ImageC", "ImageI"};
515 SkRefPtr<SkPDFArray> procSets = new SkPDFArray();
516 procSets->unref(); // SkRefPtr and new both took a reference.
517 procSets->reserve(SK_ARRAY_COUNT(procs));
518 for (size_t i = 0; i < SK_ARRAY_COUNT(procs); i++) {
519 SkRefPtr<SkPDFName> entry = new SkPDFName(procs[i]);
520 entry->unref(); // SkRefPtr and new both took a reference.
521 procSets->append(entry.get());
522 }
523 fResourceDict->insert("ProcSet", procSets.get());
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000524 }
525 return fResourceDict;
526}
527
vandebo@chromium.orga5180862010-10-26 19:48:49 +0000528void SkPDFDevice::getResources(SkTDArray<SkPDFObject*>* resourceList) const {
529 resourceList->setReserve(resourceList->count() +
530 fGraphicStateResources.count() +
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000531 fXObjectResources.count() +
532 fFontResources.count());
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000533 for (int i = 0; i < fGraphicStateResources.count(); i++) {
vandebo@chromium.orga5180862010-10-26 19:48:49 +0000534 resourceList->push(fGraphicStateResources[i]);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000535 fGraphicStateResources[i]->ref();
vandebo@chromium.orga5180862010-10-26 19:48:49 +0000536 fGraphicStateResources[i]->getResources(resourceList);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000537 }
538 for (int i = 0; i < fXObjectResources.count(); i++) {
vandebo@chromium.orga5180862010-10-26 19:48:49 +0000539 resourceList->push(fXObjectResources[i]);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000540 fXObjectResources[i]->ref();
vandebo@chromium.orga5180862010-10-26 19:48:49 +0000541 fXObjectResources[i]->getResources(resourceList);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000542 }
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000543 for (int i = 0; i < fFontResources.count(); i++) {
544 resourceList->push(fFontResources[i]);
545 fFontResources[i]->ref();
546 fFontResources[i]->getResources(resourceList);
547 }
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000548}
549
vandebo@chromium.orga5180862010-10-26 19:48:49 +0000550SkRefPtr<SkPDFArray> SkPDFDevice::getMediaBox() const {
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000551 SkRefPtr<SkPDFInt> zero = new SkPDFInt(0);
552 zero->unref(); // SkRefPtr and new both took a reference.
553 SkRefPtr<SkPDFInt> width = new SkPDFInt(fWidth);
554 width->unref(); // SkRefPtr and new both took a reference.
555 SkRefPtr<SkPDFInt> height = new SkPDFInt(fHeight);
556 height->unref(); // SkRefPtr and new both took a reference.
557 SkRefPtr<SkPDFArray> mediaBox = new SkPDFArray();
558 mediaBox->unref(); // SkRefPtr and new both took a reference.
559 mediaBox->reserve(4);
560 mediaBox->append(zero.get());
561 mediaBox->append(zero.get());
562 mediaBox->append(width.get());
563 mediaBox->append(height.get());
564 return mediaBox;
565}
566
vandebo@chromium.orgddbbd802010-10-26 19:45:06 +0000567SkString SkPDFDevice::content(bool flipOrigin) const {
568 SkString result;
569 // Scale and translate to move the origin from the lower left to the
570 // upper left.
571 if (flipOrigin)
572 result.printf("1 0 0 -1 0 %d cm\n", fHeight);
573 result.append(fContent);
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000574 for (int i = 0; i < fGraphicStackIndex; i++)
575 result.append("Q\n");
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000576 return result;
577}
578
579// Private
580
581// TODO(vandebo) handle these cases.
vandebo@chromium.orga5180862010-10-26 19:48:49 +0000582#define PAINTCHECK(x,y) NOT_IMPLEMENTED(newPaint.x() y, false)
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000583
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000584void SkPDFDevice::updateGSFromPaint(const SkPaint& newPaint, bool forText) {
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000585 PAINTCHECK(getXfermode, != NULL);
586 PAINTCHECK(getPathEffect, != NULL);
587 PAINTCHECK(getMaskFilter, != NULL);
588 PAINTCHECK(getShader, != NULL);
589 PAINTCHECK(getColorFilter, != NULL);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000590
591 SkRefPtr<SkPDFGraphicState> newGraphicState =
592 SkPDFGraphicState::getGraphicStateForPaint(newPaint);
593 newGraphicState->unref(); // getGraphicState and SkRefPtr both took a ref.
594 // newGraphicState has been canonicalized so we can directly compare
595 // pointers.
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000596 if (fGraphicStack[fGraphicStackIndex].fGraphicState !=
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000597 newGraphicState.get()) {
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000598 int resourceIndex = fGraphicStateResources.find(newGraphicState.get());
599 if (resourceIndex < 0) {
600 resourceIndex = fGraphicStateResources.count();
601 fGraphicStateResources.push(newGraphicState.get());
602 newGraphicState->ref();
603 }
604 fContent.append("/G");
605 fContent.appendS32(resourceIndex);
606 fContent.append(" gs\n");
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000607 fGraphicStack[fGraphicStackIndex].fGraphicState = newGraphicState.get();
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000608 }
609
610 SkColor newColor = newPaint.getColor();
611 newColor = SkColorSetA(newColor, 0xFF);
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000612 if (fGraphicStack[fGraphicStackIndex].fColor != newColor) {
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000613 SkString colorString = toPDFColor(newColor);
614 fContent.append(colorString);
615 fContent.append("RG ");
616 fContent.append(colorString);
617 fContent.append("rg\n");
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000618 fGraphicStack[fGraphicStackIndex].fColor = newColor;
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000619 }
620
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000621 if (forText) {
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000622 if (fGraphicStack[fGraphicStackIndex].fTextScaleX !=
623 newPaint.getTextScaleX()) {
624 SkScalar scale = newPaint.getTextScaleX();
625 SkScalar pdfScale = SkScalarMul(scale, SkIntToScalar(100));
626 fContent.appendScalar(pdfScale);
627 fContent.append(" Tz\n");
628 fGraphicStack[fGraphicStackIndex].fTextScaleX = scale;
629 }
630
631 if (fGraphicStack[fGraphicStackIndex].fTextFill !=
632 newPaint.getStyle()) {
633 SK_COMPILE_ASSERT(SkPaint::kFill_Style == 0, enum_must_match_value);
634 SK_COMPILE_ASSERT(SkPaint::kStroke_Style == 1,
635 enum_must_match_value);
636 SK_COMPILE_ASSERT(SkPaint::kStrokeAndFill_Style == 2,
637 enum_must_match_value);
638 fContent.appendS32(newPaint.getStyle());
639 fContent.append(" Tr\n");
640 fGraphicStack[fGraphicStackIndex].fTextFill = newPaint.getStyle();
641 }
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000642 }
643}
644
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000645void SkPDFDevice::updateFont(const SkPaint& paint, uint16_t glyphID) {
646 uint32_t fontID = SkTypeface::UniqueID(paint.getTypeface());
647 if (fGraphicStack[fGraphicStackIndex].fTextSize != paint.getTextSize() ||
648 fGraphicStack[fGraphicStackIndex].fFont == NULL ||
649 fGraphicStack[fGraphicStackIndex].fFont->fontID() != fontID ||
650 !fGraphicStack[fGraphicStackIndex].fFont->hasGlyph(glyphID)) {
651 int fontIndex = getFontResourceIndex(fontID, glyphID);
652 fContent.append("/F");
653 fContent.appendS32(fontIndex);
654 fContent.append(" ");
655 fContent.appendScalar(paint.getTextSize());
656 fContent.append(" Tf\n");
657 fGraphicStack[fGraphicStackIndex].fTextSize = paint.getTextSize();
658 fGraphicStack[fGraphicStackIndex].fFont = fFontResources[fontIndex];
659 }
660}
661
662
663int SkPDFDevice::getFontResourceIndex(uint32_t fontID, uint16_t glyphID) {
664 SkRefPtr<SkPDFFont> newFont = SkPDFFont::getFontResource(fontID, glyphID);
665 newFont->unref(); // getFontResource and SkRefPtr both took a ref.
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000666 int resourceIndex = fFontResources.find(newFont.get());
667 if (resourceIndex < 0) {
668 resourceIndex = fFontResources.count();
669 fFontResources.push(newFont.get());
670 newFont->ref();
671 }
672 return resourceIndex;
673}
674
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000675void SkPDFDevice::moveTo(SkScalar x, SkScalar y) {
676 fContent.appendScalar(x);
677 fContent.append(" ");
678 fContent.appendScalar(y);
679 fContent.append(" m\n");
680}
681
682void SkPDFDevice::appendLine(SkScalar x, SkScalar y) {
683 fContent.appendScalar(x);
684 fContent.append(" ");
685 fContent.appendScalar(y);
686 fContent.append(" l\n");
687}
688
689void SkPDFDevice::appendCubic(SkScalar ctl1X, SkScalar ctl1Y,
690 SkScalar ctl2X, SkScalar ctl2Y,
691 SkScalar dstX, SkScalar dstY) {
692 SkString cmd("y\n");
693 fContent.appendScalar(ctl1X);
694 fContent.append(" ");
695 fContent.appendScalar(ctl1Y);
696 fContent.append(" ");
697 if (ctl2X != dstX || ctl2Y != dstY) {
698 cmd.set("c\n");
699 fContent.appendScalar(ctl2X);
700 fContent.append(" ");
701 fContent.appendScalar(ctl2Y);
702 fContent.append(" ");
703 }
704 fContent.appendScalar(dstX);
705 fContent.append(" ");
706 fContent.appendScalar(dstY);
707 fContent.append(cmd);
708}
709
710void SkPDFDevice::appendRectangle(SkScalar x, SkScalar y,
711 SkScalar w, SkScalar h) {
712 fContent.appendScalar(x);
713 fContent.append(" ");
714 fContent.appendScalar(y);
715 fContent.append(" ");
716 fContent.appendScalar(w);
717 fContent.append(" ");
718 fContent.appendScalar(h);
719 fContent.append(" re\n");
720}
721
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000722void SkPDFDevice::emitPath(const SkPath& path) {
723 SkPoint args[4];
724 SkPath::Iter iter(path, false);
725 for (SkPath::Verb verb = iter.next(args);
726 verb != SkPath::kDone_Verb;
727 verb = iter.next(args)) {
728 // args gets all the points, even the implicit first point.
729 switch (verb) {
730 case SkPath::kMove_Verb:
731 moveTo(args[0].fX, args[0].fY);
732 break;
733 case SkPath::kLine_Verb:
734 appendLine(args[1].fX, args[1].fY);
735 break;
736 case SkPath::kQuad_Verb: {
737 // Convert quad to cubic (degree elevation). http://goo.gl/vS4i
738 const SkScalar three = SkIntToScalar(3);
739 args[1].scale(SkIntToScalar(2));
740 SkScalar ctl1X = SkScalarDiv(args[0].fX + args[1].fX, three);
741 SkScalar ctl1Y = SkScalarDiv(args[0].fY + args[1].fY, three);
742 SkScalar ctl2X = SkScalarDiv(args[2].fX + args[1].fX, three);
743 SkScalar ctl2Y = SkScalarDiv(args[2].fY + args[1].fY, three);
744 appendCubic(ctl1X, ctl1Y, ctl2X, ctl2Y, args[2].fX, args[2].fY);
745 break;
746 }
747 case SkPath::kCubic_Verb:
748 appendCubic(args[1].fX, args[1].fY, args[2].fX, args[2].fY,
749 args[3].fX, args[3].fY);
750 break;
751 case SkPath::kClose_Verb:
752 closePath();
753 break;
754 case SkPath::kDone_Verb:
755 break;
756 default:
757 SkASSERT(false);
758 break;
759 }
760 }
761}
762
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000763void SkPDFDevice::closePath() {
764 fContent.append("h\n");
765}
766
vandebo@chromium.orga5180862010-10-26 19:48:49 +0000767void SkPDFDevice::paintPath(SkPaint::Style style, SkPath::FillType fill) {
768 if (style == SkPaint::kFill_Style)
769 fContent.append("f");
770 else if (style == SkPaint::kStrokeAndFill_Style)
771 fContent.append("B");
772 else if (style == SkPaint::kStroke_Style)
773 fContent.append("S");
774
775 if (style != SkPaint::kStroke_Style) {
776 // Not supported yet.
777 NOT_IMPLEMENTED(fill == SkPath::kInverseEvenOdd_FillType, false);
778 NOT_IMPLEMENTED(fill == SkPath::kInverseWinding_FillType, false);
779 if (fill == SkPath::kEvenOdd_FillType)
780 fContent.append("*");
781 }
782 fContent.append("\n");
783}
784
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000785void SkPDFDevice::strokePath() {
vandebo@chromium.orga5180862010-10-26 19:48:49 +0000786 paintPath(SkPaint::kStroke_Style, SkPath::kWinding_FillType);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000787}
788
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000789void SkPDFDevice::pushGS() {
790 SkASSERT(fGraphicStackIndex < 2);
791 fContent.append("q\n");
792 fGraphicStackIndex++;
793 fGraphicStack[fGraphicStackIndex] = fGraphicStack[fGraphicStackIndex - 1];
794}
795
796void SkPDFDevice::popGS() {
797 SkASSERT(fGraphicStackIndex > 0);
798 fContent.append("Q\n");
799 fGraphicStackIndex--;
800}
801
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000802void SkPDFDevice::setTextTransform(SkScalar x, SkScalar y, SkScalar textSkewX) {
803 // Flip the text about the x-axis to account for origin swap and include
804 // the passed parameters.
805 fContent.append("1 0 ");
806 fContent.appendScalar(0 - textSkewX);
807 fContent.append(" -1 ");
808 fContent.appendScalar(x);
809 fContent.append(" ");
810 fContent.appendScalar(y);
811 fContent.append(" Tm\n");
812}
813
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000814void SkPDFDevice::internalDrawBitmap(const SkMatrix& matrix,
815 const SkBitmap& bitmap,
816 const SkPaint& paint) {
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000817 SkMatrix scaled;
818 // Adjust for origin flip.
819 scaled.setScale(1, -1);
820 scaled.postTranslate(0, 1);
821 // Scale the image up from 1x1 to WxH.
822 scaled.postScale(bitmap.width(), bitmap.height());
823 scaled.postConcat(matrix);
824 SkMatrix curTransform = setTransform(scaled);
825
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000826 SkPDFImage* image = new SkPDFImage(bitmap, paint);
827 fXObjectResources.push(image); // Transfer reference.
828 fContent.append("/X");
829 fContent.appendS32(fXObjectResources.count() - 1);
830 fContent.append(" Do\n");
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000831 setTransform(curTransform);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000832}
833
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000834SkMatrix SkPDFDevice::setTransform(const SkMatrix& m) {
835 SkMatrix old = fGraphicStack[fGraphicStackIndex].fTransform;
836 if (old == m)
837 return old;
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000838
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000839 if (old.getType() != SkMatrix::kIdentity_Mask) {
840 SkASSERT(fGraphicStackIndex > 0);
841 SkASSERT(fGraphicStack[fGraphicStackIndex - 1].fTransform.getType() ==
842 SkMatrix::kIdentity_Mask);
843 SkASSERT(fGraphicStack[fGraphicStackIndex].fClip ==
844 fGraphicStack[fGraphicStackIndex - 1].fClip);
845 popGS();
846 }
847 if (m.getType() == SkMatrix::kIdentity_Mask)
848 return old;
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000849
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000850 if (fGraphicStackIndex == 0 || fGraphicStack[fGraphicStackIndex].fClip !=
851 fGraphicStack[fGraphicStackIndex - 1].fClip)
852 pushGS();
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000853
vandebo@chromium.orgddbbd802010-10-26 19:45:06 +0000854 SkScalar transform[6];
855 SkAssertResult(m.pdfTransform(transform));
856 for (size_t i = 0; i < SK_ARRAY_COUNT(transform); i++) {
857 fContent.appendScalar(transform[i]);
858 fContent.append(" ");
859 }
860 fContent.append("cm\n");
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000861 fGraphicStack[fGraphicStackIndex].fTransform = m;
862
863 return old;
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000864}