blob: cf6a68e3d9bc901284a1e14129bcedb783ccfe5d [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,
vandebo@chromium.orgf60a0012011-02-24 23:14:04 +0000123 bool isForLayer) {
124 SkPDFDevice::OriginTransform flip = SkPDFDevice::kFlip_OriginTransform;
125 if (isForLayer) {
126 flip = SkPDFDevice::kNoFlip_OriginTransform;
127 }
128 return SkNEW_ARGS(SkPDFDevice, (width, height, flip));
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000129}
130
reed@android.comf2b98d62010-12-20 18:26:13 +0000131static inline SkBitmap makeABitmap(int width, int height) {
132 SkBitmap bitmap;
133 bitmap.setConfig(SkBitmap::kNo_Config, width, height);
134 return bitmap;
135}
136
vandebo@chromium.orgf60a0012011-02-24 23:14:04 +0000137SkPDFDevice::SkPDFDevice(int width, int height, OriginTransform flipOrigin)
reed@google.com07700442010-12-20 19:46:07 +0000138 : SkDevice(NULL, makeABitmap(width, height), false),
vandebo@chromium.org48543272011-02-08 19:28:07 +0000139 fWidth(width),
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000140 fHeight(height),
vandebo@chromium.orgf60a0012011-02-24 23:14:04 +0000141 fFlipOrigin(flipOrigin),
reed@google.com07700442010-12-20 19:46:07 +0000142 fGraphicStackIndex(0) {
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000143 fGraphicStack[0].fColor = SK_ColorBLACK;
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000144 fGraphicStack[0].fTextSize = SK_ScalarNaN; // This has no default value.
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000145 fGraphicStack[0].fTextScaleX = SK_Scalar1;
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000146 fGraphicStack[0].fTextFill = SkPaint::kFill_Style;
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000147 fGraphicStack[0].fFont = NULL;
148 fGraphicStack[0].fGraphicState = NULL;
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000149 fGraphicStack[0].fClip.setRect(0,0, width, height);
150 fGraphicStack[0].fTransform.reset();
vandebo@chromium.orgf60a0012011-02-24 23:14:04 +0000151
152 if (flipOrigin == kFlip_OriginTransform) {
153 fContent.printf("1 0 0 -1 0 %d cm\n", fHeight);
154 }
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000155}
156
157SkPDFDevice::~SkPDFDevice() {
158 fGraphicStateResources.unrefAll();
159 fXObjectResources.unrefAll();
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000160 fFontResources.unrefAll();
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000161}
162
163void SkPDFDevice::setMatrixClip(const SkMatrix& matrix,
reed@google.com46799cd2011-02-22 20:56:26 +0000164 const SkRegion& region,
165 const SkClipStack&) {
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000166 // See the comment in the header file above GraphicStackEntry.
167 if (region != fGraphicStack[fGraphicStackIndex].fClip) {
168 while (fGraphicStackIndex > 0)
169 popGS();
170 pushGS();
171
172 SkPath clipPath;
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000173 if (region.getBoundaryPath(&clipPath)) {
174 emitPath(clipPath);
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000175
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000176 SkPath::FillType clipFill = clipPath.getFillType();
177 NOT_IMPLEMENTED(clipFill == SkPath::kInverseEvenOdd_FillType,
178 false);
179 NOT_IMPLEMENTED(clipFill == SkPath::kInverseWinding_FillType,
180 false);
181 if (clipFill == SkPath::kEvenOdd_FillType)
182 fContent.append("W* n ");
183 else
184 fContent.append("W n ");
185 }
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000186
187 fGraphicStack[fGraphicStackIndex].fClip = region;
188 }
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000189 setTransform(matrix);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000190}
191
192void SkPDFDevice::drawPaint(const SkDraw& d, const SkPaint& paint) {
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000193 SkMatrix identityTransform;
194 identityTransform.reset();
195 SkMatrix curTransform = setTransform(identityTransform);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000196
197 SkPaint newPaint = paint;
198 newPaint.setStyle(SkPaint::kFill_Style);
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000199 updateGSFromPaint(newPaint, false);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000200
201 SkRect all = SkRect::MakeWH(width() + 1, height() + 1);
202 drawRect(d, all, newPaint);
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000203 setTransform(curTransform);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000204}
205
206void SkPDFDevice::drawPoints(const SkDraw& d, SkCanvas::PointMode mode,
207 size_t count, const SkPoint* points,
208 const SkPaint& paint) {
209 if (count == 0)
210 return;
211
212 switch (mode) {
213 case SkCanvas::kPolygon_PointMode:
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000214 updateGSFromPaint(paint, false);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000215 moveTo(points[0].fX, points[0].fY);
216 for (size_t i = 1; i < count; i++)
217 appendLine(points[i].fX, points[i].fY);
218 strokePath();
219 break;
220 case SkCanvas::kLines_PointMode:
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/2; i++) {
223 moveTo(points[i * 2].fX, points[i * 2].fY);
224 appendLine(points[i * 2 + 1].fX, points[i * 2 + 1].fY);
225 strokePath();
226 }
227 break;
228 case SkCanvas::kPoints_PointMode:
229 if (paint.getStrokeCap() == SkPaint::kRound_Cap) {
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000230 updateGSFromPaint(paint, false);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000231 for (size_t i = 0; i < count; i++) {
232 moveTo(points[i].fX, points[i].fY);
233 strokePath();
234 }
235 } else {
236 // PDF won't draw a single point with square/butt caps because
237 // the orientation is ambiguous. Draw a rectangle instead.
238 SkPaint newPaint = paint;
239 newPaint.setStyle(SkPaint::kFill_Style);
240 SkScalar strokeWidth = paint.getStrokeWidth();
241 SkScalar halfStroke = strokeWidth * SK_ScalarHalf;
242 for (size_t i = 0; i < count; i++) {
243 SkRect r = SkRect::MakeXYWH(points[i].fX, points[i].fY,
244 0, 0);
245 r.inset(-halfStroke, -halfStroke);
246 drawRect(d, r, newPaint);
247 }
248 }
249 break;
250 default:
251 SkASSERT(false);
252 }
253}
254
255void SkPDFDevice::drawRect(const SkDraw& d, const SkRect& r,
256 const SkPaint& paint) {
257 if (paint.getPathEffect()) {
vandebo@chromium.org7d71f7f2010-10-26 19:51:44 +0000258 // Create a path for the rectangle and apply the path effect to it.
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000259 SkPath path;
260 path.addRect(r);
261 paint.getFillPath(path, &path);
262
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000263 SkPaint noEffectPaint(paint);
264 SkSafeUnref(noEffectPaint.setPathEffect(NULL));
vandebo@chromium.org02cc5aa2011-01-25 22:06:29 +0000265 drawPath(d, path, noEffectPaint, NULL, true);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000266 return;
267 }
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000268 updateGSFromPaint(paint, false);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000269
270 // Skia has 0,0 at top left, pdf at bottom left. Do the right thing.
271 SkScalar bottom = r.fBottom < r.fTop ? r.fBottom : r.fTop;
272 appendRectangle(r.fLeft, bottom, r.width(), r.height());
vandebo@chromium.orga5180862010-10-26 19:48:49 +0000273 paintPath(paint.getStyle(), SkPath::kWinding_FillType);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000274}
275
vandebo@chromium.org7d71f7f2010-10-26 19:51:44 +0000276void SkPDFDevice::drawPath(const SkDraw& d, const SkPath& path,
vandebo@chromium.org02cc5aa2011-01-25 22:06:29 +0000277 const SkPaint& paint, const SkMatrix* prePathMatrix,
278 bool pathIsMutable) {
vandebo@chromium.orgbefebb82011-01-29 01:38:50 +0000279 NOT_IMPLEMENTED(prePathMatrix != NULL, true);
vandebo@chromium.org02cc5aa2011-01-25 22:06:29 +0000280
vandebo@chromium.org7d71f7f2010-10-26 19:51:44 +0000281 if (paint.getPathEffect()) {
282 // Apply the path effect to path and draw it that way.
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000283 SkPath noEffectPath;
284 paint.getFillPath(path, &noEffectPath);
vandebo@chromium.org7d71f7f2010-10-26 19:51:44 +0000285
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000286 SkPaint noEffectPaint(paint);
287 SkSafeUnref(noEffectPaint.setPathEffect(NULL));
vandebo@chromium.org02cc5aa2011-01-25 22:06:29 +0000288 drawPath(d, noEffectPath, noEffectPaint, NULL, true);
vandebo@chromium.org7d71f7f2010-10-26 19:51:44 +0000289 return;
290 }
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000291 updateGSFromPaint(paint, false);
vandebo@chromium.org7d71f7f2010-10-26 19:51:44 +0000292
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000293 emitPath(path);
vandebo@chromium.org7d71f7f2010-10-26 19:51:44 +0000294 paintPath(paint.getStyle(), path.getFillType());
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000295}
296
297void SkPDFDevice::drawBitmap(const SkDraw&, const SkBitmap& bitmap,
reed@android.comf2b98d62010-12-20 18:26:13 +0000298 const SkIRect* srcRect,
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000299 const SkMatrix& matrix, const SkPaint& paint) {
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000300 SkMatrix transform = matrix;
301 transform.postConcat(fGraphicStack[fGraphicStackIndex].fTransform);
vandebo@chromium.orgbefebb82011-01-29 01:38:50 +0000302 internalDrawBitmap(transform, bitmap, srcRect, paint);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000303}
304
305void SkPDFDevice::drawSprite(const SkDraw&, const SkBitmap& bitmap,
306 int x, int y, const SkPaint& paint) {
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000307 SkMatrix matrix;
308 matrix.setTranslate(x, y);
vandebo@chromium.orgbefebb82011-01-29 01:38:50 +0000309 internalDrawBitmap(matrix, bitmap, NULL, paint);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000310}
311
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000312void SkPDFDevice::drawText(const SkDraw& d, const void* text, size_t len,
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000313 SkScalar x, SkScalar y, const SkPaint& paint) {
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000314 SkPaint textPaint = calculateTextPaint(paint);
315 updateGSFromPaint(textPaint, true);
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000316
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000317 // Make sure we have a glyph id encoding.
318 SkAutoFree glyphStorage;
319 uint16_t* glyphIDs;
320 size_t numGlyphs;
321 if (paint.getTextEncoding() != SkPaint::kGlyphID_TextEncoding) {
322 numGlyphs = paint.textToGlyphs(text, len, NULL);
323 glyphIDs = (uint16_t*)sk_malloc_flags(numGlyphs * 2,
324 SK_MALLOC_TEMP | SK_MALLOC_THROW);
325 glyphStorage.set(glyphIDs);
326 paint.textToGlyphs(text, len, glyphIDs);
327 textPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
328 } else {
329 SkASSERT((len & 1) == 0);
330 numGlyphs = len / 2;
331 glyphIDs = (uint16_t*)text;
332 }
333 SkAutoFree encodedStorage(
334 sk_malloc_flags(numGlyphs * 2, SK_MALLOC_TEMP | SK_MALLOC_THROW));
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000335
336 SkScalar width;
337 SkScalar* widthPtr = NULL;
338 if (textPaint.isUnderlineText() || textPaint.isStrikeThruText())
339 widthPtr = &width;
340
341 SkDrawCacheProc glyphCacheProc = textPaint.getDrawCacheProc();
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000342 alignText(glyphCacheProc, textPaint, glyphIDs, numGlyphs, &x, &y, widthPtr);
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000343 fContent.append("BT\n");
344 setTextTransform(x, y, textPaint.getTextSkewX());
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000345 size_t consumedGlyphCount = 0;
346 while (numGlyphs > consumedGlyphCount) {
347 updateFont(textPaint, glyphIDs[consumedGlyphCount]);
348 SkPDFFont* font = fGraphicStack[fGraphicStackIndex].fFont;
349 size_t encodedLength = numGlyphs * 2;
350 consumedGlyphCount += font->glyphsToPDFFontEncoding(
351 glyphIDs + consumedGlyphCount, numGlyphs - consumedGlyphCount,
352 encodedStorage.get(), &encodedLength);
353 if (font->multiByteGlyphs())
354 encodedLength /= 2;
355 fContent.append(
356 SkPDFString::formatString((const uint16_t*)encodedStorage.get(),
357 encodedLength,
358 font->multiByteGlyphs()));
359 fContent.append(" Tj\n");
360 }
361 fContent.append("ET\n");
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000362
363 // Draw underline and/or strikethrough if the paint has them.
364 // drawPosText() and drawTextOnPath() don't draw underline or strikethrough
365 // because the raster versions don't. Use paint instead of textPaint
366 // because we may have changed strokeWidth to do fakeBold text.
367 if (paint.isUnderlineText() || paint.isStrikeThruText()) {
368 SkScalar textSize = paint.getTextSize();
369 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
370
371 if (paint.isUnderlineText()) {
372 SkScalar top = SkScalarMulAdd(textSize, kStdUnderline_Offset, y);
373 SkRect r = SkRect::MakeXYWH(x, top - height, width, height);
374 drawRect(d, r, paint);
375 }
376 if (paint.isStrikeThruText()) {
377 SkScalar top = SkScalarMulAdd(textSize, kStdStrikeThru_Offset, y);
378 SkRect r = SkRect::MakeXYWH(x, top - height, width, height);
379 drawRect(d, r, paint);
380 }
381 }
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000382}
383
384void SkPDFDevice::drawPosText(const SkDraw&, const void* text, size_t len,
385 const SkScalar pos[], SkScalar constY,
386 int scalarsPerPos, const SkPaint& paint) {
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000387 SkASSERT(1 == scalarsPerPos || 2 == scalarsPerPos);
388 SkPaint textPaint = calculateTextPaint(paint);
389 updateGSFromPaint(textPaint, true);
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000390
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000391 // Make sure we have a glyph id encoding.
392 SkAutoFree glyphStorage;
393 uint16_t* glyphIDs;
394 size_t numGlyphs;
395 if (paint.getTextEncoding() != SkPaint::kGlyphID_TextEncoding) {
396 numGlyphs = paint.textToGlyphs(text, len, NULL);
397 glyphIDs = (uint16_t*)sk_malloc_flags(numGlyphs * 2,
398 SK_MALLOC_TEMP | SK_MALLOC_THROW);
399 glyphStorage.set(glyphIDs);
400 paint.textToGlyphs(text, len, glyphIDs);
401 textPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
402 } else {
403 SkASSERT((len & 1) == 0);
404 numGlyphs = len / 2;
405 glyphIDs = (uint16_t*)text;
406 }
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000407
408 SkDrawCacheProc glyphCacheProc = textPaint.getDrawCacheProc();
409 fContent.append("BT\n");
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000410 updateFont(textPaint, glyphIDs[0]);
411 for (size_t i = 0; i < numGlyphs; i++) {
412 SkPDFFont* font = fGraphicStack[fGraphicStackIndex].fFont;
413 uint16_t encodedValue;
414 size_t encodedLength = 2;
415 if (font->glyphsToPDFFontEncoding(glyphIDs + i, 1, &encodedValue,
416 &encodedLength) == 0) {
417 updateFont(textPaint, glyphIDs[i]);
418 i--;
419 continue;
420 }
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000421 SkScalar x = pos[i * scalarsPerPos];
422 SkScalar y = scalarsPerPos == 1 ? constY : pos[i * scalarsPerPos + 1];
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000423 alignText(glyphCacheProc, textPaint, glyphIDs + i, 1, &x, &y, NULL);
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000424 setTextTransform(x, y, textPaint.getTextSkewX());
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000425 fContent.append(SkPDFString::formatString(&encodedValue, 1,
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000426 font->multiByteGlyphs()));
427 fContent.append(" Tj\n");
428 }
429 fContent.append("ET\n");
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000430}
431
432void SkPDFDevice::drawTextOnPath(const SkDraw&, const void* text, size_t len,
433 const SkPath& path, const SkMatrix* matrix,
434 const SkPaint& paint) {
vandebo@chromium.orga5180862010-10-26 19:48:49 +0000435 NOT_IMPLEMENTED("drawTextOnPath", true);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000436}
437
438void SkPDFDevice::drawVertices(const SkDraw&, SkCanvas::VertexMode,
439 int vertexCount, const SkPoint verts[],
440 const SkPoint texs[], const SkColor colors[],
441 SkXfermode* xmode, const uint16_t indices[],
442 int indexCount, const SkPaint& paint) {
vandebo@chromium.orga5180862010-10-26 19:48:49 +0000443 NOT_IMPLEMENTED("drawVerticies", true);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000444}
445
vandebo@chromium.orgeb6c7592010-10-26 19:54:45 +0000446void SkPDFDevice::drawDevice(const SkDraw& d, SkDevice* device, int x, int y,
447 const SkPaint& paint) {
448 if ((device->getDeviceCapabilities() & kVector_Capability) == 0) {
449 // If we somehow get a raster device, do what our parent would do.
450 SkDevice::drawDevice(d, device, x, y, paint);
451 return;
452 }
vandebo@chromium.orgeb6c7592010-10-26 19:54:45 +0000453 // Assume that a vector capable device means that it's a PDF Device.
vandebo@chromium.orgeb6c7592010-10-26 19:54:45 +0000454 SkPDFDevice* pdfDevice = static_cast<SkPDFDevice*>(device);
455
vandebo@chromium.org1aef2ed2011-02-03 21:46:10 +0000456 SkMatrix matrix;
457 matrix.setTranslate(x, y);
458 SkMatrix curTransform = setTransform(matrix);
vandebo@chromium.org48543272011-02-08 19:28:07 +0000459 updateGSFromPaint(paint, false);
vandebo@chromium.org1aef2ed2011-02-03 21:46:10 +0000460
461 SkPDFFormXObject* xobject = new SkPDFFormXObject(pdfDevice);
vandebo@chromium.orgeb6c7592010-10-26 19:54:45 +0000462 fXObjectResources.push(xobject); // Transfer reference.
463 fContent.append("/X");
464 fContent.appendS32(fXObjectResources.count() - 1);
465 fContent.append(" Do\n");
vandebo@chromium.org1aef2ed2011-02-03 21:46:10 +0000466 setTransform(curTransform);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000467}
468
469const SkRefPtr<SkPDFDict>& SkPDFDevice::getResourceDict() {
470 if (fResourceDict.get() == NULL) {
471 fResourceDict = new SkPDFDict;
472 fResourceDict->unref(); // SkRefPtr and new both took a reference.
473
474 if (fGraphicStateResources.count()) {
475 SkRefPtr<SkPDFDict> extGState = new SkPDFDict();
476 extGState->unref(); // SkRefPtr and new both took a reference.
477 for (int i = 0; i < fGraphicStateResources.count(); i++) {
478 SkString nameString("G");
479 nameString.appendS32(i);
vandebo@chromium.orgf7c15762011-02-01 22:19:44 +0000480 extGState->insert(
481 nameString.c_str(),
482 new SkPDFObjRef(fGraphicStateResources[i]))->unref();
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000483 }
484 fResourceDict->insert("ExtGState", extGState.get());
485 }
486
487 if (fXObjectResources.count()) {
488 SkRefPtr<SkPDFDict> xObjects = new SkPDFDict();
489 xObjects->unref(); // SkRefPtr and new both took a reference.
490 for (int i = 0; i < fXObjectResources.count(); i++) {
491 SkString nameString("X");
492 nameString.appendS32(i);
vandebo@chromium.orgf7c15762011-02-01 22:19:44 +0000493 xObjects->insert(
494 nameString.c_str(),
495 new SkPDFObjRef(fXObjectResources[i]))->unref();
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000496 }
497 fResourceDict->insert("XObject", xObjects.get());
498 }
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000499
500 if (fFontResources.count()) {
501 SkRefPtr<SkPDFDict> fonts = new SkPDFDict();
502 fonts->unref(); // SkRefPtr and new both took a reference.
503 for (int i = 0; i < fFontResources.count(); i++) {
504 SkString nameString("F");
505 nameString.appendS32(i);
vandebo@chromium.orgf7c15762011-02-01 22:19:44 +0000506 fonts->insert(nameString.c_str(),
507 new SkPDFObjRef(fFontResources[i]))->unref();
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000508 }
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));
vandebo@chromium.orgf7c15762011-02-01 22:19:44 +0000518 for (size_t i = 0; i < SK_ARRAY_COUNT(procs); i++)
519 procSets->append(new SkPDFName(procs[i]))->unref();
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000520 fResourceDict->insert("ProcSet", procSets.get());
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000521 }
522 return fResourceDict;
523}
524
vandebo@chromium.orga5180862010-10-26 19:48:49 +0000525void SkPDFDevice::getResources(SkTDArray<SkPDFObject*>* resourceList) const {
526 resourceList->setReserve(resourceList->count() +
527 fGraphicStateResources.count() +
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000528 fXObjectResources.count() +
529 fFontResources.count());
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000530 for (int i = 0; i < fGraphicStateResources.count(); i++) {
vandebo@chromium.orga5180862010-10-26 19:48:49 +0000531 resourceList->push(fGraphicStateResources[i]);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000532 fGraphicStateResources[i]->ref();
vandebo@chromium.orga5180862010-10-26 19:48:49 +0000533 fGraphicStateResources[i]->getResources(resourceList);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000534 }
535 for (int i = 0; i < fXObjectResources.count(); i++) {
vandebo@chromium.orga5180862010-10-26 19:48:49 +0000536 resourceList->push(fXObjectResources[i]);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000537 fXObjectResources[i]->ref();
vandebo@chromium.orga5180862010-10-26 19:48:49 +0000538 fXObjectResources[i]->getResources(resourceList);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000539 }
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000540 for (int i = 0; i < fFontResources.count(); i++) {
541 resourceList->push(fFontResources[i]);
542 fFontResources[i]->ref();
543 fFontResources[i]->getResources(resourceList);
544 }
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000545}
546
vandebo@chromium.orga5180862010-10-26 19:48:49 +0000547SkRefPtr<SkPDFArray> SkPDFDevice::getMediaBox() const {
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000548 SkRefPtr<SkPDFInt> zero = new SkPDFInt(0);
549 zero->unref(); // SkRefPtr and new both took a reference.
vandebo@chromium.orgf7c15762011-02-01 22:19:44 +0000550
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000551 SkRefPtr<SkPDFArray> mediaBox = new SkPDFArray();
552 mediaBox->unref(); // SkRefPtr and new both took a reference.
553 mediaBox->reserve(4);
554 mediaBox->append(zero.get());
555 mediaBox->append(zero.get());
vandebo@chromium.orgf7c15762011-02-01 22:19:44 +0000556 mediaBox->append(new SkPDFInt(fWidth))->unref();
557 mediaBox->append(new SkPDFInt(fHeight))->unref();
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000558 return mediaBox;
559}
560
vandebo@chromium.orgc2a9b7f2011-02-24 23:22:30 +0000561SkStream* SkPDFDevice::content() const {
562 size_t offset = fContent.size();
563 char* data = (char*)sk_malloc_throw(offset + fGraphicStackIndex * 2);
564 memcpy(data, fContent.c_str(), offset);
565 for (int i = 0; i < fGraphicStackIndex; i++) {
566 data[offset++] = 'Q';
567 data[offset++] = '\n';
568 }
569 SkMemoryStream* result = new SkMemoryStream;
570 result->setMemoryOwned(data, offset);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000571 return result;
572}
573
574// Private
575
576// TODO(vandebo) handle these cases.
vandebo@chromium.orga5180862010-10-26 19:48:49 +0000577#define PAINTCHECK(x,y) NOT_IMPLEMENTED(newPaint.x() y, false)
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000578
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000579void SkPDFDevice::updateGSFromPaint(const SkPaint& newPaint, bool forText) {
vandebo@chromium.org48543272011-02-08 19:28:07 +0000580 SkASSERT(newPaint.getPathEffect() == NULL);
581
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000582 PAINTCHECK(getMaskFilter, != NULL);
583 PAINTCHECK(getShader, != NULL);
584 PAINTCHECK(getColorFilter, != NULL);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000585
586 SkRefPtr<SkPDFGraphicState> newGraphicState =
587 SkPDFGraphicState::getGraphicStateForPaint(newPaint);
588 newGraphicState->unref(); // getGraphicState and SkRefPtr both took a ref.
589 // newGraphicState has been canonicalized so we can directly compare
590 // pointers.
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000591 if (fGraphicStack[fGraphicStackIndex].fGraphicState !=
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000592 newGraphicState.get()) {
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000593 int resourceIndex = fGraphicStateResources.find(newGraphicState.get());
594 if (resourceIndex < 0) {
595 resourceIndex = fGraphicStateResources.count();
596 fGraphicStateResources.push(newGraphicState.get());
597 newGraphicState->ref();
598 }
599 fContent.append("/G");
600 fContent.appendS32(resourceIndex);
601 fContent.append(" gs\n");
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000602 fGraphicStack[fGraphicStackIndex].fGraphicState = newGraphicState.get();
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000603 }
604
605 SkColor newColor = newPaint.getColor();
606 newColor = SkColorSetA(newColor, 0xFF);
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000607 if (fGraphicStack[fGraphicStackIndex].fColor != newColor) {
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000608 SkString colorString = toPDFColor(newColor);
609 fContent.append(colorString);
610 fContent.append("RG ");
611 fContent.append(colorString);
612 fContent.append("rg\n");
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000613 fGraphicStack[fGraphicStackIndex].fColor = newColor;
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000614 }
615
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000616 if (forText) {
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000617 if (fGraphicStack[fGraphicStackIndex].fTextScaleX !=
618 newPaint.getTextScaleX()) {
619 SkScalar scale = newPaint.getTextScaleX();
620 SkScalar pdfScale = SkScalarMul(scale, SkIntToScalar(100));
621 fContent.appendScalar(pdfScale);
622 fContent.append(" Tz\n");
623 fGraphicStack[fGraphicStackIndex].fTextScaleX = scale;
624 }
625
626 if (fGraphicStack[fGraphicStackIndex].fTextFill !=
627 newPaint.getStyle()) {
628 SK_COMPILE_ASSERT(SkPaint::kFill_Style == 0, enum_must_match_value);
629 SK_COMPILE_ASSERT(SkPaint::kStroke_Style == 1,
630 enum_must_match_value);
631 SK_COMPILE_ASSERT(SkPaint::kStrokeAndFill_Style == 2,
632 enum_must_match_value);
633 fContent.appendS32(newPaint.getStyle());
634 fContent.append(" Tr\n");
635 fGraphicStack[fGraphicStackIndex].fTextFill = newPaint.getStyle();
636 }
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000637 }
638}
639
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000640void SkPDFDevice::updateFont(const SkPaint& paint, uint16_t glyphID) {
641 uint32_t fontID = SkTypeface::UniqueID(paint.getTypeface());
642 if (fGraphicStack[fGraphicStackIndex].fTextSize != paint.getTextSize() ||
643 fGraphicStack[fGraphicStackIndex].fFont == NULL ||
644 fGraphicStack[fGraphicStackIndex].fFont->fontID() != fontID ||
645 !fGraphicStack[fGraphicStackIndex].fFont->hasGlyph(glyphID)) {
646 int fontIndex = getFontResourceIndex(fontID, glyphID);
647 fContent.append("/F");
648 fContent.appendS32(fontIndex);
649 fContent.append(" ");
650 fContent.appendScalar(paint.getTextSize());
651 fContent.append(" Tf\n");
652 fGraphicStack[fGraphicStackIndex].fTextSize = paint.getTextSize();
653 fGraphicStack[fGraphicStackIndex].fFont = fFontResources[fontIndex];
654 }
655}
656
657
658int SkPDFDevice::getFontResourceIndex(uint32_t fontID, uint16_t glyphID) {
659 SkRefPtr<SkPDFFont> newFont = SkPDFFont::getFontResource(fontID, glyphID);
660 newFont->unref(); // getFontResource and SkRefPtr both took a ref.
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000661 int resourceIndex = fFontResources.find(newFont.get());
662 if (resourceIndex < 0) {
663 resourceIndex = fFontResources.count();
664 fFontResources.push(newFont.get());
665 newFont->ref();
666 }
667 return resourceIndex;
668}
669
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000670void SkPDFDevice::moveTo(SkScalar x, SkScalar y) {
671 fContent.appendScalar(x);
672 fContent.append(" ");
673 fContent.appendScalar(y);
674 fContent.append(" m\n");
675}
676
677void SkPDFDevice::appendLine(SkScalar x, SkScalar y) {
678 fContent.appendScalar(x);
679 fContent.append(" ");
680 fContent.appendScalar(y);
681 fContent.append(" l\n");
682}
683
684void SkPDFDevice::appendCubic(SkScalar ctl1X, SkScalar ctl1Y,
685 SkScalar ctl2X, SkScalar ctl2Y,
686 SkScalar dstX, SkScalar dstY) {
687 SkString cmd("y\n");
688 fContent.appendScalar(ctl1X);
689 fContent.append(" ");
690 fContent.appendScalar(ctl1Y);
691 fContent.append(" ");
692 if (ctl2X != dstX || ctl2Y != dstY) {
693 cmd.set("c\n");
694 fContent.appendScalar(ctl2X);
695 fContent.append(" ");
696 fContent.appendScalar(ctl2Y);
697 fContent.append(" ");
698 }
699 fContent.appendScalar(dstX);
700 fContent.append(" ");
701 fContent.appendScalar(dstY);
vandebo@chromium.org1cfa2c42011-01-31 19:35:43 +0000702 fContent.append(" ");
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000703 fContent.append(cmd);
704}
705
706void SkPDFDevice::appendRectangle(SkScalar x, SkScalar y,
707 SkScalar w, SkScalar h) {
708 fContent.appendScalar(x);
709 fContent.append(" ");
710 fContent.appendScalar(y);
711 fContent.append(" ");
712 fContent.appendScalar(w);
713 fContent.append(" ");
714 fContent.appendScalar(h);
715 fContent.append(" re\n");
716}
717
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000718void SkPDFDevice::emitPath(const SkPath& path) {
719 SkPoint args[4];
720 SkPath::Iter iter(path, false);
721 for (SkPath::Verb verb = iter.next(args);
722 verb != SkPath::kDone_Verb;
723 verb = iter.next(args)) {
724 // args gets all the points, even the implicit first point.
725 switch (verb) {
726 case SkPath::kMove_Verb:
727 moveTo(args[0].fX, args[0].fY);
728 break;
729 case SkPath::kLine_Verb:
730 appendLine(args[1].fX, args[1].fY);
731 break;
732 case SkPath::kQuad_Verb: {
733 // Convert quad to cubic (degree elevation). http://goo.gl/vS4i
734 const SkScalar three = SkIntToScalar(3);
735 args[1].scale(SkIntToScalar(2));
736 SkScalar ctl1X = SkScalarDiv(args[0].fX + args[1].fX, three);
737 SkScalar ctl1Y = SkScalarDiv(args[0].fY + args[1].fY, three);
738 SkScalar ctl2X = SkScalarDiv(args[2].fX + args[1].fX, three);
739 SkScalar ctl2Y = SkScalarDiv(args[2].fY + args[1].fY, three);
740 appendCubic(ctl1X, ctl1Y, ctl2X, ctl2Y, args[2].fX, args[2].fY);
741 break;
742 }
743 case SkPath::kCubic_Verb:
744 appendCubic(args[1].fX, args[1].fY, args[2].fX, args[2].fY,
745 args[3].fX, args[3].fY);
746 break;
747 case SkPath::kClose_Verb:
748 closePath();
749 break;
750 case SkPath::kDone_Verb:
751 break;
752 default:
753 SkASSERT(false);
754 break;
755 }
756 }
757}
758
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000759void SkPDFDevice::closePath() {
760 fContent.append("h\n");
761}
762
vandebo@chromium.orga5180862010-10-26 19:48:49 +0000763void SkPDFDevice::paintPath(SkPaint::Style style, SkPath::FillType fill) {
764 if (style == SkPaint::kFill_Style)
765 fContent.append("f");
766 else if (style == SkPaint::kStrokeAndFill_Style)
767 fContent.append("B");
768 else if (style == SkPaint::kStroke_Style)
769 fContent.append("S");
770
771 if (style != SkPaint::kStroke_Style) {
772 // Not supported yet.
773 NOT_IMPLEMENTED(fill == SkPath::kInverseEvenOdd_FillType, false);
774 NOT_IMPLEMENTED(fill == SkPath::kInverseWinding_FillType, false);
775 if (fill == SkPath::kEvenOdd_FillType)
776 fContent.append("*");
777 }
778 fContent.append("\n");
779}
780
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000781void SkPDFDevice::strokePath() {
vandebo@chromium.orga5180862010-10-26 19:48:49 +0000782 paintPath(SkPaint::kStroke_Style, SkPath::kWinding_FillType);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000783}
784
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000785void SkPDFDevice::pushGS() {
786 SkASSERT(fGraphicStackIndex < 2);
787 fContent.append("q\n");
788 fGraphicStackIndex++;
789 fGraphicStack[fGraphicStackIndex] = fGraphicStack[fGraphicStackIndex - 1];
790}
791
792void SkPDFDevice::popGS() {
793 SkASSERT(fGraphicStackIndex > 0);
794 fContent.append("Q\n");
795 fGraphicStackIndex--;
796}
797
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000798void SkPDFDevice::setTextTransform(SkScalar x, SkScalar y, SkScalar textSkewX) {
799 // Flip the text about the x-axis to account for origin swap and include
800 // the passed parameters.
801 fContent.append("1 0 ");
802 fContent.appendScalar(0 - textSkewX);
803 fContent.append(" -1 ");
804 fContent.appendScalar(x);
805 fContent.append(" ");
806 fContent.appendScalar(y);
807 fContent.append(" Tm\n");
808}
809
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000810void SkPDFDevice::internalDrawBitmap(const SkMatrix& matrix,
811 const SkBitmap& bitmap,
vandebo@chromium.orgbefebb82011-01-29 01:38:50 +0000812 const SkIRect* srcRect,
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000813 const SkPaint& paint) {
vandebo@chromium.orgbefebb82011-01-29 01:38:50 +0000814 SkIRect subset = SkIRect::MakeWH(bitmap.width(), bitmap.height());
815 if (srcRect && !subset.intersect(*srcRect))
816 return;
817
vandebo@chromium.org1cfa2c42011-01-31 19:35:43 +0000818 SkPDFImage* image = SkPDFImage::CreateImage(bitmap, subset, paint);
819 if (!image)
820 return;
821
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000822 SkMatrix scaled;
823 // Adjust for origin flip.
824 scaled.setScale(1, -1);
825 scaled.postTranslate(0, 1);
826 // Scale the image up from 1x1 to WxH.
vandebo@chromium.orgbefebb82011-01-29 01:38:50 +0000827 scaled.postScale(subset.width(), subset.height());
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000828 scaled.postConcat(matrix);
829 SkMatrix curTransform = setTransform(scaled);
vandebo@chromium.org48543272011-02-08 19:28:07 +0000830 updateGSFromPaint(paint, false);
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000831
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000832 fXObjectResources.push(image); // Transfer reference.
833 fContent.append("/X");
834 fContent.appendS32(fXObjectResources.count() - 1);
835 fContent.append(" Do\n");
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000836 setTransform(curTransform);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000837}
838
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000839SkMatrix SkPDFDevice::setTransform(const SkMatrix& m) {
840 SkMatrix old = fGraphicStack[fGraphicStackIndex].fTransform;
841 if (old == m)
842 return old;
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000843
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000844 if (old.getType() != SkMatrix::kIdentity_Mask) {
845 SkASSERT(fGraphicStackIndex > 0);
846 SkASSERT(fGraphicStack[fGraphicStackIndex - 1].fTransform.getType() ==
847 SkMatrix::kIdentity_Mask);
848 SkASSERT(fGraphicStack[fGraphicStackIndex].fClip ==
849 fGraphicStack[fGraphicStackIndex - 1].fClip);
850 popGS();
851 }
852 if (m.getType() == SkMatrix::kIdentity_Mask)
853 return old;
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000854
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000855 if (fGraphicStackIndex == 0 || fGraphicStack[fGraphicStackIndex].fClip !=
856 fGraphicStack[fGraphicStackIndex - 1].fClip)
857 pushGS();
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000858
vandebo@chromium.orgddbbd802010-10-26 19:45:06 +0000859 SkScalar transform[6];
860 SkAssertResult(m.pdfTransform(transform));
861 for (size_t i = 0; i < SK_ARRAY_COUNT(transform); i++) {
862 fContent.appendScalar(transform[i]);
863 fContent.append(" ");
864 }
865 fContent.append("cm\n");
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000866 fGraphicStack[fGraphicStackIndex].fTransform = m;
867
868 return old;
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000869}