blob: 72b2e1bf6a6a0dcd44281b67d848dc591f1bd3f3 [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"); \
vandebo@chromium.orgfb697e72011-02-01 01:04:00 +000039 SkDEBUGCODE(SkASSERT(!assert);) \
vandebo@chromium.orga5180862010-10-26 19:48:49 +000040 } \
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),
vandebo@chromium.org48543272011-02-08 19:28:07 +0000135 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,
reed@google.com46799cd2011-02-22 20:56:26 +0000155 const SkRegion& region,
156 const SkClipStack&) {
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000157 // See the comment in the header file above GraphicStackEntry.
158 if (region != fGraphicStack[fGraphicStackIndex].fClip) {
159 while (fGraphicStackIndex > 0)
160 popGS();
161 pushGS();
162
163 SkPath clipPath;
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000164 if (region.getBoundaryPath(&clipPath)) {
165 emitPath(clipPath);
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000166
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000167 SkPath::FillType clipFill = clipPath.getFillType();
168 NOT_IMPLEMENTED(clipFill == SkPath::kInverseEvenOdd_FillType,
169 false);
170 NOT_IMPLEMENTED(clipFill == SkPath::kInverseWinding_FillType,
171 false);
172 if (clipFill == SkPath::kEvenOdd_FillType)
173 fContent.append("W* n ");
174 else
175 fContent.append("W n ");
176 }
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000177
178 fGraphicStack[fGraphicStackIndex].fClip = region;
179 }
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000180 setTransform(matrix);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000181}
182
183void SkPDFDevice::drawPaint(const SkDraw& d, const SkPaint& paint) {
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000184 SkMatrix identityTransform;
185 identityTransform.reset();
186 SkMatrix curTransform = setTransform(identityTransform);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000187
188 SkPaint newPaint = paint;
189 newPaint.setStyle(SkPaint::kFill_Style);
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000190 updateGSFromPaint(newPaint, false);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000191
192 SkRect all = SkRect::MakeWH(width() + 1, height() + 1);
193 drawRect(d, all, newPaint);
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000194 setTransform(curTransform);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000195}
196
197void SkPDFDevice::drawPoints(const SkDraw& d, SkCanvas::PointMode mode,
198 size_t count, const SkPoint* points,
199 const SkPaint& paint) {
200 if (count == 0)
201 return;
202
203 switch (mode) {
204 case SkCanvas::kPolygon_PointMode:
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000205 updateGSFromPaint(paint, false);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000206 moveTo(points[0].fX, points[0].fY);
207 for (size_t i = 1; i < count; i++)
208 appendLine(points[i].fX, points[i].fY);
209 strokePath();
210 break;
211 case SkCanvas::kLines_PointMode:
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000212 updateGSFromPaint(paint, false);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000213 for (size_t i = 0; i < count/2; i++) {
214 moveTo(points[i * 2].fX, points[i * 2].fY);
215 appendLine(points[i * 2 + 1].fX, points[i * 2 + 1].fY);
216 strokePath();
217 }
218 break;
219 case SkCanvas::kPoints_PointMode:
220 if (paint.getStrokeCap() == SkPaint::kRound_Cap) {
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000221 updateGSFromPaint(paint, false);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000222 for (size_t i = 0; i < count; i++) {
223 moveTo(points[i].fX, points[i].fY);
224 strokePath();
225 }
226 } else {
227 // PDF won't draw a single point with square/butt caps because
228 // the orientation is ambiguous. Draw a rectangle instead.
229 SkPaint newPaint = paint;
230 newPaint.setStyle(SkPaint::kFill_Style);
231 SkScalar strokeWidth = paint.getStrokeWidth();
232 SkScalar halfStroke = strokeWidth * SK_ScalarHalf;
233 for (size_t i = 0; i < count; i++) {
234 SkRect r = SkRect::MakeXYWH(points[i].fX, points[i].fY,
235 0, 0);
236 r.inset(-halfStroke, -halfStroke);
237 drawRect(d, r, newPaint);
238 }
239 }
240 break;
241 default:
242 SkASSERT(false);
243 }
244}
245
246void SkPDFDevice::drawRect(const SkDraw& d, const SkRect& r,
247 const SkPaint& paint) {
248 if (paint.getPathEffect()) {
vandebo@chromium.org7d71f7f2010-10-26 19:51:44 +0000249 // Create a path for the rectangle and apply the path effect to it.
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000250 SkPath path;
251 path.addRect(r);
252 paint.getFillPath(path, &path);
253
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000254 SkPaint noEffectPaint(paint);
255 SkSafeUnref(noEffectPaint.setPathEffect(NULL));
vandebo@chromium.org02cc5aa2011-01-25 22:06:29 +0000256 drawPath(d, path, noEffectPaint, NULL, true);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000257 return;
258 }
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000259 updateGSFromPaint(paint, false);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000260
261 // Skia has 0,0 at top left, pdf at bottom left. Do the right thing.
262 SkScalar bottom = r.fBottom < r.fTop ? r.fBottom : r.fTop;
263 appendRectangle(r.fLeft, bottom, r.width(), r.height());
vandebo@chromium.orga5180862010-10-26 19:48:49 +0000264 paintPath(paint.getStyle(), SkPath::kWinding_FillType);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000265}
266
vandebo@chromium.org7d71f7f2010-10-26 19:51:44 +0000267void SkPDFDevice::drawPath(const SkDraw& d, const SkPath& path,
vandebo@chromium.org02cc5aa2011-01-25 22:06:29 +0000268 const SkPaint& paint, const SkMatrix* prePathMatrix,
269 bool pathIsMutable) {
vandebo@chromium.orgbefebb82011-01-29 01:38:50 +0000270 NOT_IMPLEMENTED(prePathMatrix != NULL, true);
vandebo@chromium.org02cc5aa2011-01-25 22:06:29 +0000271
vandebo@chromium.org7d71f7f2010-10-26 19:51:44 +0000272 if (paint.getPathEffect()) {
273 // Apply the path effect to path and draw it that way.
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000274 SkPath noEffectPath;
275 paint.getFillPath(path, &noEffectPath);
vandebo@chromium.org7d71f7f2010-10-26 19:51:44 +0000276
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000277 SkPaint noEffectPaint(paint);
278 SkSafeUnref(noEffectPaint.setPathEffect(NULL));
vandebo@chromium.org02cc5aa2011-01-25 22:06:29 +0000279 drawPath(d, noEffectPath, noEffectPaint, NULL, true);
vandebo@chromium.org7d71f7f2010-10-26 19:51:44 +0000280 return;
281 }
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000282 updateGSFromPaint(paint, false);
vandebo@chromium.org7d71f7f2010-10-26 19:51:44 +0000283
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000284 emitPath(path);
vandebo@chromium.org7d71f7f2010-10-26 19:51:44 +0000285 paintPath(paint.getStyle(), path.getFillType());
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000286}
287
288void SkPDFDevice::drawBitmap(const SkDraw&, const SkBitmap& bitmap,
reed@android.comf2b98d62010-12-20 18:26:13 +0000289 const SkIRect* srcRect,
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000290 const SkMatrix& matrix, const SkPaint& paint) {
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000291 SkMatrix transform = matrix;
292 transform.postConcat(fGraphicStack[fGraphicStackIndex].fTransform);
vandebo@chromium.orgbefebb82011-01-29 01:38:50 +0000293 internalDrawBitmap(transform, bitmap, srcRect, paint);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000294}
295
296void SkPDFDevice::drawSprite(const SkDraw&, const SkBitmap& bitmap,
297 int x, int y, const SkPaint& paint) {
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000298 SkMatrix matrix;
299 matrix.setTranslate(x, y);
vandebo@chromium.orgbefebb82011-01-29 01:38:50 +0000300 internalDrawBitmap(matrix, bitmap, NULL, paint);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000301}
302
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000303void SkPDFDevice::drawText(const SkDraw& d, const void* text, size_t len,
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000304 SkScalar x, SkScalar y, const SkPaint& paint) {
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000305 SkPaint textPaint = calculateTextPaint(paint);
306 updateGSFromPaint(textPaint, true);
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000307
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000308 // Make sure we have a glyph id encoding.
309 SkAutoFree glyphStorage;
310 uint16_t* glyphIDs;
311 size_t numGlyphs;
312 if (paint.getTextEncoding() != SkPaint::kGlyphID_TextEncoding) {
313 numGlyphs = paint.textToGlyphs(text, len, NULL);
314 glyphIDs = (uint16_t*)sk_malloc_flags(numGlyphs * 2,
315 SK_MALLOC_TEMP | SK_MALLOC_THROW);
316 glyphStorage.set(glyphIDs);
317 paint.textToGlyphs(text, len, glyphIDs);
318 textPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
319 } else {
320 SkASSERT((len & 1) == 0);
321 numGlyphs = len / 2;
322 glyphIDs = (uint16_t*)text;
323 }
324 SkAutoFree encodedStorage(
325 sk_malloc_flags(numGlyphs * 2, SK_MALLOC_TEMP | SK_MALLOC_THROW));
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000326
327 SkScalar width;
328 SkScalar* widthPtr = NULL;
329 if (textPaint.isUnderlineText() || textPaint.isStrikeThruText())
330 widthPtr = &width;
331
332 SkDrawCacheProc glyphCacheProc = textPaint.getDrawCacheProc();
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000333 alignText(glyphCacheProc, textPaint, glyphIDs, numGlyphs, &x, &y, widthPtr);
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000334 fContent.append("BT\n");
335 setTextTransform(x, y, textPaint.getTextSkewX());
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000336 size_t consumedGlyphCount = 0;
337 while (numGlyphs > consumedGlyphCount) {
338 updateFont(textPaint, glyphIDs[consumedGlyphCount]);
339 SkPDFFont* font = fGraphicStack[fGraphicStackIndex].fFont;
340 size_t encodedLength = numGlyphs * 2;
341 consumedGlyphCount += font->glyphsToPDFFontEncoding(
342 glyphIDs + consumedGlyphCount, numGlyphs - consumedGlyphCount,
343 encodedStorage.get(), &encodedLength);
344 if (font->multiByteGlyphs())
345 encodedLength /= 2;
346 fContent.append(
347 SkPDFString::formatString((const uint16_t*)encodedStorage.get(),
348 encodedLength,
349 font->multiByteGlyphs()));
350 fContent.append(" Tj\n");
351 }
352 fContent.append("ET\n");
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000353
354 // Draw underline and/or strikethrough if the paint has them.
355 // drawPosText() and drawTextOnPath() don't draw underline or strikethrough
356 // because the raster versions don't. Use paint instead of textPaint
357 // because we may have changed strokeWidth to do fakeBold text.
358 if (paint.isUnderlineText() || paint.isStrikeThruText()) {
359 SkScalar textSize = paint.getTextSize();
360 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
361
362 if (paint.isUnderlineText()) {
363 SkScalar top = SkScalarMulAdd(textSize, kStdUnderline_Offset, y);
364 SkRect r = SkRect::MakeXYWH(x, top - height, width, height);
365 drawRect(d, r, paint);
366 }
367 if (paint.isStrikeThruText()) {
368 SkScalar top = SkScalarMulAdd(textSize, kStdStrikeThru_Offset, y);
369 SkRect r = SkRect::MakeXYWH(x, top - height, width, height);
370 drawRect(d, r, paint);
371 }
372 }
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000373}
374
375void SkPDFDevice::drawPosText(const SkDraw&, const void* text, size_t len,
376 const SkScalar pos[], SkScalar constY,
377 int scalarsPerPos, const SkPaint& paint) {
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000378 SkASSERT(1 == scalarsPerPos || 2 == scalarsPerPos);
379 SkPaint textPaint = calculateTextPaint(paint);
380 updateGSFromPaint(textPaint, true);
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000381
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000382 // Make sure we have a glyph id encoding.
383 SkAutoFree glyphStorage;
384 uint16_t* glyphIDs;
385 size_t numGlyphs;
386 if (paint.getTextEncoding() != SkPaint::kGlyphID_TextEncoding) {
387 numGlyphs = paint.textToGlyphs(text, len, NULL);
388 glyphIDs = (uint16_t*)sk_malloc_flags(numGlyphs * 2,
389 SK_MALLOC_TEMP | SK_MALLOC_THROW);
390 glyphStorage.set(glyphIDs);
391 paint.textToGlyphs(text, len, glyphIDs);
392 textPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
393 } else {
394 SkASSERT((len & 1) == 0);
395 numGlyphs = len / 2;
396 glyphIDs = (uint16_t*)text;
397 }
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000398
399 SkDrawCacheProc glyphCacheProc = textPaint.getDrawCacheProc();
400 fContent.append("BT\n");
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000401 updateFont(textPaint, glyphIDs[0]);
402 for (size_t i = 0; i < numGlyphs; i++) {
403 SkPDFFont* font = fGraphicStack[fGraphicStackIndex].fFont;
404 uint16_t encodedValue;
405 size_t encodedLength = 2;
406 if (font->glyphsToPDFFontEncoding(glyphIDs + i, 1, &encodedValue,
407 &encodedLength) == 0) {
408 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;
448 matrix.setTranslate(x, y);
449 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.orgddbbd802010-10-26 19:45:06 +0000552SkString SkPDFDevice::content(bool flipOrigin) const {
553 SkString result;
554 // Scale and translate to move the origin from the lower left to the
555 // upper left.
556 if (flipOrigin)
557 result.printf("1 0 0 -1 0 %d cm\n", fHeight);
558 result.append(fContent);
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000559 for (int i = 0; i < fGraphicStackIndex; i++)
560 result.append("Q\n");
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000561 return result;
562}
563
564// Private
565
566// TODO(vandebo) handle these cases.
vandebo@chromium.orga5180862010-10-26 19:48:49 +0000567#define PAINTCHECK(x,y) NOT_IMPLEMENTED(newPaint.x() y, false)
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000568
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000569void SkPDFDevice::updateGSFromPaint(const SkPaint& newPaint, bool forText) {
vandebo@chromium.org48543272011-02-08 19:28:07 +0000570 SkASSERT(newPaint.getPathEffect() == NULL);
571
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000572 PAINTCHECK(getMaskFilter, != NULL);
573 PAINTCHECK(getShader, != NULL);
574 PAINTCHECK(getColorFilter, != NULL);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000575
576 SkRefPtr<SkPDFGraphicState> newGraphicState =
577 SkPDFGraphicState::getGraphicStateForPaint(newPaint);
578 newGraphicState->unref(); // getGraphicState and SkRefPtr both took a ref.
579 // newGraphicState has been canonicalized so we can directly compare
580 // pointers.
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000581 if (fGraphicStack[fGraphicStackIndex].fGraphicState !=
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000582 newGraphicState.get()) {
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000583 int resourceIndex = fGraphicStateResources.find(newGraphicState.get());
584 if (resourceIndex < 0) {
585 resourceIndex = fGraphicStateResources.count();
586 fGraphicStateResources.push(newGraphicState.get());
587 newGraphicState->ref();
588 }
589 fContent.append("/G");
590 fContent.appendS32(resourceIndex);
591 fContent.append(" gs\n");
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000592 fGraphicStack[fGraphicStackIndex].fGraphicState = newGraphicState.get();
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000593 }
594
595 SkColor newColor = newPaint.getColor();
596 newColor = SkColorSetA(newColor, 0xFF);
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000597 if (fGraphicStack[fGraphicStackIndex].fColor != newColor) {
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000598 SkString colorString = toPDFColor(newColor);
599 fContent.append(colorString);
600 fContent.append("RG ");
601 fContent.append(colorString);
602 fContent.append("rg\n");
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000603 fGraphicStack[fGraphicStackIndex].fColor = newColor;
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000604 }
605
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000606 if (forText) {
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000607 if (fGraphicStack[fGraphicStackIndex].fTextScaleX !=
608 newPaint.getTextScaleX()) {
609 SkScalar scale = newPaint.getTextScaleX();
610 SkScalar pdfScale = SkScalarMul(scale, SkIntToScalar(100));
611 fContent.appendScalar(pdfScale);
612 fContent.append(" Tz\n");
613 fGraphicStack[fGraphicStackIndex].fTextScaleX = scale;
614 }
615
616 if (fGraphicStack[fGraphicStackIndex].fTextFill !=
617 newPaint.getStyle()) {
618 SK_COMPILE_ASSERT(SkPaint::kFill_Style == 0, enum_must_match_value);
619 SK_COMPILE_ASSERT(SkPaint::kStroke_Style == 1,
620 enum_must_match_value);
621 SK_COMPILE_ASSERT(SkPaint::kStrokeAndFill_Style == 2,
622 enum_must_match_value);
623 fContent.appendS32(newPaint.getStyle());
624 fContent.append(" Tr\n");
625 fGraphicStack[fGraphicStackIndex].fTextFill = newPaint.getStyle();
626 }
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000627 }
628}
629
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000630void SkPDFDevice::updateFont(const SkPaint& paint, uint16_t glyphID) {
631 uint32_t fontID = SkTypeface::UniqueID(paint.getTypeface());
632 if (fGraphicStack[fGraphicStackIndex].fTextSize != paint.getTextSize() ||
633 fGraphicStack[fGraphicStackIndex].fFont == NULL ||
634 fGraphicStack[fGraphicStackIndex].fFont->fontID() != fontID ||
635 !fGraphicStack[fGraphicStackIndex].fFont->hasGlyph(glyphID)) {
636 int fontIndex = getFontResourceIndex(fontID, glyphID);
637 fContent.append("/F");
638 fContent.appendS32(fontIndex);
639 fContent.append(" ");
640 fContent.appendScalar(paint.getTextSize());
641 fContent.append(" Tf\n");
642 fGraphicStack[fGraphicStackIndex].fTextSize = paint.getTextSize();
643 fGraphicStack[fGraphicStackIndex].fFont = fFontResources[fontIndex];
644 }
645}
646
647
648int SkPDFDevice::getFontResourceIndex(uint32_t fontID, uint16_t glyphID) {
649 SkRefPtr<SkPDFFont> newFont = SkPDFFont::getFontResource(fontID, glyphID);
650 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.org9b49dc02010-10-20 22:23:29 +0000660void SkPDFDevice::moveTo(SkScalar x, SkScalar y) {
661 fContent.appendScalar(x);
662 fContent.append(" ");
663 fContent.appendScalar(y);
664 fContent.append(" m\n");
665}
666
667void SkPDFDevice::appendLine(SkScalar x, SkScalar y) {
668 fContent.appendScalar(x);
669 fContent.append(" ");
670 fContent.appendScalar(y);
671 fContent.append(" l\n");
672}
673
674void SkPDFDevice::appendCubic(SkScalar ctl1X, SkScalar ctl1Y,
675 SkScalar ctl2X, SkScalar ctl2Y,
676 SkScalar dstX, SkScalar dstY) {
677 SkString cmd("y\n");
678 fContent.appendScalar(ctl1X);
679 fContent.append(" ");
680 fContent.appendScalar(ctl1Y);
681 fContent.append(" ");
682 if (ctl2X != dstX || ctl2Y != dstY) {
683 cmd.set("c\n");
684 fContent.appendScalar(ctl2X);
685 fContent.append(" ");
686 fContent.appendScalar(ctl2Y);
687 fContent.append(" ");
688 }
689 fContent.appendScalar(dstX);
690 fContent.append(" ");
691 fContent.appendScalar(dstY);
vandebo@chromium.org1cfa2c42011-01-31 19:35:43 +0000692 fContent.append(" ");
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000693 fContent.append(cmd);
694}
695
696void SkPDFDevice::appendRectangle(SkScalar x, SkScalar y,
697 SkScalar w, SkScalar h) {
698 fContent.appendScalar(x);
699 fContent.append(" ");
700 fContent.appendScalar(y);
701 fContent.append(" ");
702 fContent.appendScalar(w);
703 fContent.append(" ");
704 fContent.appendScalar(h);
705 fContent.append(" re\n");
706}
707
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000708void SkPDFDevice::emitPath(const SkPath& path) {
709 SkPoint args[4];
710 SkPath::Iter iter(path, false);
711 for (SkPath::Verb verb = iter.next(args);
712 verb != SkPath::kDone_Verb;
713 verb = iter.next(args)) {
714 // args gets all the points, even the implicit first point.
715 switch (verb) {
716 case SkPath::kMove_Verb:
717 moveTo(args[0].fX, args[0].fY);
718 break;
719 case SkPath::kLine_Verb:
720 appendLine(args[1].fX, args[1].fY);
721 break;
722 case SkPath::kQuad_Verb: {
723 // Convert quad to cubic (degree elevation). http://goo.gl/vS4i
724 const SkScalar three = SkIntToScalar(3);
725 args[1].scale(SkIntToScalar(2));
726 SkScalar ctl1X = SkScalarDiv(args[0].fX + args[1].fX, three);
727 SkScalar ctl1Y = SkScalarDiv(args[0].fY + args[1].fY, three);
728 SkScalar ctl2X = SkScalarDiv(args[2].fX + args[1].fX, three);
729 SkScalar ctl2Y = SkScalarDiv(args[2].fY + args[1].fY, three);
730 appendCubic(ctl1X, ctl1Y, ctl2X, ctl2Y, args[2].fX, args[2].fY);
731 break;
732 }
733 case SkPath::kCubic_Verb:
734 appendCubic(args[1].fX, args[1].fY, args[2].fX, args[2].fY,
735 args[3].fX, args[3].fY);
736 break;
737 case SkPath::kClose_Verb:
738 closePath();
739 break;
740 case SkPath::kDone_Verb:
741 break;
742 default:
743 SkASSERT(false);
744 break;
745 }
746 }
747}
748
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000749void SkPDFDevice::closePath() {
750 fContent.append("h\n");
751}
752
vandebo@chromium.orga5180862010-10-26 19:48:49 +0000753void SkPDFDevice::paintPath(SkPaint::Style style, SkPath::FillType fill) {
754 if (style == SkPaint::kFill_Style)
755 fContent.append("f");
756 else if (style == SkPaint::kStrokeAndFill_Style)
757 fContent.append("B");
758 else if (style == SkPaint::kStroke_Style)
759 fContent.append("S");
760
761 if (style != SkPaint::kStroke_Style) {
762 // Not supported yet.
763 NOT_IMPLEMENTED(fill == SkPath::kInverseEvenOdd_FillType, false);
764 NOT_IMPLEMENTED(fill == SkPath::kInverseWinding_FillType, false);
765 if (fill == SkPath::kEvenOdd_FillType)
766 fContent.append("*");
767 }
768 fContent.append("\n");
769}
770
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000771void SkPDFDevice::strokePath() {
vandebo@chromium.orga5180862010-10-26 19:48:49 +0000772 paintPath(SkPaint::kStroke_Style, SkPath::kWinding_FillType);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000773}
774
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000775void SkPDFDevice::pushGS() {
776 SkASSERT(fGraphicStackIndex < 2);
777 fContent.append("q\n");
778 fGraphicStackIndex++;
779 fGraphicStack[fGraphicStackIndex] = fGraphicStack[fGraphicStackIndex - 1];
780}
781
782void SkPDFDevice::popGS() {
783 SkASSERT(fGraphicStackIndex > 0);
784 fContent.append("Q\n");
785 fGraphicStackIndex--;
786}
787
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000788void SkPDFDevice::setTextTransform(SkScalar x, SkScalar y, SkScalar textSkewX) {
789 // Flip the text about the x-axis to account for origin swap and include
790 // the passed parameters.
791 fContent.append("1 0 ");
792 fContent.appendScalar(0 - textSkewX);
793 fContent.append(" -1 ");
794 fContent.appendScalar(x);
795 fContent.append(" ");
796 fContent.appendScalar(y);
797 fContent.append(" Tm\n");
798}
799
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000800void SkPDFDevice::internalDrawBitmap(const SkMatrix& matrix,
801 const SkBitmap& bitmap,
vandebo@chromium.orgbefebb82011-01-29 01:38:50 +0000802 const SkIRect* srcRect,
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000803 const SkPaint& paint) {
vandebo@chromium.orgbefebb82011-01-29 01:38:50 +0000804 SkIRect subset = SkIRect::MakeWH(bitmap.width(), bitmap.height());
805 if (srcRect && !subset.intersect(*srcRect))
806 return;
807
vandebo@chromium.org1cfa2c42011-01-31 19:35:43 +0000808 SkPDFImage* image = SkPDFImage::CreateImage(bitmap, subset, paint);
809 if (!image)
810 return;
811
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000812 SkMatrix scaled;
813 // Adjust for origin flip.
814 scaled.setScale(1, -1);
815 scaled.postTranslate(0, 1);
816 // Scale the image up from 1x1 to WxH.
vandebo@chromium.orgbefebb82011-01-29 01:38:50 +0000817 scaled.postScale(subset.width(), subset.height());
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000818 scaled.postConcat(matrix);
819 SkMatrix curTransform = setTransform(scaled);
vandebo@chromium.org48543272011-02-08 19:28:07 +0000820 updateGSFromPaint(paint, false);
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000821
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000822 fXObjectResources.push(image); // Transfer reference.
823 fContent.append("/X");
824 fContent.appendS32(fXObjectResources.count() - 1);
825 fContent.append(" Do\n");
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000826 setTransform(curTransform);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000827}
828
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000829SkMatrix SkPDFDevice::setTransform(const SkMatrix& m) {
830 SkMatrix old = fGraphicStack[fGraphicStackIndex].fTransform;
831 if (old == m)
832 return old;
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000833
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000834 if (old.getType() != SkMatrix::kIdentity_Mask) {
835 SkASSERT(fGraphicStackIndex > 0);
836 SkASSERT(fGraphicStack[fGraphicStackIndex - 1].fTransform.getType() ==
837 SkMatrix::kIdentity_Mask);
838 SkASSERT(fGraphicStack[fGraphicStackIndex].fClip ==
839 fGraphicStack[fGraphicStackIndex - 1].fClip);
840 popGS();
841 }
842 if (m.getType() == SkMatrix::kIdentity_Mask)
843 return old;
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000844
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000845 if (fGraphicStackIndex == 0 || fGraphicStack[fGraphicStackIndex].fClip !=
846 fGraphicStack[fGraphicStackIndex - 1].fClip)
847 pushGS();
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000848
vandebo@chromium.orgddbbd802010-10-26 19:45:06 +0000849 SkScalar transform[6];
850 SkAssertResult(m.pdfTransform(transform));
851 for (size_t i = 0; i < SK_ARRAY_COUNT(transform); i++) {
852 fContent.appendScalar(transform[i]);
853 fContent.append(" ");
854 }
855 fContent.append("cm\n");
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000856 fGraphicStack[fGraphicStackIndex].fTransform = m;
857
858 return old;
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000859}