blob: e9625cf6a6851128661f74ab8e3ab3b5986c32ff [file] [log] [blame]
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +00001/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
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"
20#include "SkPaint.h"
vandebo@chromium.orga5180862010-10-26 19:48:49 +000021#include "SkPath.h"
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +000022#include "SkPDFImage.h"
23#include "SkPDFGraphicState.h"
vandebo@chromium.orgeb6c7592010-10-26 19:54:45 +000024#include "SkPDFFormXObject.h"
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +000025#include "SkPDFTypes.h"
26#include "SkPDFStream.h"
27#include "SkRect.h"
28#include "SkString.h"
29
vandebo@chromium.orga5180862010-10-26 19:48:49 +000030#define NOT_IMPLEMENTED(condition, assert) \
31 do { \
32 if (condition) { \
33 fprintf(stderr, "NOT_IMPLEMENTED: " #condition "\n"); \
34 SkASSERT(!assert); \
35 } \
36 } while(0)
37
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +000038// Utility functions
39
40namespace {
41
42SkString toPDFColor(SkColor color) {
43 SkASSERT(SkColorGetA(color) == 0xFF); // We handle alpha elsewhere.
44 SkScalar colorMax = SkIntToScalar(0xFF);
45 SkString result;
vandebo@chromium.orga5180862010-10-26 19:48:49 +000046 result.appendScalar(SkScalarDiv(SkIntToScalar(SkColorGetR(color)),
47 colorMax));
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +000048 result.append(" ");
vandebo@chromium.orga5180862010-10-26 19:48:49 +000049 result.appendScalar(SkScalarDiv(SkIntToScalar(SkColorGetG(color)),
50 colorMax));
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +000051 result.append(" ");
vandebo@chromium.orga5180862010-10-26 19:48:49 +000052 result.appendScalar(SkScalarDiv(SkIntToScalar(SkColorGetB(color)),
53 colorMax));
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +000054 result.append(" ");
55 return result;
56}
57
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +000058} // namespace
59
60////////////////////////////////////////////////////////////////////////////////
61
62SkDevice* SkPDFDeviceFactory::newDevice(SkBitmap::Config config,
63 int width, int height,
64 bool isOpaque, bool isForLayer) {
65 return SkNEW_ARGS(SkPDFDevice, (width, height));
66}
67
68SkPDFDevice::SkPDFDevice(int width, int height)
69 : fWidth(width),
70 fHeight(height),
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +000071 fGraphicStackIndex(0) {
72 fGraphicStack[0].fColor = SK_ColorBLACK;
73 fGraphicStack[0].fTextScaleX = SK_Scalar1;
74 fGraphicStack[0].fClip.setRect(0,0, width, height);
75 fGraphicStack[0].fTransform.reset();
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +000076}
77
78SkPDFDevice::~SkPDFDevice() {
79 fGraphicStateResources.unrefAll();
80 fXObjectResources.unrefAll();
81}
82
83void SkPDFDevice::setMatrixClip(const SkMatrix& matrix,
84 const SkRegion& region) {
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +000085 // See the comment in the header file above GraphicStackEntry.
86 if (region != fGraphicStack[fGraphicStackIndex].fClip) {
87 while (fGraphicStackIndex > 0)
88 popGS();
89 pushGS();
90
91 SkPath clipPath;
92 if (!region.getBoundaryPath(&clipPath))
93 clipPath.moveTo(SkIntToScalar(-1), SkIntToScalar(-1));
94 emitPath(clipPath);
95
96 SkPath::FillType clipFill = clipPath.getFillType();
97 NOT_IMPLEMENTED(clipFill == SkPath::kInverseEvenOdd_FillType, false);
98 NOT_IMPLEMENTED(clipFill == SkPath::kInverseWinding_FillType, false);
99 if (clipFill == SkPath::kEvenOdd_FillType)
100 fContent.append("W* n ");
101 else
102 fContent.append("W n ");
103
104 fGraphicStack[fGraphicStackIndex].fClip = region;
105 }
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000106 setTransform(matrix);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000107}
108
109void SkPDFDevice::drawPaint(const SkDraw& d, const SkPaint& paint) {
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000110 SkMatrix identityTransform;
111 identityTransform.reset();
112 SkMatrix curTransform = setTransform(identityTransform);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000113
114 SkPaint newPaint = paint;
115 newPaint.setStyle(SkPaint::kFill_Style);
116 updateGSFromPaint(newPaint, NULL);
117
118 SkRect all = SkRect::MakeWH(width() + 1, height() + 1);
119 drawRect(d, all, newPaint);
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000120 setTransform(curTransform);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000121}
122
123void SkPDFDevice::drawPoints(const SkDraw& d, SkCanvas::PointMode mode,
124 size_t count, const SkPoint* points,
125 const SkPaint& paint) {
126 if (count == 0)
127 return;
128
129 switch (mode) {
130 case SkCanvas::kPolygon_PointMode:
131 updateGSFromPaint(paint, NULL);
132 moveTo(points[0].fX, points[0].fY);
133 for (size_t i = 1; i < count; i++)
134 appendLine(points[i].fX, points[i].fY);
135 strokePath();
136 break;
137 case SkCanvas::kLines_PointMode:
138 updateGSFromPaint(paint, NULL);
139 for (size_t i = 0; i < count/2; i++) {
140 moveTo(points[i * 2].fX, points[i * 2].fY);
141 appendLine(points[i * 2 + 1].fX, points[i * 2 + 1].fY);
142 strokePath();
143 }
144 break;
145 case SkCanvas::kPoints_PointMode:
146 if (paint.getStrokeCap() == SkPaint::kRound_Cap) {
147 updateGSFromPaint(paint, NULL);
148 for (size_t i = 0; i < count; i++) {
149 moveTo(points[i].fX, points[i].fY);
150 strokePath();
151 }
152 } else {
153 // PDF won't draw a single point with square/butt caps because
154 // the orientation is ambiguous. Draw a rectangle instead.
155 SkPaint newPaint = paint;
156 newPaint.setStyle(SkPaint::kFill_Style);
157 SkScalar strokeWidth = paint.getStrokeWidth();
158 SkScalar halfStroke = strokeWidth * SK_ScalarHalf;
159 for (size_t i = 0; i < count; i++) {
160 SkRect r = SkRect::MakeXYWH(points[i].fX, points[i].fY,
161 0, 0);
162 r.inset(-halfStroke, -halfStroke);
163 drawRect(d, r, newPaint);
164 }
165 }
166 break;
167 default:
168 SkASSERT(false);
169 }
170}
171
172void SkPDFDevice::drawRect(const SkDraw& d, const SkRect& r,
173 const SkPaint& paint) {
174 if (paint.getPathEffect()) {
vandebo@chromium.org7d71f7f2010-10-26 19:51:44 +0000175 // Create a path for the rectangle and apply the path effect to it.
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000176 SkPath path;
177 path.addRect(r);
178 paint.getFillPath(path, &path);
179
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000180 SkPaint noEffectPaint(paint);
181 SkSafeUnref(noEffectPaint.setPathEffect(NULL));
182 drawPath(d, path, noEffectPaint);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000183 return;
184 }
185 updateGSFromPaint(paint, NULL);
186
187 // Skia has 0,0 at top left, pdf at bottom left. Do the right thing.
188 SkScalar bottom = r.fBottom < r.fTop ? r.fBottom : r.fTop;
189 appendRectangle(r.fLeft, bottom, r.width(), r.height());
vandebo@chromium.orga5180862010-10-26 19:48:49 +0000190 paintPath(paint.getStyle(), SkPath::kWinding_FillType);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000191}
192
vandebo@chromium.org7d71f7f2010-10-26 19:51:44 +0000193void SkPDFDevice::drawPath(const SkDraw& d, const SkPath& path,
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000194 const SkPaint& paint) {
vandebo@chromium.org7d71f7f2010-10-26 19:51:44 +0000195 if (paint.getPathEffect()) {
196 // Apply the path effect to path and draw it that way.
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000197 SkPath noEffectPath;
198 paint.getFillPath(path, &noEffectPath);
vandebo@chromium.org7d71f7f2010-10-26 19:51:44 +0000199
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000200 SkPaint noEffectPaint(paint);
201 SkSafeUnref(noEffectPaint.setPathEffect(NULL));
202 drawPath(d, noEffectPath, noEffectPaint);
vandebo@chromium.org7d71f7f2010-10-26 19:51:44 +0000203 return;
204 }
205 updateGSFromPaint(paint, NULL);
206
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000207 emitPath(path);
vandebo@chromium.org7d71f7f2010-10-26 19:51:44 +0000208 paintPath(paint.getStyle(), path.getFillType());
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000209}
210
211void SkPDFDevice::drawBitmap(const SkDraw&, const SkBitmap& bitmap,
212 const SkMatrix& matrix, const SkPaint& paint) {
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000213 SkMatrix transform = matrix;
214 transform.postConcat(fGraphicStack[fGraphicStackIndex].fTransform);
215 internalDrawBitmap(transform, bitmap, paint);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000216}
217
218void SkPDFDevice::drawSprite(const SkDraw&, const SkBitmap& bitmap,
219 int x, int y, const SkPaint& paint) {
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000220 SkMatrix matrix;
221 matrix.setTranslate(x, y);
222 internalDrawBitmap(matrix, bitmap, paint);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000223}
224
225void SkPDFDevice::drawText(const SkDraw&, const void* text, size_t len,
226 SkScalar x, SkScalar y, const SkPaint& paint) {
vandebo@chromium.orga5180862010-10-26 19:48:49 +0000227 NOT_IMPLEMENTED("drawText", true);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000228}
229
230void SkPDFDevice::drawPosText(const SkDraw&, const void* text, size_t len,
231 const SkScalar pos[], SkScalar constY,
232 int scalarsPerPos, const SkPaint& paint) {
vandebo@chromium.orga5180862010-10-26 19:48:49 +0000233 NOT_IMPLEMENTED("drawPosText", false);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000234}
235
236void SkPDFDevice::drawTextOnPath(const SkDraw&, const void* text, size_t len,
237 const SkPath& path, const SkMatrix* matrix,
238 const SkPaint& paint) {
vandebo@chromium.orga5180862010-10-26 19:48:49 +0000239 NOT_IMPLEMENTED("drawTextOnPath", true);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000240}
241
242void SkPDFDevice::drawVertices(const SkDraw&, SkCanvas::VertexMode,
243 int vertexCount, const SkPoint verts[],
244 const SkPoint texs[], const SkColor colors[],
245 SkXfermode* xmode, const uint16_t indices[],
246 int indexCount, const SkPaint& paint) {
vandebo@chromium.orga5180862010-10-26 19:48:49 +0000247 NOT_IMPLEMENTED("drawVerticies", true);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000248}
249
vandebo@chromium.orgeb6c7592010-10-26 19:54:45 +0000250void SkPDFDevice::drawDevice(const SkDraw& d, SkDevice* device, int x, int y,
251 const SkPaint& paint) {
252 if ((device->getDeviceCapabilities() & kVector_Capability) == 0) {
253 // If we somehow get a raster device, do what our parent would do.
254 SkDevice::drawDevice(d, device, x, y, paint);
255 return;
256 }
257
258 // Assume that a vector capable device means that it's a PDF Device.
259 // TODO(vandebo) handle the paint (alpha and compositing mode).
260 SkMatrix matrix;
261 matrix.setTranslate(x, y);
262 SkPDFDevice* pdfDevice = static_cast<SkPDFDevice*>(device);
263
264 SkPDFFormXObject* xobject = new SkPDFFormXObject(pdfDevice, matrix);
265 fXObjectResources.push(xobject); // Transfer reference.
266 fContent.append("/X");
267 fContent.appendS32(fXObjectResources.count() - 1);
268 fContent.append(" Do\n");
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000269}
270
271const SkRefPtr<SkPDFDict>& SkPDFDevice::getResourceDict() {
272 if (fResourceDict.get() == NULL) {
273 fResourceDict = new SkPDFDict;
274 fResourceDict->unref(); // SkRefPtr and new both took a reference.
275
276 if (fGraphicStateResources.count()) {
277 SkRefPtr<SkPDFDict> extGState = new SkPDFDict();
278 extGState->unref(); // SkRefPtr and new both took a reference.
279 for (int i = 0; i < fGraphicStateResources.count(); i++) {
280 SkString nameString("G");
281 nameString.appendS32(i);
282 SkRefPtr<SkPDFName> name = new SkPDFName(nameString);
283 name->unref(); // SkRefPtr and new both took a reference.
284 SkRefPtr<SkPDFObjRef> gsRef =
285 new SkPDFObjRef(fGraphicStateResources[i]);
286 gsRef->unref(); // SkRefPtr and new both took a reference.
287 extGState->insert(name.get(), gsRef.get());
288 }
289 fResourceDict->insert("ExtGState", extGState.get());
290 }
291
292 if (fXObjectResources.count()) {
293 SkRefPtr<SkPDFDict> xObjects = new SkPDFDict();
294 xObjects->unref(); // SkRefPtr and new both took a reference.
295 for (int i = 0; i < fXObjectResources.count(); i++) {
296 SkString nameString("X");
297 nameString.appendS32(i);
298 SkRefPtr<SkPDFName> name = new SkPDFName(nameString);
299 name->unref(); // SkRefPtr and new both took a reference.
300 SkRefPtr<SkPDFObjRef> xObjRef =
301 new SkPDFObjRef(fXObjectResources[i]);
302 xObjRef->unref(); // SkRefPtr and new both took a reference.
303 xObjects->insert(name.get(), xObjRef.get());
304 }
305 fResourceDict->insert("XObject", xObjects.get());
306 }
307 }
308 return fResourceDict;
309}
310
vandebo@chromium.orga5180862010-10-26 19:48:49 +0000311void SkPDFDevice::getResources(SkTDArray<SkPDFObject*>* resourceList) const {
312 resourceList->setReserve(resourceList->count() +
313 fGraphicStateResources.count() +
314 fXObjectResources.count());
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000315 for (int i = 0; i < fGraphicStateResources.count(); i++) {
vandebo@chromium.orga5180862010-10-26 19:48:49 +0000316 resourceList->push(fGraphicStateResources[i]);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000317 fGraphicStateResources[i]->ref();
vandebo@chromium.orga5180862010-10-26 19:48:49 +0000318 fGraphicStateResources[i]->getResources(resourceList);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000319 }
320 for (int i = 0; i < fXObjectResources.count(); i++) {
vandebo@chromium.orga5180862010-10-26 19:48:49 +0000321 resourceList->push(fXObjectResources[i]);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000322 fXObjectResources[i]->ref();
vandebo@chromium.orga5180862010-10-26 19:48:49 +0000323 fXObjectResources[i]->getResources(resourceList);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000324 }
325}
326
vandebo@chromium.orga5180862010-10-26 19:48:49 +0000327SkRefPtr<SkPDFArray> SkPDFDevice::getMediaBox() const {
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000328 SkRefPtr<SkPDFInt> zero = new SkPDFInt(0);
329 zero->unref(); // SkRefPtr and new both took a reference.
330 SkRefPtr<SkPDFInt> width = new SkPDFInt(fWidth);
331 width->unref(); // SkRefPtr and new both took a reference.
332 SkRefPtr<SkPDFInt> height = new SkPDFInt(fHeight);
333 height->unref(); // SkRefPtr and new both took a reference.
334 SkRefPtr<SkPDFArray> mediaBox = new SkPDFArray();
335 mediaBox->unref(); // SkRefPtr and new both took a reference.
336 mediaBox->reserve(4);
337 mediaBox->append(zero.get());
338 mediaBox->append(zero.get());
339 mediaBox->append(width.get());
340 mediaBox->append(height.get());
341 return mediaBox;
342}
343
vandebo@chromium.orgddbbd802010-10-26 19:45:06 +0000344SkString SkPDFDevice::content(bool flipOrigin) const {
345 SkString result;
346 // Scale and translate to move the origin from the lower left to the
347 // upper left.
348 if (flipOrigin)
349 result.printf("1 0 0 -1 0 %d cm\n", fHeight);
350 result.append(fContent);
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000351 for (int i = 0; i < fGraphicStackIndex; i++)
352 result.append("Q\n");
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000353 return result;
354}
355
356// Private
357
358// TODO(vandebo) handle these cases.
vandebo@chromium.orga5180862010-10-26 19:48:49 +0000359#define PAINTCHECK(x,y) NOT_IMPLEMENTED(newPaint.x() y, false)
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000360
361void SkPDFDevice::updateGSFromPaint(const SkPaint& newPaint,
362 SkString* textStateUpdate) {
363 PAINTCHECK(getXfermode, != NULL);
364 PAINTCHECK(getPathEffect, != NULL);
365 PAINTCHECK(getMaskFilter, != NULL);
366 PAINTCHECK(getShader, != NULL);
367 PAINTCHECK(getColorFilter, != NULL);
368 PAINTCHECK(isFakeBoldText, == true);
369 PAINTCHECK(isUnderlineText, == true);
370 PAINTCHECK(isStrikeThruText, == true);
371 PAINTCHECK(getTextSkewX, != 0);
372
373 SkRefPtr<SkPDFGraphicState> newGraphicState =
374 SkPDFGraphicState::getGraphicStateForPaint(newPaint);
375 newGraphicState->unref(); // getGraphicState and SkRefPtr both took a ref.
376 // newGraphicState has been canonicalized so we can directly compare
377 // pointers.
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000378 if (fGraphicStack[fGraphicStackIndex].fGraphicState.get() !=
379 newGraphicState.get()) {
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000380 int resourceIndex = fGraphicStateResources.find(newGraphicState.get());
381 if (resourceIndex < 0) {
382 resourceIndex = fGraphicStateResources.count();
383 fGraphicStateResources.push(newGraphicState.get());
384 newGraphicState->ref();
385 }
386 fContent.append("/G");
387 fContent.appendS32(resourceIndex);
388 fContent.append(" gs\n");
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000389 fGraphicStack[fGraphicStackIndex].fGraphicState = newGraphicState;
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000390 }
391
392 SkColor newColor = newPaint.getColor();
393 newColor = SkColorSetA(newColor, 0xFF);
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000394 if (fGraphicStack[fGraphicStackIndex].fColor != newColor) {
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000395 SkString colorString = toPDFColor(newColor);
396 fContent.append(colorString);
397 fContent.append("RG ");
398 fContent.append(colorString);
399 fContent.append("rg\n");
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000400 fGraphicStack[fGraphicStackIndex].fColor = newColor;
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000401 }
402
403 if (textStateUpdate != NULL &&
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000404 fGraphicStack[fGraphicStackIndex].fTextScaleX !=
405 newPaint.getTextScaleX()) {
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000406 SkScalar scale = newPaint.getTextScaleX();
407 SkScalar pdfScale = scale * 100;
408 textStateUpdate->appendScalar(pdfScale);
409 textStateUpdate->append(" Tz\n");
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000410 fGraphicStack[fGraphicStackIndex].fTextScaleX = scale;
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000411 }
412}
413
414void SkPDFDevice::moveTo(SkScalar x, SkScalar y) {
415 fContent.appendScalar(x);
416 fContent.append(" ");
417 fContent.appendScalar(y);
418 fContent.append(" m\n");
419}
420
421void SkPDFDevice::appendLine(SkScalar x, SkScalar y) {
422 fContent.appendScalar(x);
423 fContent.append(" ");
424 fContent.appendScalar(y);
425 fContent.append(" l\n");
426}
427
428void SkPDFDevice::appendCubic(SkScalar ctl1X, SkScalar ctl1Y,
429 SkScalar ctl2X, SkScalar ctl2Y,
430 SkScalar dstX, SkScalar dstY) {
431 SkString cmd("y\n");
432 fContent.appendScalar(ctl1X);
433 fContent.append(" ");
434 fContent.appendScalar(ctl1Y);
435 fContent.append(" ");
436 if (ctl2X != dstX || ctl2Y != dstY) {
437 cmd.set("c\n");
438 fContent.appendScalar(ctl2X);
439 fContent.append(" ");
440 fContent.appendScalar(ctl2Y);
441 fContent.append(" ");
442 }
443 fContent.appendScalar(dstX);
444 fContent.append(" ");
445 fContent.appendScalar(dstY);
446 fContent.append(cmd);
447}
448
449void SkPDFDevice::appendRectangle(SkScalar x, SkScalar y,
450 SkScalar w, SkScalar h) {
451 fContent.appendScalar(x);
452 fContent.append(" ");
453 fContent.appendScalar(y);
454 fContent.append(" ");
455 fContent.appendScalar(w);
456 fContent.append(" ");
457 fContent.appendScalar(h);
458 fContent.append(" re\n");
459}
460
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000461void SkPDFDevice::emitPath(const SkPath& path) {
462 SkPoint args[4];
463 SkPath::Iter iter(path, false);
464 for (SkPath::Verb verb = iter.next(args);
465 verb != SkPath::kDone_Verb;
466 verb = iter.next(args)) {
467 // args gets all the points, even the implicit first point.
468 switch (verb) {
469 case SkPath::kMove_Verb:
470 moveTo(args[0].fX, args[0].fY);
471 break;
472 case SkPath::kLine_Verb:
473 appendLine(args[1].fX, args[1].fY);
474 break;
475 case SkPath::kQuad_Verb: {
476 // Convert quad to cubic (degree elevation). http://goo.gl/vS4i
477 const SkScalar three = SkIntToScalar(3);
478 args[1].scale(SkIntToScalar(2));
479 SkScalar ctl1X = SkScalarDiv(args[0].fX + args[1].fX, three);
480 SkScalar ctl1Y = SkScalarDiv(args[0].fY + args[1].fY, three);
481 SkScalar ctl2X = SkScalarDiv(args[2].fX + args[1].fX, three);
482 SkScalar ctl2Y = SkScalarDiv(args[2].fY + args[1].fY, three);
483 appendCubic(ctl1X, ctl1Y, ctl2X, ctl2Y, args[2].fX, args[2].fY);
484 break;
485 }
486 case SkPath::kCubic_Verb:
487 appendCubic(args[1].fX, args[1].fY, args[2].fX, args[2].fY,
488 args[3].fX, args[3].fY);
489 break;
490 case SkPath::kClose_Verb:
491 closePath();
492 break;
493 case SkPath::kDone_Verb:
494 break;
495 default:
496 SkASSERT(false);
497 break;
498 }
499 }
500}
501
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000502void SkPDFDevice::closePath() {
503 fContent.append("h\n");
504}
505
vandebo@chromium.orga5180862010-10-26 19:48:49 +0000506void SkPDFDevice::paintPath(SkPaint::Style style, SkPath::FillType fill) {
507 if (style == SkPaint::kFill_Style)
508 fContent.append("f");
509 else if (style == SkPaint::kStrokeAndFill_Style)
510 fContent.append("B");
511 else if (style == SkPaint::kStroke_Style)
512 fContent.append("S");
513
514 if (style != SkPaint::kStroke_Style) {
515 // Not supported yet.
516 NOT_IMPLEMENTED(fill == SkPath::kInverseEvenOdd_FillType, false);
517 NOT_IMPLEMENTED(fill == SkPath::kInverseWinding_FillType, false);
518 if (fill == SkPath::kEvenOdd_FillType)
519 fContent.append("*");
520 }
521 fContent.append("\n");
522}
523
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000524void SkPDFDevice::strokePath() {
vandebo@chromium.orga5180862010-10-26 19:48:49 +0000525 paintPath(SkPaint::kStroke_Style, SkPath::kWinding_FillType);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000526}
527
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000528void SkPDFDevice::pushGS() {
529 SkASSERT(fGraphicStackIndex < 2);
530 fContent.append("q\n");
531 fGraphicStackIndex++;
532 fGraphicStack[fGraphicStackIndex] = fGraphicStack[fGraphicStackIndex - 1];
533}
534
535void SkPDFDevice::popGS() {
536 SkASSERT(fGraphicStackIndex > 0);
537 fContent.append("Q\n");
538 fGraphicStackIndex--;
539}
540
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000541void SkPDFDevice::internalDrawBitmap(const SkMatrix& matrix,
542 const SkBitmap& bitmap,
543 const SkPaint& paint) {
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000544 SkMatrix scaled;
545 // Adjust for origin flip.
546 scaled.setScale(1, -1);
547 scaled.postTranslate(0, 1);
548 // Scale the image up from 1x1 to WxH.
549 scaled.postScale(bitmap.width(), bitmap.height());
550 scaled.postConcat(matrix);
551 SkMatrix curTransform = setTransform(scaled);
552
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000553 SkPDFImage* image = new SkPDFImage(bitmap, paint);
554 fXObjectResources.push(image); // Transfer reference.
555 fContent.append("/X");
556 fContent.appendS32(fXObjectResources.count() - 1);
557 fContent.append(" Do\n");
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000558 setTransform(curTransform);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000559}
560
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000561SkMatrix SkPDFDevice::setTransform(const SkMatrix& m) {
562 SkMatrix old = fGraphicStack[fGraphicStackIndex].fTransform;
563 if (old == m)
564 return old;
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000565
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000566 if (old.getType() != SkMatrix::kIdentity_Mask) {
567 SkASSERT(fGraphicStackIndex > 0);
568 SkASSERT(fGraphicStack[fGraphicStackIndex - 1].fTransform.getType() ==
569 SkMatrix::kIdentity_Mask);
570 SkASSERT(fGraphicStack[fGraphicStackIndex].fClip ==
571 fGraphicStack[fGraphicStackIndex - 1].fClip);
572 popGS();
573 }
574 if (m.getType() == SkMatrix::kIdentity_Mask)
575 return old;
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000576
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000577 if (fGraphicStackIndex == 0 || fGraphicStack[fGraphicStackIndex].fClip !=
578 fGraphicStack[fGraphicStackIndex - 1].fClip)
579 pushGS();
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000580
vandebo@chromium.orgddbbd802010-10-26 19:45:06 +0000581 SkScalar transform[6];
582 SkAssertResult(m.pdfTransform(transform));
583 for (size_t i = 0; i < SK_ARRAY_COUNT(transform); i++) {
584 fContent.appendScalar(transform[i]);
585 fContent.append(" ");
586 }
587 fContent.append("cm\n");
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000588 fGraphicStack[fGraphicStackIndex].fTransform = m;
589
590 return old;
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000591}