blob: ff9e4db433a8bf921ae49cf59ea6771104b03d8a [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.org094316b2011-03-04 03:15:13 +000051 SkPDFScalar::Append(
52 SkScalarDiv(SkIntToScalar(SkColorGetR(color)), colorMax), &result);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +000053 result.append(" ");
vandebo@chromium.org094316b2011-03-04 03:15:13 +000054 SkPDFScalar::Append(
55 SkScalarDiv(SkIntToScalar(SkColorGetG(color)), colorMax), &result);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +000056 result.append(" ");
vandebo@chromium.org094316b2011-03-04 03:15:13 +000057 SkPDFScalar::Append(
58 SkScalarDiv(SkIntToScalar(SkColorGetB(color)), colorMax), &result);
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.org01294102011-02-28 19:52:18 +0000317 // We want the text in glyph id encoding and a writable buffer, so we end
318 // up making a copy either way.
319 size_t numGlyphs = paint.textToGlyphs(text, len, NULL);
320 uint16_t* glyphIDs =
321 (uint16_t*)sk_malloc_flags(numGlyphs * 2,
322 SK_MALLOC_TEMP | SK_MALLOC_THROW);
323 SkAutoFree autoFreeGlyphIDs(glyphIDs);
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000324 if (paint.getTextEncoding() != SkPaint::kGlyphID_TextEncoding) {
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000325 paint.textToGlyphs(text, len, glyphIDs);
326 textPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
327 } else {
328 SkASSERT((len & 1) == 0);
vandebo@chromium.org01294102011-02-28 19:52:18 +0000329 SkASSERT(len / 2 == numGlyphs);
330 memcpy(glyphIDs, text, len);
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000331 }
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000332
333 SkScalar width;
334 SkScalar* widthPtr = NULL;
335 if (textPaint.isUnderlineText() || textPaint.isStrikeThruText())
336 widthPtr = &width;
337
338 SkDrawCacheProc glyphCacheProc = textPaint.getDrawCacheProc();
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000339 alignText(glyphCacheProc, textPaint, glyphIDs, numGlyphs, &x, &y, widthPtr);
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000340 fContent.append("BT\n");
341 setTextTransform(x, y, textPaint.getTextSkewX());
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000342 size_t consumedGlyphCount = 0;
343 while (numGlyphs > consumedGlyphCount) {
344 updateFont(textPaint, glyphIDs[consumedGlyphCount]);
345 SkPDFFont* font = fGraphicStack[fGraphicStackIndex].fFont;
vandebo@chromium.org01294102011-02-28 19:52:18 +0000346 size_t availableGlyphs =
347 font->glyphsToPDFFontEncoding(glyphIDs + consumedGlyphCount,
348 numGlyphs - consumedGlyphCount);
349 fContent.append(SkPDFString::formatString(glyphIDs + consumedGlyphCount,
350 availableGlyphs,
351 font->multiByteGlyphs()));
352 consumedGlyphCount += availableGlyphs;
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000353 fContent.append(" Tj\n");
354 }
355 fContent.append("ET\n");
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000356
357 // Draw underline and/or strikethrough if the paint has them.
358 // drawPosText() and drawTextOnPath() don't draw underline or strikethrough
359 // because the raster versions don't. Use paint instead of textPaint
360 // because we may have changed strokeWidth to do fakeBold text.
361 if (paint.isUnderlineText() || paint.isStrikeThruText()) {
362 SkScalar textSize = paint.getTextSize();
363 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
364
365 if (paint.isUnderlineText()) {
366 SkScalar top = SkScalarMulAdd(textSize, kStdUnderline_Offset, y);
367 SkRect r = SkRect::MakeXYWH(x, top - height, width, height);
368 drawRect(d, r, paint);
369 }
370 if (paint.isStrikeThruText()) {
371 SkScalar top = SkScalarMulAdd(textSize, kStdStrikeThru_Offset, y);
372 SkRect r = SkRect::MakeXYWH(x, top - height, width, height);
373 drawRect(d, r, paint);
374 }
375 }
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000376}
377
378void SkPDFDevice::drawPosText(const SkDraw&, const void* text, size_t len,
379 const SkScalar pos[], SkScalar constY,
380 int scalarsPerPos, const SkPaint& paint) {
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000381 SkASSERT(1 == scalarsPerPos || 2 == scalarsPerPos);
382 SkPaint textPaint = calculateTextPaint(paint);
383 updateGSFromPaint(textPaint, true);
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000384
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000385 // Make sure we have a glyph id encoding.
386 SkAutoFree glyphStorage;
387 uint16_t* glyphIDs;
388 size_t numGlyphs;
389 if (paint.getTextEncoding() != SkPaint::kGlyphID_TextEncoding) {
390 numGlyphs = paint.textToGlyphs(text, len, NULL);
391 glyphIDs = (uint16_t*)sk_malloc_flags(numGlyphs * 2,
392 SK_MALLOC_TEMP | SK_MALLOC_THROW);
393 glyphStorage.set(glyphIDs);
394 paint.textToGlyphs(text, len, glyphIDs);
395 textPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
396 } else {
397 SkASSERT((len & 1) == 0);
398 numGlyphs = len / 2;
399 glyphIDs = (uint16_t*)text;
400 }
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000401
402 SkDrawCacheProc glyphCacheProc = textPaint.getDrawCacheProc();
403 fContent.append("BT\n");
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000404 updateFont(textPaint, glyphIDs[0]);
405 for (size_t i = 0; i < numGlyphs; i++) {
406 SkPDFFont* font = fGraphicStack[fGraphicStackIndex].fFont;
vandebo@chromium.org01294102011-02-28 19:52:18 +0000407 uint16_t encodedValue = glyphIDs[i];
408 if (font->glyphsToPDFFontEncoding(&encodedValue, 1) != 1) {
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000409 updateFont(textPaint, glyphIDs[i]);
410 i--;
411 continue;
412 }
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000413 SkScalar x = pos[i * scalarsPerPos];
414 SkScalar y = scalarsPerPos == 1 ? constY : pos[i * scalarsPerPos + 1];
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000415 alignText(glyphCacheProc, textPaint, glyphIDs + i, 1, &x, &y, NULL);
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000416 setTextTransform(x, y, textPaint.getTextSkewX());
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000417 fContent.append(SkPDFString::formatString(&encodedValue, 1,
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000418 font->multiByteGlyphs()));
419 fContent.append(" Tj\n");
420 }
421 fContent.append("ET\n");
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000422}
423
424void SkPDFDevice::drawTextOnPath(const SkDraw&, const void* text, size_t len,
425 const SkPath& path, const SkMatrix* matrix,
426 const SkPaint& paint) {
vandebo@chromium.orga5180862010-10-26 19:48:49 +0000427 NOT_IMPLEMENTED("drawTextOnPath", true);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000428}
429
430void SkPDFDevice::drawVertices(const SkDraw&, SkCanvas::VertexMode,
431 int vertexCount, const SkPoint verts[],
432 const SkPoint texs[], const SkColor colors[],
433 SkXfermode* xmode, const uint16_t indices[],
434 int indexCount, const SkPaint& paint) {
vandebo@chromium.orga5180862010-10-26 19:48:49 +0000435 NOT_IMPLEMENTED("drawVerticies", true);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000436}
437
vandebo@chromium.orgeb6c7592010-10-26 19:54:45 +0000438void SkPDFDevice::drawDevice(const SkDraw& d, SkDevice* device, int x, int y,
439 const SkPaint& paint) {
440 if ((device->getDeviceCapabilities() & kVector_Capability) == 0) {
441 // If we somehow get a raster device, do what our parent would do.
442 SkDevice::drawDevice(d, device, x, y, paint);
443 return;
444 }
vandebo@chromium.orgeb6c7592010-10-26 19:54:45 +0000445 // Assume that a vector capable device means that it's a PDF Device.
vandebo@chromium.orgeb6c7592010-10-26 19:54:45 +0000446 SkPDFDevice* pdfDevice = static_cast<SkPDFDevice*>(device);
447
vandebo@chromium.org1aef2ed2011-02-03 21:46:10 +0000448 SkMatrix matrix;
449 matrix.setTranslate(x, y);
450 SkMatrix curTransform = setTransform(matrix);
vandebo@chromium.org48543272011-02-08 19:28:07 +0000451 updateGSFromPaint(paint, false);
vandebo@chromium.org1aef2ed2011-02-03 21:46:10 +0000452
453 SkPDFFormXObject* xobject = new SkPDFFormXObject(pdfDevice);
vandebo@chromium.orgeb6c7592010-10-26 19:54:45 +0000454 fXObjectResources.push(xobject); // Transfer reference.
455 fContent.append("/X");
456 fContent.appendS32(fXObjectResources.count() - 1);
457 fContent.append(" Do\n");
vandebo@chromium.org1aef2ed2011-02-03 21:46:10 +0000458 setTransform(curTransform);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000459}
460
461const SkRefPtr<SkPDFDict>& SkPDFDevice::getResourceDict() {
462 if (fResourceDict.get() == NULL) {
463 fResourceDict = new SkPDFDict;
464 fResourceDict->unref(); // SkRefPtr and new both took a reference.
465
466 if (fGraphicStateResources.count()) {
467 SkRefPtr<SkPDFDict> extGState = new SkPDFDict();
468 extGState->unref(); // SkRefPtr and new both took a reference.
469 for (int i = 0; i < fGraphicStateResources.count(); i++) {
470 SkString nameString("G");
471 nameString.appendS32(i);
vandebo@chromium.orgf7c15762011-02-01 22:19:44 +0000472 extGState->insert(
473 nameString.c_str(),
474 new SkPDFObjRef(fGraphicStateResources[i]))->unref();
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000475 }
476 fResourceDict->insert("ExtGState", extGState.get());
477 }
478
479 if (fXObjectResources.count()) {
480 SkRefPtr<SkPDFDict> xObjects = new SkPDFDict();
481 xObjects->unref(); // SkRefPtr and new both took a reference.
482 for (int i = 0; i < fXObjectResources.count(); i++) {
483 SkString nameString("X");
484 nameString.appendS32(i);
vandebo@chromium.orgf7c15762011-02-01 22:19:44 +0000485 xObjects->insert(
486 nameString.c_str(),
487 new SkPDFObjRef(fXObjectResources[i]))->unref();
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000488 }
489 fResourceDict->insert("XObject", xObjects.get());
490 }
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000491
492 if (fFontResources.count()) {
493 SkRefPtr<SkPDFDict> fonts = new SkPDFDict();
494 fonts->unref(); // SkRefPtr and new both took a reference.
495 for (int i = 0; i < fFontResources.count(); i++) {
496 SkString nameString("F");
497 nameString.appendS32(i);
vandebo@chromium.orgf7c15762011-02-01 22:19:44 +0000498 fonts->insert(nameString.c_str(),
499 new SkPDFObjRef(fFontResources[i]))->unref();
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000500 }
501 fResourceDict->insert("Font", fonts.get());
502 }
503
504 // For compatibility, add all proc sets (only used for output to PS
505 // devices).
506 const char procs[][7] = {"PDF", "Text", "ImageB", "ImageC", "ImageI"};
507 SkRefPtr<SkPDFArray> procSets = new SkPDFArray();
508 procSets->unref(); // SkRefPtr and new both took a reference.
509 procSets->reserve(SK_ARRAY_COUNT(procs));
vandebo@chromium.orgf7c15762011-02-01 22:19:44 +0000510 for (size_t i = 0; i < SK_ARRAY_COUNT(procs); i++)
511 procSets->append(new SkPDFName(procs[i]))->unref();
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000512 fResourceDict->insert("ProcSet", procSets.get());
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000513 }
514 return fResourceDict;
515}
516
vandebo@chromium.orga5180862010-10-26 19:48:49 +0000517void SkPDFDevice::getResources(SkTDArray<SkPDFObject*>* resourceList) const {
518 resourceList->setReserve(resourceList->count() +
519 fGraphicStateResources.count() +
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000520 fXObjectResources.count() +
521 fFontResources.count());
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000522 for (int i = 0; i < fGraphicStateResources.count(); i++) {
vandebo@chromium.orga5180862010-10-26 19:48:49 +0000523 resourceList->push(fGraphicStateResources[i]);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000524 fGraphicStateResources[i]->ref();
vandebo@chromium.orga5180862010-10-26 19:48:49 +0000525 fGraphicStateResources[i]->getResources(resourceList);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000526 }
527 for (int i = 0; i < fXObjectResources.count(); i++) {
vandebo@chromium.orga5180862010-10-26 19:48:49 +0000528 resourceList->push(fXObjectResources[i]);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000529 fXObjectResources[i]->ref();
vandebo@chromium.orga5180862010-10-26 19:48:49 +0000530 fXObjectResources[i]->getResources(resourceList);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000531 }
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000532 for (int i = 0; i < fFontResources.count(); i++) {
533 resourceList->push(fFontResources[i]);
534 fFontResources[i]->ref();
535 fFontResources[i]->getResources(resourceList);
536 }
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000537}
538
vandebo@chromium.orga5180862010-10-26 19:48:49 +0000539SkRefPtr<SkPDFArray> SkPDFDevice::getMediaBox() const {
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000540 SkRefPtr<SkPDFInt> zero = new SkPDFInt(0);
541 zero->unref(); // SkRefPtr and new both took a reference.
vandebo@chromium.orgf7c15762011-02-01 22:19:44 +0000542
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000543 SkRefPtr<SkPDFArray> mediaBox = new SkPDFArray();
544 mediaBox->unref(); // SkRefPtr and new both took a reference.
545 mediaBox->reserve(4);
546 mediaBox->append(zero.get());
547 mediaBox->append(zero.get());
vandebo@chromium.orgf7c15762011-02-01 22:19:44 +0000548 mediaBox->append(new SkPDFInt(fWidth))->unref();
549 mediaBox->append(new SkPDFInt(fHeight))->unref();
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000550 return mediaBox;
551}
552
vandebo@chromium.orgc2a9b7f2011-02-24 23:22:30 +0000553SkStream* SkPDFDevice::content() const {
554 size_t offset = fContent.size();
555 char* data = (char*)sk_malloc_throw(offset + fGraphicStackIndex * 2);
556 memcpy(data, fContent.c_str(), offset);
557 for (int i = 0; i < fGraphicStackIndex; i++) {
558 data[offset++] = 'Q';
559 data[offset++] = '\n';
560 }
561 SkMemoryStream* result = new SkMemoryStream;
562 result->setMemoryOwned(data, offset);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000563 return result;
564}
565
566// Private
567
568// TODO(vandebo) handle these cases.
vandebo@chromium.orga5180862010-10-26 19:48:49 +0000569#define PAINTCHECK(x,y) NOT_IMPLEMENTED(newPaint.x() y, false)
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000570
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000571void SkPDFDevice::updateGSFromPaint(const SkPaint& newPaint, bool forText) {
vandebo@chromium.org48543272011-02-08 19:28:07 +0000572 SkASSERT(newPaint.getPathEffect() == NULL);
573
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000574 PAINTCHECK(getMaskFilter, != NULL);
575 PAINTCHECK(getShader, != NULL);
576 PAINTCHECK(getColorFilter, != NULL);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000577
578 SkRefPtr<SkPDFGraphicState> newGraphicState =
579 SkPDFGraphicState::getGraphicStateForPaint(newPaint);
580 newGraphicState->unref(); // getGraphicState and SkRefPtr both took a ref.
581 // newGraphicState has been canonicalized so we can directly compare
582 // pointers.
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000583 if (fGraphicStack[fGraphicStackIndex].fGraphicState !=
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000584 newGraphicState.get()) {
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000585 int resourceIndex = fGraphicStateResources.find(newGraphicState.get());
586 if (resourceIndex < 0) {
587 resourceIndex = fGraphicStateResources.count();
588 fGraphicStateResources.push(newGraphicState.get());
589 newGraphicState->ref();
590 }
591 fContent.append("/G");
592 fContent.appendS32(resourceIndex);
593 fContent.append(" gs\n");
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000594 fGraphicStack[fGraphicStackIndex].fGraphicState = newGraphicState.get();
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000595 }
596
597 SkColor newColor = newPaint.getColor();
598 newColor = SkColorSetA(newColor, 0xFF);
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000599 if (fGraphicStack[fGraphicStackIndex].fColor != newColor) {
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000600 SkString colorString = toPDFColor(newColor);
601 fContent.append(colorString);
602 fContent.append("RG ");
603 fContent.append(colorString);
604 fContent.append("rg\n");
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000605 fGraphicStack[fGraphicStackIndex].fColor = newColor;
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000606 }
607
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000608 if (forText) {
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000609 if (fGraphicStack[fGraphicStackIndex].fTextScaleX !=
610 newPaint.getTextScaleX()) {
611 SkScalar scale = newPaint.getTextScaleX();
612 SkScalar pdfScale = SkScalarMul(scale, SkIntToScalar(100));
vandebo@chromium.org094316b2011-03-04 03:15:13 +0000613 SkPDFScalar::Append(pdfScale, &fContent);
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000614 fContent.append(" Tz\n");
615 fGraphicStack[fGraphicStackIndex].fTextScaleX = scale;
616 }
617
618 if (fGraphicStack[fGraphicStackIndex].fTextFill !=
619 newPaint.getStyle()) {
620 SK_COMPILE_ASSERT(SkPaint::kFill_Style == 0, enum_must_match_value);
621 SK_COMPILE_ASSERT(SkPaint::kStroke_Style == 1,
622 enum_must_match_value);
623 SK_COMPILE_ASSERT(SkPaint::kStrokeAndFill_Style == 2,
624 enum_must_match_value);
625 fContent.appendS32(newPaint.getStyle());
626 fContent.append(" Tr\n");
627 fGraphicStack[fGraphicStackIndex].fTextFill = newPaint.getStyle();
628 }
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000629 }
630}
631
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000632void SkPDFDevice::updateFont(const SkPaint& paint, uint16_t glyphID) {
633 uint32_t fontID = SkTypeface::UniqueID(paint.getTypeface());
634 if (fGraphicStack[fGraphicStackIndex].fTextSize != paint.getTextSize() ||
635 fGraphicStack[fGraphicStackIndex].fFont == NULL ||
636 fGraphicStack[fGraphicStackIndex].fFont->fontID() != fontID ||
637 !fGraphicStack[fGraphicStackIndex].fFont->hasGlyph(glyphID)) {
638 int fontIndex = getFontResourceIndex(fontID, glyphID);
639 fContent.append("/F");
640 fContent.appendS32(fontIndex);
641 fContent.append(" ");
vandebo@chromium.org094316b2011-03-04 03:15:13 +0000642 SkPDFScalar::Append(paint.getTextSize(), &fContent);
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000643 fContent.append(" Tf\n");
644 fGraphicStack[fGraphicStackIndex].fTextSize = paint.getTextSize();
645 fGraphicStack[fGraphicStackIndex].fFont = fFontResources[fontIndex];
646 }
647}
648
649
650int SkPDFDevice::getFontResourceIndex(uint32_t fontID, uint16_t glyphID) {
651 SkRefPtr<SkPDFFont> newFont = SkPDFFont::getFontResource(fontID, glyphID);
652 newFont->unref(); // getFontResource and SkRefPtr both took a ref.
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000653 int resourceIndex = fFontResources.find(newFont.get());
654 if (resourceIndex < 0) {
655 resourceIndex = fFontResources.count();
656 fFontResources.push(newFont.get());
657 newFont->ref();
658 }
659 return resourceIndex;
660}
661
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000662void SkPDFDevice::moveTo(SkScalar x, SkScalar y) {
vandebo@chromium.org094316b2011-03-04 03:15:13 +0000663 SkPDFScalar::Append(x, &fContent);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000664 fContent.append(" ");
vandebo@chromium.org094316b2011-03-04 03:15:13 +0000665 SkPDFScalar::Append(y, &fContent);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000666 fContent.append(" m\n");
667}
668
669void SkPDFDevice::appendLine(SkScalar x, SkScalar y) {
vandebo@chromium.org094316b2011-03-04 03:15:13 +0000670 SkPDFScalar::Append(x, &fContent);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000671 fContent.append(" ");
vandebo@chromium.org094316b2011-03-04 03:15:13 +0000672 SkPDFScalar::Append(y, &fContent);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000673 fContent.append(" l\n");
674}
675
676void SkPDFDevice::appendCubic(SkScalar ctl1X, SkScalar ctl1Y,
677 SkScalar ctl2X, SkScalar ctl2Y,
678 SkScalar dstX, SkScalar dstY) {
679 SkString cmd("y\n");
vandebo@chromium.org094316b2011-03-04 03:15:13 +0000680 SkPDFScalar::Append(ctl1X, &fContent);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000681 fContent.append(" ");
vandebo@chromium.org094316b2011-03-04 03:15:13 +0000682 SkPDFScalar::Append(ctl1Y, &fContent);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000683 fContent.append(" ");
684 if (ctl2X != dstX || ctl2Y != dstY) {
685 cmd.set("c\n");
vandebo@chromium.org094316b2011-03-04 03:15:13 +0000686 SkPDFScalar::Append(ctl2X, &fContent);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000687 fContent.append(" ");
vandebo@chromium.org094316b2011-03-04 03:15:13 +0000688 SkPDFScalar::Append(ctl2Y, &fContent);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000689 fContent.append(" ");
690 }
vandebo@chromium.org094316b2011-03-04 03:15:13 +0000691 SkPDFScalar::Append(dstX, &fContent);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000692 fContent.append(" ");
vandebo@chromium.org094316b2011-03-04 03:15:13 +0000693 SkPDFScalar::Append(dstY, &fContent);
vandebo@chromium.org1cfa2c42011-01-31 19:35:43 +0000694 fContent.append(" ");
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000695 fContent.append(cmd);
696}
697
698void SkPDFDevice::appendRectangle(SkScalar x, SkScalar y,
699 SkScalar w, SkScalar h) {
vandebo@chromium.org094316b2011-03-04 03:15:13 +0000700 SkPDFScalar::Append(x, &fContent);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000701 fContent.append(" ");
vandebo@chromium.org094316b2011-03-04 03:15:13 +0000702 SkPDFScalar::Append(y, &fContent);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000703 fContent.append(" ");
vandebo@chromium.org094316b2011-03-04 03:15:13 +0000704 SkPDFScalar::Append(w, &fContent);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000705 fContent.append(" ");
vandebo@chromium.org094316b2011-03-04 03:15:13 +0000706 SkPDFScalar::Append(h, &fContent);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000707 fContent.append(" re\n");
708}
709
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000710void SkPDFDevice::emitPath(const SkPath& path) {
711 SkPoint args[4];
712 SkPath::Iter iter(path, false);
713 for (SkPath::Verb verb = iter.next(args);
714 verb != SkPath::kDone_Verb;
715 verb = iter.next(args)) {
716 // args gets all the points, even the implicit first point.
717 switch (verb) {
718 case SkPath::kMove_Verb:
719 moveTo(args[0].fX, args[0].fY);
720 break;
721 case SkPath::kLine_Verb:
722 appendLine(args[1].fX, args[1].fY);
723 break;
724 case SkPath::kQuad_Verb: {
725 // Convert quad to cubic (degree elevation). http://goo.gl/vS4i
726 const SkScalar three = SkIntToScalar(3);
727 args[1].scale(SkIntToScalar(2));
728 SkScalar ctl1X = SkScalarDiv(args[0].fX + args[1].fX, three);
729 SkScalar ctl1Y = SkScalarDiv(args[0].fY + args[1].fY, three);
730 SkScalar ctl2X = SkScalarDiv(args[2].fX + args[1].fX, three);
731 SkScalar ctl2Y = SkScalarDiv(args[2].fY + args[1].fY, three);
732 appendCubic(ctl1X, ctl1Y, ctl2X, ctl2Y, args[2].fX, args[2].fY);
733 break;
734 }
735 case SkPath::kCubic_Verb:
736 appendCubic(args[1].fX, args[1].fY, args[2].fX, args[2].fY,
737 args[3].fX, args[3].fY);
738 break;
739 case SkPath::kClose_Verb:
740 closePath();
741 break;
742 case SkPath::kDone_Verb:
743 break;
744 default:
745 SkASSERT(false);
746 break;
747 }
748 }
749}
750
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000751void SkPDFDevice::closePath() {
752 fContent.append("h\n");
753}
754
vandebo@chromium.orga5180862010-10-26 19:48:49 +0000755void SkPDFDevice::paintPath(SkPaint::Style style, SkPath::FillType fill) {
756 if (style == SkPaint::kFill_Style)
757 fContent.append("f");
758 else if (style == SkPaint::kStrokeAndFill_Style)
759 fContent.append("B");
760 else if (style == SkPaint::kStroke_Style)
761 fContent.append("S");
762
763 if (style != SkPaint::kStroke_Style) {
764 // Not supported yet.
765 NOT_IMPLEMENTED(fill == SkPath::kInverseEvenOdd_FillType, false);
766 NOT_IMPLEMENTED(fill == SkPath::kInverseWinding_FillType, false);
767 if (fill == SkPath::kEvenOdd_FillType)
768 fContent.append("*");
769 }
770 fContent.append("\n");
771}
772
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000773void SkPDFDevice::strokePath() {
vandebo@chromium.orga5180862010-10-26 19:48:49 +0000774 paintPath(SkPaint::kStroke_Style, SkPath::kWinding_FillType);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000775}
776
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000777void SkPDFDevice::pushGS() {
778 SkASSERT(fGraphicStackIndex < 2);
779 fContent.append("q\n");
780 fGraphicStackIndex++;
781 fGraphicStack[fGraphicStackIndex] = fGraphicStack[fGraphicStackIndex - 1];
782}
783
784void SkPDFDevice::popGS() {
785 SkASSERT(fGraphicStackIndex > 0);
786 fContent.append("Q\n");
787 fGraphicStackIndex--;
788}
789
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000790void SkPDFDevice::setTextTransform(SkScalar x, SkScalar y, SkScalar textSkewX) {
791 // Flip the text about the x-axis to account for origin swap and include
792 // the passed parameters.
793 fContent.append("1 0 ");
vandebo@chromium.org094316b2011-03-04 03:15:13 +0000794 SkPDFScalar::Append(0 - textSkewX, &fContent);
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000795 fContent.append(" -1 ");
vandebo@chromium.org094316b2011-03-04 03:15:13 +0000796 SkPDFScalar::Append(x, &fContent);
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000797 fContent.append(" ");
vandebo@chromium.org094316b2011-03-04 03:15:13 +0000798 SkPDFScalar::Append(y, &fContent);
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000799 fContent.append(" Tm\n");
800}
801
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000802void SkPDFDevice::internalDrawBitmap(const SkMatrix& matrix,
803 const SkBitmap& bitmap,
vandebo@chromium.orgbefebb82011-01-29 01:38:50 +0000804 const SkIRect* srcRect,
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000805 const SkPaint& paint) {
vandebo@chromium.orgbefebb82011-01-29 01:38:50 +0000806 SkIRect subset = SkIRect::MakeWH(bitmap.width(), bitmap.height());
807 if (srcRect && !subset.intersect(*srcRect))
808 return;
809
vandebo@chromium.org1cfa2c42011-01-31 19:35:43 +0000810 SkPDFImage* image = SkPDFImage::CreateImage(bitmap, subset, paint);
811 if (!image)
812 return;
813
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000814 SkMatrix scaled;
815 // Adjust for origin flip.
816 scaled.setScale(1, -1);
817 scaled.postTranslate(0, 1);
818 // Scale the image up from 1x1 to WxH.
vandebo@chromium.orgbefebb82011-01-29 01:38:50 +0000819 scaled.postScale(subset.width(), subset.height());
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000820 scaled.postConcat(matrix);
821 SkMatrix curTransform = setTransform(scaled);
vandebo@chromium.org48543272011-02-08 19:28:07 +0000822 updateGSFromPaint(paint, false);
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000823
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000824 fXObjectResources.push(image); // Transfer reference.
825 fContent.append("/X");
826 fContent.appendS32(fXObjectResources.count() - 1);
827 fContent.append(" Do\n");
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000828 setTransform(curTransform);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000829}
830
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000831SkMatrix SkPDFDevice::setTransform(const SkMatrix& m) {
832 SkMatrix old = fGraphicStack[fGraphicStackIndex].fTransform;
833 if (old == m)
834 return old;
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000835
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000836 if (old.getType() != SkMatrix::kIdentity_Mask) {
837 SkASSERT(fGraphicStackIndex > 0);
838 SkASSERT(fGraphicStack[fGraphicStackIndex - 1].fTransform.getType() ==
839 SkMatrix::kIdentity_Mask);
840 SkASSERT(fGraphicStack[fGraphicStackIndex].fClip ==
841 fGraphicStack[fGraphicStackIndex - 1].fClip);
842 popGS();
843 }
844 if (m.getType() == SkMatrix::kIdentity_Mask)
845 return old;
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000846
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000847 if (fGraphicStackIndex == 0 || fGraphicStack[fGraphicStackIndex].fClip !=
848 fGraphicStack[fGraphicStackIndex - 1].fClip)
849 pushGS();
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000850
vandebo@chromium.orgddbbd802010-10-26 19:45:06 +0000851 SkScalar transform[6];
852 SkAssertResult(m.pdfTransform(transform));
853 for (size_t i = 0; i < SK_ARRAY_COUNT(transform); i++) {
vandebo@chromium.org094316b2011-03-04 03:15:13 +0000854 SkPDFScalar::Append(transform[i], &fContent);
vandebo@chromium.orgddbbd802010-10-26 19:45:06 +0000855 fContent.append(" ");
856 }
857 fContent.append("cm\n");
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000858 fGraphicStack[fGraphicStackIndex].fTransform = m;
859
860 return old;
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000861}