blob: da2e306d71298fc48f6c2a85faaaf4c14e53e3ed [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),
71 fCurrentColor(0),
72 fCurrentTextScaleX(SK_Scalar1) {
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +000073 fContent.append("q\n");
74 fCurTransform.reset();
75 fActiveTransform.reset();
76}
77
78SkPDFDevice::~SkPDFDevice() {
79 fGraphicStateResources.unrefAll();
80 fXObjectResources.unrefAll();
81}
82
83void SkPDFDevice::setMatrixClip(const SkMatrix& matrix,
84 const SkRegion& region) {
85 // TODO(vandebo) handle clipping
86 setTransform(matrix);
87 fCurTransform = matrix;
88}
89
90void SkPDFDevice::drawPaint(const SkDraw& d, const SkPaint& paint) {
91 setNoTransform();
92
93 SkPaint newPaint = paint;
94 newPaint.setStyle(SkPaint::kFill_Style);
95 updateGSFromPaint(newPaint, NULL);
96
97 SkRect all = SkRect::MakeWH(width() + 1, height() + 1);
98 drawRect(d, all, newPaint);
99 setTransform(fCurTransform);
100}
101
102void SkPDFDevice::drawPoints(const SkDraw& d, SkCanvas::PointMode mode,
103 size_t count, const SkPoint* points,
104 const SkPaint& paint) {
105 if (count == 0)
106 return;
107
108 switch (mode) {
109 case SkCanvas::kPolygon_PointMode:
110 updateGSFromPaint(paint, NULL);
111 moveTo(points[0].fX, points[0].fY);
112 for (size_t i = 1; i < count; i++)
113 appendLine(points[i].fX, points[i].fY);
114 strokePath();
115 break;
116 case SkCanvas::kLines_PointMode:
117 updateGSFromPaint(paint, NULL);
118 for (size_t i = 0; i < count/2; i++) {
119 moveTo(points[i * 2].fX, points[i * 2].fY);
120 appendLine(points[i * 2 + 1].fX, points[i * 2 + 1].fY);
121 strokePath();
122 }
123 break;
124 case SkCanvas::kPoints_PointMode:
125 if (paint.getStrokeCap() == SkPaint::kRound_Cap) {
126 updateGSFromPaint(paint, NULL);
127 for (size_t i = 0; i < count; i++) {
128 moveTo(points[i].fX, points[i].fY);
129 strokePath();
130 }
131 } else {
132 // PDF won't draw a single point with square/butt caps because
133 // the orientation is ambiguous. Draw a rectangle instead.
134 SkPaint newPaint = paint;
135 newPaint.setStyle(SkPaint::kFill_Style);
136 SkScalar strokeWidth = paint.getStrokeWidth();
137 SkScalar halfStroke = strokeWidth * SK_ScalarHalf;
138 for (size_t i = 0; i < count; i++) {
139 SkRect r = SkRect::MakeXYWH(points[i].fX, points[i].fY,
140 0, 0);
141 r.inset(-halfStroke, -halfStroke);
142 drawRect(d, r, newPaint);
143 }
144 }
145 break;
146 default:
147 SkASSERT(false);
148 }
149}
150
151void SkPDFDevice::drawRect(const SkDraw& d, const SkRect& r,
152 const SkPaint& paint) {
153 if (paint.getPathEffect()) {
vandebo@chromium.org7d71f7f2010-10-26 19:51:44 +0000154 // Create a path for the rectangle and apply the path effect to it.
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000155 SkPath path;
156 path.addRect(r);
157 paint.getFillPath(path, &path);
158
159 SkPaint no_effect_paint(paint);
160 SkSafeUnref(no_effect_paint.setPathEffect(NULL));
161 drawPath(d, path, no_effect_paint);
162 return;
163 }
164 updateGSFromPaint(paint, NULL);
165
166 // Skia has 0,0 at top left, pdf at bottom left. Do the right thing.
167 SkScalar bottom = r.fBottom < r.fTop ? r.fBottom : r.fTop;
168 appendRectangle(r.fLeft, bottom, r.width(), r.height());
vandebo@chromium.orga5180862010-10-26 19:48:49 +0000169 paintPath(paint.getStyle(), SkPath::kWinding_FillType);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000170}
171
vandebo@chromium.org7d71f7f2010-10-26 19:51:44 +0000172void SkPDFDevice::drawPath(const SkDraw& d, const SkPath& path,
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000173 const SkPaint& paint) {
vandebo@chromium.org7d71f7f2010-10-26 19:51:44 +0000174 if (paint.getPathEffect()) {
175 // Apply the path effect to path and draw it that way.
176 SkPath no_effect_path;
177 paint.getFillPath(path, &no_effect_path);
178
179 SkPaint no_effect_paint(paint);
180 SkSafeUnref(no_effect_paint.setPathEffect(NULL));
181 drawPath(d, no_effect_path, no_effect_paint);
182 return;
183 }
184 updateGSFromPaint(paint, NULL);
185
186 SkPoint args[4];
187 SkPath::Iter iter(path, false);
188 for (SkPath::Verb verb = iter.next(args);
189 verb != SkPath::kDone_Verb;
190 verb = iter.next(args)) {
191 // args gets all the points, even the implicit first point.
192 switch (verb) {
193 case SkPath::kMove_Verb:
194 moveTo(args[0].fX, args[0].fY);
195 break;
196 case SkPath::kLine_Verb:
197 appendLine(args[1].fX, args[1].fY);
198 break;
199 case SkPath::kQuad_Verb: {
200 // Convert quad to cubic (degree elevation). http://goo.gl/vS4i
201 const SkScalar three = SkIntToScalar(3);
202 args[1].scale(SkIntToScalar(2));
203 SkScalar ctl1X = SkScalarDiv(args[0].fX + args[1].fX, three);
204 SkScalar ctl1Y = SkScalarDiv(args[0].fY + args[1].fY, three);
205 SkScalar ctl2X = SkScalarDiv(args[2].fX + args[1].fX, three);
206 SkScalar ctl2Y = SkScalarDiv(args[2].fY + args[1].fY, three);
207 appendCubic(ctl1X, ctl1Y, ctl2X, ctl2Y, args[2].fX, args[2].fY);
208 break;
209 }
210 case SkPath::kCubic_Verb:
211 appendCubic(args[1].fX, args[1].fY, args[2].fX, args[2].fY,
212 args[3].fX, args[3].fY);
213 break;
214 case SkPath::kClose_Verb:
215 closePath();
216 break;
217 case SkPath::kDone_Verb:
218 break;
219 default:
220 SkASSERT(false);
221 break;
222 }
223 }
224 paintPath(paint.getStyle(), path.getFillType());
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000225}
226
227void SkPDFDevice::drawBitmap(const SkDraw&, const SkBitmap& bitmap,
228 const SkMatrix& matrix, const SkPaint& paint) {
229 SkMatrix scaled;
230 // Adjust for origin flip.
231 scaled.setScale(1, -1);
232 scaled.postTranslate(0, 1);
233 scaled.postConcat(fCurTransform);
234 // Scale the image up from 1x1 to WxH.
235 scaled.postScale(bitmap.width(), bitmap.height());
236 scaled.postConcat(matrix);
237 internalDrawBitmap(scaled, bitmap, paint);
238}
239
240void SkPDFDevice::drawSprite(const SkDraw&, const SkBitmap& bitmap,
241 int x, int y, const SkPaint& paint) {
242 SkMatrix scaled;
243 // Adjust for origin flip.
244 scaled.setScale(1, -1);
245 scaled.postTranslate(0, 1);
246 // Scale the image up from 1x1 to WxH.
247 scaled.postScale(bitmap.width(), -bitmap.height());
248 scaled.postTranslate(x, y);
249 internalDrawBitmap(scaled, bitmap, paint);
250}
251
252void SkPDFDevice::drawText(const SkDraw&, const void* text, size_t len,
253 SkScalar x, SkScalar y, const SkPaint& paint) {
vandebo@chromium.orga5180862010-10-26 19:48:49 +0000254 NOT_IMPLEMENTED("drawText", true);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000255}
256
257void SkPDFDevice::drawPosText(const SkDraw&, const void* text, size_t len,
258 const SkScalar pos[], SkScalar constY,
259 int scalarsPerPos, const SkPaint& paint) {
vandebo@chromium.orga5180862010-10-26 19:48:49 +0000260 NOT_IMPLEMENTED("drawPosText", false);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000261}
262
263void SkPDFDevice::drawTextOnPath(const SkDraw&, const void* text, size_t len,
264 const SkPath& path, const SkMatrix* matrix,
265 const SkPaint& paint) {
vandebo@chromium.orga5180862010-10-26 19:48:49 +0000266 NOT_IMPLEMENTED("drawTextOnPath", true);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000267}
268
269void SkPDFDevice::drawVertices(const SkDraw&, SkCanvas::VertexMode,
270 int vertexCount, const SkPoint verts[],
271 const SkPoint texs[], const SkColor colors[],
272 SkXfermode* xmode, const uint16_t indices[],
273 int indexCount, const SkPaint& paint) {
vandebo@chromium.orga5180862010-10-26 19:48:49 +0000274 NOT_IMPLEMENTED("drawVerticies", true);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000275}
276
vandebo@chromium.orgeb6c7592010-10-26 19:54:45 +0000277void SkPDFDevice::drawDevice(const SkDraw& d, SkDevice* device, int x, int y,
278 const SkPaint& paint) {
279 if ((device->getDeviceCapabilities() & kVector_Capability) == 0) {
280 // If we somehow get a raster device, do what our parent would do.
281 SkDevice::drawDevice(d, device, x, y, paint);
282 return;
283 }
284
285 // Assume that a vector capable device means that it's a PDF Device.
286 // TODO(vandebo) handle the paint (alpha and compositing mode).
287 SkMatrix matrix;
288 matrix.setTranslate(x, y);
289 SkPDFDevice* pdfDevice = static_cast<SkPDFDevice*>(device);
290
291 SkPDFFormXObject* xobject = new SkPDFFormXObject(pdfDevice, matrix);
292 fXObjectResources.push(xobject); // Transfer reference.
293 fContent.append("/X");
294 fContent.appendS32(fXObjectResources.count() - 1);
295 fContent.append(" Do\n");
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000296}
297
298const SkRefPtr<SkPDFDict>& SkPDFDevice::getResourceDict() {
299 if (fResourceDict.get() == NULL) {
300 fResourceDict = new SkPDFDict;
301 fResourceDict->unref(); // SkRefPtr and new both took a reference.
302
303 if (fGraphicStateResources.count()) {
304 SkRefPtr<SkPDFDict> extGState = new SkPDFDict();
305 extGState->unref(); // SkRefPtr and new both took a reference.
306 for (int i = 0; i < fGraphicStateResources.count(); i++) {
307 SkString nameString("G");
308 nameString.appendS32(i);
309 SkRefPtr<SkPDFName> name = new SkPDFName(nameString);
310 name->unref(); // SkRefPtr and new both took a reference.
311 SkRefPtr<SkPDFObjRef> gsRef =
312 new SkPDFObjRef(fGraphicStateResources[i]);
313 gsRef->unref(); // SkRefPtr and new both took a reference.
314 extGState->insert(name.get(), gsRef.get());
315 }
316 fResourceDict->insert("ExtGState", extGState.get());
317 }
318
319 if (fXObjectResources.count()) {
320 SkRefPtr<SkPDFDict> xObjects = new SkPDFDict();
321 xObjects->unref(); // SkRefPtr and new both took a reference.
322 for (int i = 0; i < fXObjectResources.count(); i++) {
323 SkString nameString("X");
324 nameString.appendS32(i);
325 SkRefPtr<SkPDFName> name = new SkPDFName(nameString);
326 name->unref(); // SkRefPtr and new both took a reference.
327 SkRefPtr<SkPDFObjRef> xObjRef =
328 new SkPDFObjRef(fXObjectResources[i]);
329 xObjRef->unref(); // SkRefPtr and new both took a reference.
330 xObjects->insert(name.get(), xObjRef.get());
331 }
332 fResourceDict->insert("XObject", xObjects.get());
333 }
334 }
335 return fResourceDict;
336}
337
vandebo@chromium.orga5180862010-10-26 19:48:49 +0000338void SkPDFDevice::getResources(SkTDArray<SkPDFObject*>* resourceList) const {
339 resourceList->setReserve(resourceList->count() +
340 fGraphicStateResources.count() +
341 fXObjectResources.count());
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000342 for (int i = 0; i < fGraphicStateResources.count(); i++) {
vandebo@chromium.orga5180862010-10-26 19:48:49 +0000343 resourceList->push(fGraphicStateResources[i]);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000344 fGraphicStateResources[i]->ref();
vandebo@chromium.orga5180862010-10-26 19:48:49 +0000345 fGraphicStateResources[i]->getResources(resourceList);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000346 }
347 for (int i = 0; i < fXObjectResources.count(); i++) {
vandebo@chromium.orga5180862010-10-26 19:48:49 +0000348 resourceList->push(fXObjectResources[i]);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000349 fXObjectResources[i]->ref();
vandebo@chromium.orga5180862010-10-26 19:48:49 +0000350 fXObjectResources[i]->getResources(resourceList);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000351 }
352}
353
vandebo@chromium.orga5180862010-10-26 19:48:49 +0000354SkRefPtr<SkPDFArray> SkPDFDevice::getMediaBox() const {
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000355 SkRefPtr<SkPDFInt> zero = new SkPDFInt(0);
356 zero->unref(); // SkRefPtr and new both took a reference.
357 SkRefPtr<SkPDFInt> width = new SkPDFInt(fWidth);
358 width->unref(); // SkRefPtr and new both took a reference.
359 SkRefPtr<SkPDFInt> height = new SkPDFInt(fHeight);
360 height->unref(); // SkRefPtr and new both took a reference.
361 SkRefPtr<SkPDFArray> mediaBox = new SkPDFArray();
362 mediaBox->unref(); // SkRefPtr and new both took a reference.
363 mediaBox->reserve(4);
364 mediaBox->append(zero.get());
365 mediaBox->append(zero.get());
366 mediaBox->append(width.get());
367 mediaBox->append(height.get());
368 return mediaBox;
369}
370
vandebo@chromium.orgddbbd802010-10-26 19:45:06 +0000371SkString SkPDFDevice::content(bool flipOrigin) const {
372 SkString result;
373 // Scale and translate to move the origin from the lower left to the
374 // upper left.
375 if (flipOrigin)
376 result.printf("1 0 0 -1 0 %d cm\n", fHeight);
377 result.append(fContent);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000378 result.append("Q");
379 return result;
380}
381
382// Private
383
384// TODO(vandebo) handle these cases.
vandebo@chromium.orga5180862010-10-26 19:48:49 +0000385#define PAINTCHECK(x,y) NOT_IMPLEMENTED(newPaint.x() y, false)
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000386
387void SkPDFDevice::updateGSFromPaint(const SkPaint& newPaint,
388 SkString* textStateUpdate) {
389 PAINTCHECK(getXfermode, != NULL);
390 PAINTCHECK(getPathEffect, != NULL);
391 PAINTCHECK(getMaskFilter, != NULL);
392 PAINTCHECK(getShader, != NULL);
393 PAINTCHECK(getColorFilter, != NULL);
394 PAINTCHECK(isFakeBoldText, == true);
395 PAINTCHECK(isUnderlineText, == true);
396 PAINTCHECK(isStrikeThruText, == true);
397 PAINTCHECK(getTextSkewX, != 0);
398
399 SkRefPtr<SkPDFGraphicState> newGraphicState =
400 SkPDFGraphicState::getGraphicStateForPaint(newPaint);
401 newGraphicState->unref(); // getGraphicState and SkRefPtr both took a ref.
402 // newGraphicState has been canonicalized so we can directly compare
403 // pointers.
404 if (fCurrentGraphicState.get() != newGraphicState.get()) {
405 int resourceIndex = fGraphicStateResources.find(newGraphicState.get());
406 if (resourceIndex < 0) {
407 resourceIndex = fGraphicStateResources.count();
408 fGraphicStateResources.push(newGraphicState.get());
409 newGraphicState->ref();
410 }
411 fContent.append("/G");
412 fContent.appendS32(resourceIndex);
413 fContent.append(" gs\n");
414 fCurrentGraphicState = newGraphicState;
415 }
416
417 SkColor newColor = newPaint.getColor();
418 newColor = SkColorSetA(newColor, 0xFF);
419 if (fCurrentColor != newColor) {
420 SkString colorString = toPDFColor(newColor);
421 fContent.append(colorString);
422 fContent.append("RG ");
423 fContent.append(colorString);
424 fContent.append("rg\n");
425 fCurrentColor = newColor;
426 }
427
428 if (textStateUpdate != NULL &&
429 fCurrentTextScaleX != newPaint.getTextScaleX()) {
430 SkScalar scale = newPaint.getTextScaleX();
431 SkScalar pdfScale = scale * 100;
432 textStateUpdate->appendScalar(pdfScale);
433 textStateUpdate->append(" Tz\n");
434 fCurrentTextScaleX = scale;
435 }
436}
437
438void SkPDFDevice::moveTo(SkScalar x, SkScalar y) {
439 fContent.appendScalar(x);
440 fContent.append(" ");
441 fContent.appendScalar(y);
442 fContent.append(" m\n");
443}
444
445void SkPDFDevice::appendLine(SkScalar x, SkScalar y) {
446 fContent.appendScalar(x);
447 fContent.append(" ");
448 fContent.appendScalar(y);
449 fContent.append(" l\n");
450}
451
452void SkPDFDevice::appendCubic(SkScalar ctl1X, SkScalar ctl1Y,
453 SkScalar ctl2X, SkScalar ctl2Y,
454 SkScalar dstX, SkScalar dstY) {
455 SkString cmd("y\n");
456 fContent.appendScalar(ctl1X);
457 fContent.append(" ");
458 fContent.appendScalar(ctl1Y);
459 fContent.append(" ");
460 if (ctl2X != dstX || ctl2Y != dstY) {
461 cmd.set("c\n");
462 fContent.appendScalar(ctl2X);
463 fContent.append(" ");
464 fContent.appendScalar(ctl2Y);
465 fContent.append(" ");
466 }
467 fContent.appendScalar(dstX);
468 fContent.append(" ");
469 fContent.appendScalar(dstY);
470 fContent.append(cmd);
471}
472
473void SkPDFDevice::appendRectangle(SkScalar x, SkScalar y,
474 SkScalar w, SkScalar h) {
475 fContent.appendScalar(x);
476 fContent.append(" ");
477 fContent.appendScalar(y);
478 fContent.append(" ");
479 fContent.appendScalar(w);
480 fContent.append(" ");
481 fContent.appendScalar(h);
482 fContent.append(" re\n");
483}
484
485void SkPDFDevice::closePath() {
486 fContent.append("h\n");
487}
488
vandebo@chromium.orga5180862010-10-26 19:48:49 +0000489void SkPDFDevice::paintPath(SkPaint::Style style, SkPath::FillType fill) {
490 if (style == SkPaint::kFill_Style)
491 fContent.append("f");
492 else if (style == SkPaint::kStrokeAndFill_Style)
493 fContent.append("B");
494 else if (style == SkPaint::kStroke_Style)
495 fContent.append("S");
496
497 if (style != SkPaint::kStroke_Style) {
498 // Not supported yet.
499 NOT_IMPLEMENTED(fill == SkPath::kInverseEvenOdd_FillType, false);
500 NOT_IMPLEMENTED(fill == SkPath::kInverseWinding_FillType, false);
501 if (fill == SkPath::kEvenOdd_FillType)
502 fContent.append("*");
503 }
504 fContent.append("\n");
505}
506
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000507void SkPDFDevice::strokePath() {
vandebo@chromium.orga5180862010-10-26 19:48:49 +0000508 paintPath(SkPaint::kStroke_Style, SkPath::kWinding_FillType);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000509}
510
511void SkPDFDevice::internalDrawBitmap(const SkMatrix& matrix,
512 const SkBitmap& bitmap,
513 const SkPaint& paint) {
514 setTransform(matrix);
515 SkPDFImage* image = new SkPDFImage(bitmap, paint);
516 fXObjectResources.push(image); // Transfer reference.
517 fContent.append("/X");
518 fContent.appendS32(fXObjectResources.count() - 1);
519 fContent.append(" Do\n");
520 setTransform(fCurTransform);
521}
522
523void SkPDFDevice::setTransform(const SkMatrix& m) {
524 setNoTransform();
525 applyTransform(m);
526}
527
528void SkPDFDevice::setNoTransform() {
529 if (fActiveTransform.getType() == SkMatrix::kIdentity_Mask)
530 return;
531 fContent.append("Q q "); // Restore the default transform and save it.
532 fCurrentGraphicState = NULL;
533 fActiveTransform.reset();
534}
535
536void SkPDFDevice::applyTempTransform(const SkMatrix& m) {
537 fContent.append("q ");
538 applyTransform(m);
539}
540
541void SkPDFDevice::removeTempTransform() {
542 fContent.append("Q\n");
543 fActiveTransform = fCurTransform;
544}
545
546void SkPDFDevice::applyTransform(const SkMatrix& m) {
547 if (m == fActiveTransform)
548 return;
vandebo@chromium.orgddbbd802010-10-26 19:45:06 +0000549 SkScalar transform[6];
550 SkAssertResult(m.pdfTransform(transform));
551 for (size_t i = 0; i < SK_ARRAY_COUNT(transform); i++) {
552 fContent.appendScalar(transform[i]);
553 fContent.append(" ");
554 }
555 fContent.append("cm\n");
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000556 fActiveTransform = m;
557}