blob: b98e4afb0920a11b3c3f6c262daf04046b185793 [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) {
vandebo@chromium.orgbefebb82011-01-29 01:38:50 +0000269 NOT_IMPLEMENTED(prePathMatrix != NULL, true);
vandebo@chromium.org02cc5aa2011-01-25 22:06:29 +0000270
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) {
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000290 SkMatrix transform = matrix;
291 transform.postConcat(fGraphicStack[fGraphicStackIndex].fTransform);
vandebo@chromium.orgbefebb82011-01-29 01:38:50 +0000292 internalDrawBitmap(transform, bitmap, srcRect, paint);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000293}
294
295void SkPDFDevice::drawSprite(const SkDraw&, const SkBitmap& bitmap,
296 int x, int y, const SkPaint& paint) {
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000297 SkMatrix matrix;
298 matrix.setTranslate(x, y);
vandebo@chromium.orgbefebb82011-01-29 01:38:50 +0000299 internalDrawBitmap(matrix, bitmap, NULL, paint);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000300}
301
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000302void SkPDFDevice::drawText(const SkDraw& d, const void* text, size_t len,
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000303 SkScalar x, SkScalar y, const SkPaint& paint) {
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000304 SkPaint textPaint = calculateTextPaint(paint);
305 updateGSFromPaint(textPaint, true);
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000306
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000307 // Make sure we have a glyph id encoding.
308 SkAutoFree glyphStorage;
309 uint16_t* glyphIDs;
310 size_t numGlyphs;
311 if (paint.getTextEncoding() != SkPaint::kGlyphID_TextEncoding) {
312 numGlyphs = paint.textToGlyphs(text, len, NULL);
313 glyphIDs = (uint16_t*)sk_malloc_flags(numGlyphs * 2,
314 SK_MALLOC_TEMP | SK_MALLOC_THROW);
315 glyphStorage.set(glyphIDs);
316 paint.textToGlyphs(text, len, glyphIDs);
317 textPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
318 } else {
319 SkASSERT((len & 1) == 0);
320 numGlyphs = len / 2;
321 glyphIDs = (uint16_t*)text;
322 }
323 SkAutoFree encodedStorage(
324 sk_malloc_flags(numGlyphs * 2, SK_MALLOC_TEMP | SK_MALLOC_THROW));
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000325
326 SkScalar width;
327 SkScalar* widthPtr = NULL;
328 if (textPaint.isUnderlineText() || textPaint.isStrikeThruText())
329 widthPtr = &width;
330
331 SkDrawCacheProc glyphCacheProc = textPaint.getDrawCacheProc();
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000332 alignText(glyphCacheProc, textPaint, glyphIDs, numGlyphs, &x, &y, widthPtr);
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000333 fContent.append("BT\n");
334 setTextTransform(x, y, textPaint.getTextSkewX());
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000335 size_t consumedGlyphCount = 0;
336 while (numGlyphs > consumedGlyphCount) {
337 updateFont(textPaint, glyphIDs[consumedGlyphCount]);
338 SkPDFFont* font = fGraphicStack[fGraphicStackIndex].fFont;
339 size_t encodedLength = numGlyphs * 2;
340 consumedGlyphCount += font->glyphsToPDFFontEncoding(
341 glyphIDs + consumedGlyphCount, numGlyphs - consumedGlyphCount,
342 encodedStorage.get(), &encodedLength);
343 if (font->multiByteGlyphs())
344 encodedLength /= 2;
345 fContent.append(
346 SkPDFString::formatString((const uint16_t*)encodedStorage.get(),
347 encodedLength,
348 font->multiByteGlyphs()));
349 fContent.append(" Tj\n");
350 }
351 fContent.append("ET\n");
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000352
353 // Draw underline and/or strikethrough if the paint has them.
354 // drawPosText() and drawTextOnPath() don't draw underline or strikethrough
355 // because the raster versions don't. Use paint instead of textPaint
356 // because we may have changed strokeWidth to do fakeBold text.
357 if (paint.isUnderlineText() || paint.isStrikeThruText()) {
358 SkScalar textSize = paint.getTextSize();
359 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
360
361 if (paint.isUnderlineText()) {
362 SkScalar top = SkScalarMulAdd(textSize, kStdUnderline_Offset, y);
363 SkRect r = SkRect::MakeXYWH(x, top - height, width, height);
364 drawRect(d, r, paint);
365 }
366 if (paint.isStrikeThruText()) {
367 SkScalar top = SkScalarMulAdd(textSize, kStdStrikeThru_Offset, y);
368 SkRect r = SkRect::MakeXYWH(x, top - height, width, height);
369 drawRect(d, r, paint);
370 }
371 }
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000372}
373
374void SkPDFDevice::drawPosText(const SkDraw&, const void* text, size_t len,
375 const SkScalar pos[], SkScalar constY,
376 int scalarsPerPos, const SkPaint& paint) {
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000377 SkASSERT(1 == scalarsPerPos || 2 == scalarsPerPos);
378 SkPaint textPaint = calculateTextPaint(paint);
379 updateGSFromPaint(textPaint, true);
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000380
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000381 // Make sure we have a glyph id encoding.
382 SkAutoFree glyphStorage;
383 uint16_t* glyphIDs;
384 size_t numGlyphs;
385 if (paint.getTextEncoding() != SkPaint::kGlyphID_TextEncoding) {
386 numGlyphs = paint.textToGlyphs(text, len, NULL);
387 glyphIDs = (uint16_t*)sk_malloc_flags(numGlyphs * 2,
388 SK_MALLOC_TEMP | SK_MALLOC_THROW);
389 glyphStorage.set(glyphIDs);
390 paint.textToGlyphs(text, len, glyphIDs);
391 textPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
392 } else {
393 SkASSERT((len & 1) == 0);
394 numGlyphs = len / 2;
395 glyphIDs = (uint16_t*)text;
396 }
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000397
398 SkDrawCacheProc glyphCacheProc = textPaint.getDrawCacheProc();
399 fContent.append("BT\n");
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000400 updateFont(textPaint, glyphIDs[0]);
401 for (size_t i = 0; i < numGlyphs; i++) {
402 SkPDFFont* font = fGraphicStack[fGraphicStackIndex].fFont;
403 uint16_t encodedValue;
404 size_t encodedLength = 2;
405 if (font->glyphsToPDFFontEncoding(glyphIDs + i, 1, &encodedValue,
406 &encodedLength) == 0) {
407 updateFont(textPaint, glyphIDs[i]);
408 i--;
409 continue;
410 }
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000411 SkScalar x = pos[i * scalarsPerPos];
412 SkScalar y = scalarsPerPos == 1 ? constY : pos[i * scalarsPerPos + 1];
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000413 alignText(glyphCacheProc, textPaint, glyphIDs + i, 1, &x, &y, NULL);
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000414 setTextTransform(x, y, textPaint.getTextSkewX());
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000415 fContent.append(SkPDFString::formatString(&encodedValue, 1,
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000416 font->multiByteGlyphs()));
417 fContent.append(" Tj\n");
418 }
419 fContent.append("ET\n");
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000420}
421
422void SkPDFDevice::drawTextOnPath(const SkDraw&, const void* text, size_t len,
423 const SkPath& path, const SkMatrix* matrix,
424 const SkPaint& paint) {
vandebo@chromium.orga5180862010-10-26 19:48:49 +0000425 NOT_IMPLEMENTED("drawTextOnPath", true);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000426}
427
428void SkPDFDevice::drawVertices(const SkDraw&, SkCanvas::VertexMode,
429 int vertexCount, const SkPoint verts[],
430 const SkPoint texs[], const SkColor colors[],
431 SkXfermode* xmode, const uint16_t indices[],
432 int indexCount, const SkPaint& paint) {
vandebo@chromium.orga5180862010-10-26 19:48:49 +0000433 NOT_IMPLEMENTED("drawVerticies", true);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000434}
435
vandebo@chromium.orgeb6c7592010-10-26 19:54:45 +0000436void SkPDFDevice::drawDevice(const SkDraw& d, SkDevice* device, int x, int y,
437 const SkPaint& paint) {
438 if ((device->getDeviceCapabilities() & kVector_Capability) == 0) {
439 // If we somehow get a raster device, do what our parent would do.
440 SkDevice::drawDevice(d, device, x, y, paint);
441 return;
442 }
443
444 // Assume that a vector capable device means that it's a PDF Device.
445 // TODO(vandebo) handle the paint (alpha and compositing mode).
446 SkMatrix matrix;
447 matrix.setTranslate(x, y);
448 SkPDFDevice* pdfDevice = static_cast<SkPDFDevice*>(device);
449
450 SkPDFFormXObject* xobject = new SkPDFFormXObject(pdfDevice, matrix);
451 fXObjectResources.push(xobject); // Transfer reference.
452 fContent.append("/X");
453 fContent.appendS32(fXObjectResources.count() - 1);
454 fContent.append(" Do\n");
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000455}
456
457const SkRefPtr<SkPDFDict>& SkPDFDevice::getResourceDict() {
458 if (fResourceDict.get() == NULL) {
459 fResourceDict = new SkPDFDict;
460 fResourceDict->unref(); // SkRefPtr and new both took a reference.
461
462 if (fGraphicStateResources.count()) {
463 SkRefPtr<SkPDFDict> extGState = new SkPDFDict();
464 extGState->unref(); // SkRefPtr and new both took a reference.
465 for (int i = 0; i < fGraphicStateResources.count(); i++) {
466 SkString nameString("G");
467 nameString.appendS32(i);
468 SkRefPtr<SkPDFName> name = new SkPDFName(nameString);
469 name->unref(); // SkRefPtr and new both took a reference.
470 SkRefPtr<SkPDFObjRef> gsRef =
471 new SkPDFObjRef(fGraphicStateResources[i]);
472 gsRef->unref(); // SkRefPtr and new both took a reference.
473 extGState->insert(name.get(), gsRef.get());
474 }
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);
484 SkRefPtr<SkPDFName> name = new SkPDFName(nameString);
485 name->unref(); // SkRefPtr and new both took a reference.
486 SkRefPtr<SkPDFObjRef> xObjRef =
487 new SkPDFObjRef(fXObjectResources[i]);
488 xObjRef->unref(); // SkRefPtr and new both took a reference.
489 xObjects->insert(name.get(), xObjRef.get());
490 }
491 fResourceDict->insert("XObject", xObjects.get());
492 }
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000493
494 if (fFontResources.count()) {
495 SkRefPtr<SkPDFDict> fonts = new SkPDFDict();
496 fonts->unref(); // SkRefPtr and new both took a reference.
497 for (int i = 0; i < fFontResources.count(); i++) {
498 SkString nameString("F");
499 nameString.appendS32(i);
500 SkRefPtr<SkPDFName> name = new SkPDFName(nameString);
501 name->unref(); // SkRefPtr and new both took a reference.
502 SkRefPtr<SkPDFObjRef> fontRef =
503 new SkPDFObjRef(fFontResources[i]);
504 fontRef->unref(); // SkRefPtr and new both took a reference.
505 fonts->insert(name.get(), fontRef.get());
506 }
507 fResourceDict->insert("Font", fonts.get());
508 }
509
510 // For compatibility, add all proc sets (only used for output to PS
511 // devices).
512 const char procs[][7] = {"PDF", "Text", "ImageB", "ImageC", "ImageI"};
513 SkRefPtr<SkPDFArray> procSets = new SkPDFArray();
514 procSets->unref(); // SkRefPtr and new both took a reference.
515 procSets->reserve(SK_ARRAY_COUNT(procs));
516 for (size_t i = 0; i < SK_ARRAY_COUNT(procs); i++) {
517 SkRefPtr<SkPDFName> entry = new SkPDFName(procs[i]);
518 entry->unref(); // SkRefPtr and new both took a reference.
519 procSets->append(entry.get());
520 }
521 fResourceDict->insert("ProcSet", procSets.get());
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000522 }
523 return fResourceDict;
524}
525
vandebo@chromium.orga5180862010-10-26 19:48:49 +0000526void SkPDFDevice::getResources(SkTDArray<SkPDFObject*>* resourceList) const {
527 resourceList->setReserve(resourceList->count() +
528 fGraphicStateResources.count() +
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000529 fXObjectResources.count() +
530 fFontResources.count());
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000531 for (int i = 0; i < fGraphicStateResources.count(); i++) {
vandebo@chromium.orga5180862010-10-26 19:48:49 +0000532 resourceList->push(fGraphicStateResources[i]);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000533 fGraphicStateResources[i]->ref();
vandebo@chromium.orga5180862010-10-26 19:48:49 +0000534 fGraphicStateResources[i]->getResources(resourceList);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000535 }
536 for (int i = 0; i < fXObjectResources.count(); i++) {
vandebo@chromium.orga5180862010-10-26 19:48:49 +0000537 resourceList->push(fXObjectResources[i]);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000538 fXObjectResources[i]->ref();
vandebo@chromium.orga5180862010-10-26 19:48:49 +0000539 fXObjectResources[i]->getResources(resourceList);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000540 }
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000541 for (int i = 0; i < fFontResources.count(); i++) {
542 resourceList->push(fFontResources[i]);
543 fFontResources[i]->ref();
544 fFontResources[i]->getResources(resourceList);
545 }
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000546}
547
vandebo@chromium.orga5180862010-10-26 19:48:49 +0000548SkRefPtr<SkPDFArray> SkPDFDevice::getMediaBox() const {
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000549 SkRefPtr<SkPDFInt> zero = new SkPDFInt(0);
550 zero->unref(); // SkRefPtr and new both took a reference.
551 SkRefPtr<SkPDFInt> width = new SkPDFInt(fWidth);
552 width->unref(); // SkRefPtr and new both took a reference.
553 SkRefPtr<SkPDFInt> height = new SkPDFInt(fHeight);
554 height->unref(); // SkRefPtr and new both took a reference.
555 SkRefPtr<SkPDFArray> mediaBox = new SkPDFArray();
556 mediaBox->unref(); // SkRefPtr and new both took a reference.
557 mediaBox->reserve(4);
558 mediaBox->append(zero.get());
559 mediaBox->append(zero.get());
560 mediaBox->append(width.get());
561 mediaBox->append(height.get());
562 return mediaBox;
563}
564
vandebo@chromium.orgddbbd802010-10-26 19:45:06 +0000565SkString SkPDFDevice::content(bool flipOrigin) const {
566 SkString result;
567 // Scale and translate to move the origin from the lower left to the
568 // upper left.
569 if (flipOrigin)
570 result.printf("1 0 0 -1 0 %d cm\n", fHeight);
571 result.append(fContent);
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000572 for (int i = 0; i < fGraphicStackIndex; i++)
573 result.append("Q\n");
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000574 return result;
575}
576
577// Private
578
579// TODO(vandebo) handle these cases.
vandebo@chromium.orga5180862010-10-26 19:48:49 +0000580#define PAINTCHECK(x,y) NOT_IMPLEMENTED(newPaint.x() y, false)
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000581
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000582void SkPDFDevice::updateGSFromPaint(const SkPaint& newPaint, bool forText) {
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000583 PAINTCHECK(getXfermode, != NULL);
584 PAINTCHECK(getPathEffect, != NULL);
585 PAINTCHECK(getMaskFilter, != NULL);
586 PAINTCHECK(getShader, != NULL);
587 PAINTCHECK(getColorFilter, != NULL);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000588
589 SkRefPtr<SkPDFGraphicState> newGraphicState =
590 SkPDFGraphicState::getGraphicStateForPaint(newPaint);
591 newGraphicState->unref(); // getGraphicState and SkRefPtr both took a ref.
592 // newGraphicState has been canonicalized so we can directly compare
593 // pointers.
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000594 if (fGraphicStack[fGraphicStackIndex].fGraphicState !=
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000595 newGraphicState.get()) {
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000596 int resourceIndex = fGraphicStateResources.find(newGraphicState.get());
597 if (resourceIndex < 0) {
598 resourceIndex = fGraphicStateResources.count();
599 fGraphicStateResources.push(newGraphicState.get());
600 newGraphicState->ref();
601 }
602 fContent.append("/G");
603 fContent.appendS32(resourceIndex);
604 fContent.append(" gs\n");
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000605 fGraphicStack[fGraphicStackIndex].fGraphicState = newGraphicState.get();
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000606 }
607
608 SkColor newColor = newPaint.getColor();
609 newColor = SkColorSetA(newColor, 0xFF);
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000610 if (fGraphicStack[fGraphicStackIndex].fColor != newColor) {
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000611 SkString colorString = toPDFColor(newColor);
612 fContent.append(colorString);
613 fContent.append("RG ");
614 fContent.append(colorString);
615 fContent.append("rg\n");
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000616 fGraphicStack[fGraphicStackIndex].fColor = newColor;
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000617 }
618
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000619 if (forText) {
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000620 if (fGraphicStack[fGraphicStackIndex].fTextScaleX !=
621 newPaint.getTextScaleX()) {
622 SkScalar scale = newPaint.getTextScaleX();
623 SkScalar pdfScale = SkScalarMul(scale, SkIntToScalar(100));
624 fContent.appendScalar(pdfScale);
625 fContent.append(" Tz\n");
626 fGraphicStack[fGraphicStackIndex].fTextScaleX = scale;
627 }
628
629 if (fGraphicStack[fGraphicStackIndex].fTextFill !=
630 newPaint.getStyle()) {
631 SK_COMPILE_ASSERT(SkPaint::kFill_Style == 0, enum_must_match_value);
632 SK_COMPILE_ASSERT(SkPaint::kStroke_Style == 1,
633 enum_must_match_value);
634 SK_COMPILE_ASSERT(SkPaint::kStrokeAndFill_Style == 2,
635 enum_must_match_value);
636 fContent.appendS32(newPaint.getStyle());
637 fContent.append(" Tr\n");
638 fGraphicStack[fGraphicStackIndex].fTextFill = newPaint.getStyle();
639 }
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000640 }
641}
642
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000643void SkPDFDevice::updateFont(const SkPaint& paint, uint16_t glyphID) {
644 uint32_t fontID = SkTypeface::UniqueID(paint.getTypeface());
645 if (fGraphicStack[fGraphicStackIndex].fTextSize != paint.getTextSize() ||
646 fGraphicStack[fGraphicStackIndex].fFont == NULL ||
647 fGraphicStack[fGraphicStackIndex].fFont->fontID() != fontID ||
648 !fGraphicStack[fGraphicStackIndex].fFont->hasGlyph(glyphID)) {
649 int fontIndex = getFontResourceIndex(fontID, glyphID);
650 fContent.append("/F");
651 fContent.appendS32(fontIndex);
652 fContent.append(" ");
653 fContent.appendScalar(paint.getTextSize());
654 fContent.append(" Tf\n");
655 fGraphicStack[fGraphicStackIndex].fTextSize = paint.getTextSize();
656 fGraphicStack[fGraphicStackIndex].fFont = fFontResources[fontIndex];
657 }
658}
659
660
661int SkPDFDevice::getFontResourceIndex(uint32_t fontID, uint16_t glyphID) {
662 SkRefPtr<SkPDFFont> newFont = SkPDFFont::getFontResource(fontID, glyphID);
663 newFont->unref(); // getFontResource and SkRefPtr both took a ref.
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000664 int resourceIndex = fFontResources.find(newFont.get());
665 if (resourceIndex < 0) {
666 resourceIndex = fFontResources.count();
667 fFontResources.push(newFont.get());
668 newFont->ref();
669 }
670 return resourceIndex;
671}
672
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000673void SkPDFDevice::moveTo(SkScalar x, SkScalar y) {
674 fContent.appendScalar(x);
675 fContent.append(" ");
676 fContent.appendScalar(y);
677 fContent.append(" m\n");
678}
679
680void SkPDFDevice::appendLine(SkScalar x, SkScalar y) {
681 fContent.appendScalar(x);
682 fContent.append(" ");
683 fContent.appendScalar(y);
684 fContent.append(" l\n");
685}
686
687void SkPDFDevice::appendCubic(SkScalar ctl1X, SkScalar ctl1Y,
688 SkScalar ctl2X, SkScalar ctl2Y,
689 SkScalar dstX, SkScalar dstY) {
690 SkString cmd("y\n");
691 fContent.appendScalar(ctl1X);
692 fContent.append(" ");
693 fContent.appendScalar(ctl1Y);
694 fContent.append(" ");
695 if (ctl2X != dstX || ctl2Y != dstY) {
696 cmd.set("c\n");
697 fContent.appendScalar(ctl2X);
698 fContent.append(" ");
699 fContent.appendScalar(ctl2Y);
700 fContent.append(" ");
701 }
702 fContent.appendScalar(dstX);
703 fContent.append(" ");
704 fContent.appendScalar(dstY);
705 fContent.append(cmd);
706}
707
708void SkPDFDevice::appendRectangle(SkScalar x, SkScalar y,
709 SkScalar w, SkScalar h) {
710 fContent.appendScalar(x);
711 fContent.append(" ");
712 fContent.appendScalar(y);
713 fContent.append(" ");
714 fContent.appendScalar(w);
715 fContent.append(" ");
716 fContent.appendScalar(h);
717 fContent.append(" re\n");
718}
719
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000720void SkPDFDevice::emitPath(const SkPath& path) {
721 SkPoint args[4];
722 SkPath::Iter iter(path, false);
723 for (SkPath::Verb verb = iter.next(args);
724 verb != SkPath::kDone_Verb;
725 verb = iter.next(args)) {
726 // args gets all the points, even the implicit first point.
727 switch (verb) {
728 case SkPath::kMove_Verb:
729 moveTo(args[0].fX, args[0].fY);
730 break;
731 case SkPath::kLine_Verb:
732 appendLine(args[1].fX, args[1].fY);
733 break;
734 case SkPath::kQuad_Verb: {
735 // Convert quad to cubic (degree elevation). http://goo.gl/vS4i
736 const SkScalar three = SkIntToScalar(3);
737 args[1].scale(SkIntToScalar(2));
738 SkScalar ctl1X = SkScalarDiv(args[0].fX + args[1].fX, three);
739 SkScalar ctl1Y = SkScalarDiv(args[0].fY + args[1].fY, three);
740 SkScalar ctl2X = SkScalarDiv(args[2].fX + args[1].fX, three);
741 SkScalar ctl2Y = SkScalarDiv(args[2].fY + args[1].fY, three);
742 appendCubic(ctl1X, ctl1Y, ctl2X, ctl2Y, args[2].fX, args[2].fY);
743 break;
744 }
745 case SkPath::kCubic_Verb:
746 appendCubic(args[1].fX, args[1].fY, args[2].fX, args[2].fY,
747 args[3].fX, args[3].fY);
748 break;
749 case SkPath::kClose_Verb:
750 closePath();
751 break;
752 case SkPath::kDone_Verb:
753 break;
754 default:
755 SkASSERT(false);
756 break;
757 }
758 }
759}
760
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000761void SkPDFDevice::closePath() {
762 fContent.append("h\n");
763}
764
vandebo@chromium.orga5180862010-10-26 19:48:49 +0000765void SkPDFDevice::paintPath(SkPaint::Style style, SkPath::FillType fill) {
766 if (style == SkPaint::kFill_Style)
767 fContent.append("f");
768 else if (style == SkPaint::kStrokeAndFill_Style)
769 fContent.append("B");
770 else if (style == SkPaint::kStroke_Style)
771 fContent.append("S");
772
773 if (style != SkPaint::kStroke_Style) {
774 // Not supported yet.
775 NOT_IMPLEMENTED(fill == SkPath::kInverseEvenOdd_FillType, false);
776 NOT_IMPLEMENTED(fill == SkPath::kInverseWinding_FillType, false);
777 if (fill == SkPath::kEvenOdd_FillType)
778 fContent.append("*");
779 }
780 fContent.append("\n");
781}
782
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000783void SkPDFDevice::strokePath() {
vandebo@chromium.orga5180862010-10-26 19:48:49 +0000784 paintPath(SkPaint::kStroke_Style, SkPath::kWinding_FillType);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000785}
786
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000787void SkPDFDevice::pushGS() {
788 SkASSERT(fGraphicStackIndex < 2);
789 fContent.append("q\n");
790 fGraphicStackIndex++;
791 fGraphicStack[fGraphicStackIndex] = fGraphicStack[fGraphicStackIndex - 1];
792}
793
794void SkPDFDevice::popGS() {
795 SkASSERT(fGraphicStackIndex > 0);
796 fContent.append("Q\n");
797 fGraphicStackIndex--;
798}
799
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000800void SkPDFDevice::setTextTransform(SkScalar x, SkScalar y, SkScalar textSkewX) {
801 // Flip the text about the x-axis to account for origin swap and include
802 // the passed parameters.
803 fContent.append("1 0 ");
804 fContent.appendScalar(0 - textSkewX);
805 fContent.append(" -1 ");
806 fContent.appendScalar(x);
807 fContent.append(" ");
808 fContent.appendScalar(y);
809 fContent.append(" Tm\n");
810}
811
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000812void SkPDFDevice::internalDrawBitmap(const SkMatrix& matrix,
813 const SkBitmap& bitmap,
vandebo@chromium.orgbefebb82011-01-29 01:38:50 +0000814 const SkIRect* srcRect,
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000815 const SkPaint& paint) {
vandebo@chromium.orgbefebb82011-01-29 01:38:50 +0000816 SkIRect subset = SkIRect::MakeWH(bitmap.width(), bitmap.height());
817 if (srcRect && !subset.intersect(*srcRect))
818 return;
819
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000820 SkMatrix scaled;
821 // Adjust for origin flip.
822 scaled.setScale(1, -1);
823 scaled.postTranslate(0, 1);
824 // Scale the image up from 1x1 to WxH.
vandebo@chromium.orgbefebb82011-01-29 01:38:50 +0000825 scaled.postScale(subset.width(), subset.height());
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000826 scaled.postConcat(matrix);
827 SkMatrix curTransform = setTransform(scaled);
828
vandebo@chromium.orgbefebb82011-01-29 01:38:50 +0000829 SkPDFImage* image = new SkPDFImage(bitmap, subset, paint);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000830 fXObjectResources.push(image); // Transfer reference.
831 fContent.append("/X");
832 fContent.appendS32(fXObjectResources.count() - 1);
833 fContent.append(" Do\n");
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000834 setTransform(curTransform);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000835}
836
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000837SkMatrix SkPDFDevice::setTransform(const SkMatrix& m) {
838 SkMatrix old = fGraphicStack[fGraphicStackIndex].fTransform;
839 if (old == m)
840 return old;
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000841
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000842 if (old.getType() != SkMatrix::kIdentity_Mask) {
843 SkASSERT(fGraphicStackIndex > 0);
844 SkASSERT(fGraphicStack[fGraphicStackIndex - 1].fTransform.getType() ==
845 SkMatrix::kIdentity_Mask);
846 SkASSERT(fGraphicStack[fGraphicStackIndex].fClip ==
847 fGraphicStack[fGraphicStackIndex - 1].fClip);
848 popGS();
849 }
850 if (m.getType() == SkMatrix::kIdentity_Mask)
851 return old;
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000852
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000853 if (fGraphicStackIndex == 0 || fGraphicStack[fGraphicStackIndex].fClip !=
854 fGraphicStack[fGraphicStackIndex - 1].fClip)
855 pushGS();
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000856
vandebo@chromium.orgddbbd802010-10-26 19:45:06 +0000857 SkScalar transform[6];
858 SkAssertResult(m.pdfTransform(transform));
859 for (size_t i = 0; i < SK_ARRAY_COUNT(transform); i++) {
860 fContent.appendScalar(transform[i]);
861 fContent.append(" ");
862 }
863 fContent.append("cm\n");
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000864 fGraphicStack[fGraphicStackIndex].fTransform = m;
865
866 return old;
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000867}