blob: 79f0967bfc76882d91d15324b165bc9c734de194 [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"
21#include "SkPDFImage.h"
22#include "SkPDFGraphicState.h"
23#include "SkPDFTypes.h"
24#include "SkPDFStream.h"
25#include "SkRect.h"
26#include "SkString.h"
27
28// Utility functions
29
30namespace {
31
32SkString toPDFColor(SkColor color) {
33 SkASSERT(SkColorGetA(color) == 0xFF); // We handle alpha elsewhere.
34 SkScalar colorMax = SkIntToScalar(0xFF);
35 SkString result;
36 result.appendScalar(SkIntToScalar(SkColorGetR(color))/colorMax);
37 result.append(" ");
38 result.appendScalar(SkIntToScalar(SkColorGetG(color))/colorMax);
39 result.append(" ");
40 result.appendScalar(SkIntToScalar(SkColorGetB(color))/colorMax);
41 result.append(" ");
42 return result;
43}
44
45SkString StyleAndFillToPaintOperator(SkPaint::Style style,
46 SkPath::FillType fillType) {
47 SkString result;
48 if (style == SkPaint::kFill_Style)
49 result.append("f");
50 else if (style == SkPaint::kStrokeAndFill_Style)
51 result.append("B");
52 else if (style == SkPaint::kStroke_Style)
53 return SkString("S\n");
54
55 // Not supported yet.
56 SkASSERT(fillType != SkPath::kInverseEvenOdd_FillType);
57 SkASSERT(fillType != SkPath::kInverseWinding_FillType);
58 if (fillType == SkPath::kEvenOdd_FillType)
59 result.append("*");
60 result.append("\n");
61 return result;
62}
63
64} // namespace
65
66////////////////////////////////////////////////////////////////////////////////
67
68SkDevice* SkPDFDeviceFactory::newDevice(SkBitmap::Config config,
69 int width, int height,
70 bool isOpaque, bool isForLayer) {
71 return SkNEW_ARGS(SkPDFDevice, (width, height));
72}
73
74SkPDFDevice::SkPDFDevice(int width, int height)
75 : fWidth(width),
76 fHeight(height),
77 fCurrentColor(0),
78 fCurrentTextScaleX(SK_Scalar1) {
79 // Scale and translate to move the origin from the lower left to the upper
80 // left.
81 fCurTransform.setTranslate(0, height);
82 fCurTransform.preScale(1, -1);
83 fActiveTransform.reset();
84 applyTransform(fCurTransform);
85
86 fContent.append("q\n");
87 fCurTransform.reset();
88 fActiveTransform.reset();
89}
90
91SkPDFDevice::~SkPDFDevice() {
92 fGraphicStateResources.unrefAll();
93 fXObjectResources.unrefAll();
94}
95
96void SkPDFDevice::setMatrixClip(const SkMatrix& matrix,
97 const SkRegion& region) {
98 // TODO(vandebo) handle clipping
99 setTransform(matrix);
100 fCurTransform = matrix;
101}
102
103void SkPDFDevice::drawPaint(const SkDraw& d, const SkPaint& paint) {
104 setNoTransform();
105
106 SkPaint newPaint = paint;
107 newPaint.setStyle(SkPaint::kFill_Style);
108 updateGSFromPaint(newPaint, NULL);
109
110 SkRect all = SkRect::MakeWH(width() + 1, height() + 1);
111 drawRect(d, all, newPaint);
112 setTransform(fCurTransform);
113}
114
115void SkPDFDevice::drawPoints(const SkDraw& d, SkCanvas::PointMode mode,
116 size_t count, const SkPoint* points,
117 const SkPaint& paint) {
118 if (count == 0)
119 return;
120
121 switch (mode) {
122 case SkCanvas::kPolygon_PointMode:
123 updateGSFromPaint(paint, NULL);
124 moveTo(points[0].fX, points[0].fY);
125 for (size_t i = 1; i < count; i++)
126 appendLine(points[i].fX, points[i].fY);
127 strokePath();
128 break;
129 case SkCanvas::kLines_PointMode:
130 updateGSFromPaint(paint, NULL);
131 for (size_t i = 0; i < count/2; i++) {
132 moveTo(points[i * 2].fX, points[i * 2].fY);
133 appendLine(points[i * 2 + 1].fX, points[i * 2 + 1].fY);
134 strokePath();
135 }
136 break;
137 case SkCanvas::kPoints_PointMode:
138 if (paint.getStrokeCap() == SkPaint::kRound_Cap) {
139 updateGSFromPaint(paint, NULL);
140 for (size_t i = 0; i < count; i++) {
141 moveTo(points[i].fX, points[i].fY);
142 strokePath();
143 }
144 } else {
145 // PDF won't draw a single point with square/butt caps because
146 // the orientation is ambiguous. Draw a rectangle instead.
147 SkPaint newPaint = paint;
148 newPaint.setStyle(SkPaint::kFill_Style);
149 SkScalar strokeWidth = paint.getStrokeWidth();
150 SkScalar halfStroke = strokeWidth * SK_ScalarHalf;
151 for (size_t i = 0; i < count; i++) {
152 SkRect r = SkRect::MakeXYWH(points[i].fX, points[i].fY,
153 0, 0);
154 r.inset(-halfStroke, -halfStroke);
155 drawRect(d, r, newPaint);
156 }
157 }
158 break;
159 default:
160 SkASSERT(false);
161 }
162}
163
164void SkPDFDevice::drawRect(const SkDraw& d, const SkRect& r,
165 const SkPaint& paint) {
166 if (paint.getPathEffect()) {
167 // Draw a path instead.
168 SkPath path;
169 path.addRect(r);
170 paint.getFillPath(path, &path);
171
172 SkPaint no_effect_paint(paint);
173 SkSafeUnref(no_effect_paint.setPathEffect(NULL));
174 drawPath(d, path, no_effect_paint);
175 return;
176 }
177 updateGSFromPaint(paint, NULL);
178
179 // Skia has 0,0 at top left, pdf at bottom left. Do the right thing.
180 SkScalar bottom = r.fBottom < r.fTop ? r.fBottom : r.fTop;
181 appendRectangle(r.fLeft, bottom, r.width(), r.height());
182 fContent.append(StyleAndFillToPaintOperator(paint.getStyle(),
183 SkPath::kWinding_FillType));
184}
185
186void SkPDFDevice::drawPath(const SkDraw&, const SkPath& path,
187 const SkPaint& paint) {
188 SkASSERT(false);
189}
190
191void SkPDFDevice::drawBitmap(const SkDraw&, const SkBitmap& bitmap,
192 const SkMatrix& matrix, const SkPaint& paint) {
193 SkMatrix scaled;
194 // Adjust for origin flip.
195 scaled.setScale(1, -1);
196 scaled.postTranslate(0, 1);
197 scaled.postConcat(fCurTransform);
198 // Scale the image up from 1x1 to WxH.
199 scaled.postScale(bitmap.width(), bitmap.height());
200 scaled.postConcat(matrix);
201 internalDrawBitmap(scaled, bitmap, paint);
202}
203
204void SkPDFDevice::drawSprite(const SkDraw&, const SkBitmap& bitmap,
205 int x, int y, const SkPaint& paint) {
206 SkMatrix scaled;
207 // Adjust for origin flip.
208 scaled.setScale(1, -1);
209 scaled.postTranslate(0, 1);
210 // Scale the image up from 1x1 to WxH.
211 scaled.postScale(bitmap.width(), -bitmap.height());
212 scaled.postTranslate(x, y);
213 internalDrawBitmap(scaled, bitmap, paint);
214}
215
216void SkPDFDevice::drawText(const SkDraw&, const void* text, size_t len,
217 SkScalar x, SkScalar y, const SkPaint& paint) {
218 SkASSERT(false);
219}
220
221void SkPDFDevice::drawPosText(const SkDraw&, const void* text, size_t len,
222 const SkScalar pos[], SkScalar constY,
223 int scalarsPerPos, const SkPaint& paint) {
224 SkASSERT(false);
225}
226
227void SkPDFDevice::drawTextOnPath(const SkDraw&, const void* text, size_t len,
228 const SkPath& path, const SkMatrix* matrix,
229 const SkPaint& paint) {
230 SkASSERT(false);
231}
232
233void SkPDFDevice::drawVertices(const SkDraw&, SkCanvas::VertexMode,
234 int vertexCount, const SkPoint verts[],
235 const SkPoint texs[], const SkColor colors[],
236 SkXfermode* xmode, const uint16_t indices[],
237 int indexCount, const SkPaint& paint) {
238 SkASSERT(false);
239}
240
241void SkPDFDevice::drawDevice(const SkDraw&, SkDevice*, int x, int y,
242 const SkPaint&) {
243 SkASSERT(false);
244}
245
246const SkRefPtr<SkPDFDict>& SkPDFDevice::getResourceDict() {
247 if (fResourceDict.get() == NULL) {
248 fResourceDict = new SkPDFDict;
249 fResourceDict->unref(); // SkRefPtr and new both took a reference.
250
251 if (fGraphicStateResources.count()) {
252 SkRefPtr<SkPDFDict> extGState = new SkPDFDict();
253 extGState->unref(); // SkRefPtr and new both took a reference.
254 for (int i = 0; i < fGraphicStateResources.count(); i++) {
255 SkString nameString("G");
256 nameString.appendS32(i);
257 SkRefPtr<SkPDFName> name = new SkPDFName(nameString);
258 name->unref(); // SkRefPtr and new both took a reference.
259 SkRefPtr<SkPDFObjRef> gsRef =
260 new SkPDFObjRef(fGraphicStateResources[i]);
261 gsRef->unref(); // SkRefPtr and new both took a reference.
262 extGState->insert(name.get(), gsRef.get());
263 }
264 fResourceDict->insert("ExtGState", extGState.get());
265 }
266
267 if (fXObjectResources.count()) {
268 SkRefPtr<SkPDFDict> xObjects = new SkPDFDict();
269 xObjects->unref(); // SkRefPtr and new both took a reference.
270 for (int i = 0; i < fXObjectResources.count(); i++) {
271 SkString nameString("X");
272 nameString.appendS32(i);
273 SkRefPtr<SkPDFName> name = new SkPDFName(nameString);
274 name->unref(); // SkRefPtr and new both took a reference.
275 SkRefPtr<SkPDFObjRef> xObjRef =
276 new SkPDFObjRef(fXObjectResources[i]);
277 xObjRef->unref(); // SkRefPtr and new both took a reference.
278 xObjects->insert(name.get(), xObjRef.get());
279 }
280 fResourceDict->insert("XObject", xObjects.get());
281 }
282 }
283 return fResourceDict;
284}
285
286void SkPDFDevice::getResouces(SkTDArray<SkPDFObject*>* resouceList) {
287 resouceList->setReserve(resouceList->count() +
288 fGraphicStateResources.count() +
289 fXObjectResources.count());
290 for (int i = 0; i < fGraphicStateResources.count(); i++) {
291 resouceList->push(fGraphicStateResources[i]);
292 fGraphicStateResources[i]->ref();
293 }
294 for (int i = 0; i < fXObjectResources.count(); i++) {
295 resouceList->push(fXObjectResources[i]);
296 fXObjectResources[i]->ref();
297 }
298}
299
300SkRefPtr<SkPDFArray> SkPDFDevice::getMediaBox() {
301 SkRefPtr<SkPDFInt> zero = new SkPDFInt(0);
302 zero->unref(); // SkRefPtr and new both took a reference.
303 SkRefPtr<SkPDFInt> width = new SkPDFInt(fWidth);
304 width->unref(); // SkRefPtr and new both took a reference.
305 SkRefPtr<SkPDFInt> height = new SkPDFInt(fHeight);
306 height->unref(); // SkRefPtr and new both took a reference.
307 SkRefPtr<SkPDFArray> mediaBox = new SkPDFArray();
308 mediaBox->unref(); // SkRefPtr and new both took a reference.
309 mediaBox->reserve(4);
310 mediaBox->append(zero.get());
311 mediaBox->append(zero.get());
312 mediaBox->append(width.get());
313 mediaBox->append(height.get());
314 return mediaBox;
315}
316
317SkString SkPDFDevice::content() {
318 SkString result = fContent;
319 result.append("Q");
320 return result;
321}
322
323// Private
324
325// TODO(vandebo) handle these cases.
326#define PAINTCHECK(x,y) do { \
327 if(newPaint.x() y) { \
328 printf("!!" #x #y "\n"); \
329 SkASSERT(false); \
330 } \
331 } while(0)
332
333void SkPDFDevice::updateGSFromPaint(const SkPaint& newPaint,
334 SkString* textStateUpdate) {
335 PAINTCHECK(getXfermode, != NULL);
336 PAINTCHECK(getPathEffect, != NULL);
337 PAINTCHECK(getMaskFilter, != NULL);
338 PAINTCHECK(getShader, != NULL);
339 PAINTCHECK(getColorFilter, != NULL);
340 PAINTCHECK(isFakeBoldText, == true);
341 PAINTCHECK(isUnderlineText, == true);
342 PAINTCHECK(isStrikeThruText, == true);
343 PAINTCHECK(getTextSkewX, != 0);
344
345 SkRefPtr<SkPDFGraphicState> newGraphicState =
346 SkPDFGraphicState::getGraphicStateForPaint(newPaint);
347 newGraphicState->unref(); // getGraphicState and SkRefPtr both took a ref.
348 // newGraphicState has been canonicalized so we can directly compare
349 // pointers.
350 if (fCurrentGraphicState.get() != newGraphicState.get()) {
351 int resourceIndex = fGraphicStateResources.find(newGraphicState.get());
352 if (resourceIndex < 0) {
353 resourceIndex = fGraphicStateResources.count();
354 fGraphicStateResources.push(newGraphicState.get());
355 newGraphicState->ref();
356 }
357 fContent.append("/G");
358 fContent.appendS32(resourceIndex);
359 fContent.append(" gs\n");
360 fCurrentGraphicState = newGraphicState;
361 }
362
363 SkColor newColor = newPaint.getColor();
364 newColor = SkColorSetA(newColor, 0xFF);
365 if (fCurrentColor != newColor) {
366 SkString colorString = toPDFColor(newColor);
367 fContent.append(colorString);
368 fContent.append("RG ");
369 fContent.append(colorString);
370 fContent.append("rg\n");
371 fCurrentColor = newColor;
372 }
373
374 if (textStateUpdate != NULL &&
375 fCurrentTextScaleX != newPaint.getTextScaleX()) {
376 SkScalar scale = newPaint.getTextScaleX();
377 SkScalar pdfScale = scale * 100;
378 textStateUpdate->appendScalar(pdfScale);
379 textStateUpdate->append(" Tz\n");
380 fCurrentTextScaleX = scale;
381 }
382}
383
384void SkPDFDevice::moveTo(SkScalar x, SkScalar y) {
385 fContent.appendScalar(x);
386 fContent.append(" ");
387 fContent.appendScalar(y);
388 fContent.append(" m\n");
389}
390
391void SkPDFDevice::appendLine(SkScalar x, SkScalar y) {
392 fContent.appendScalar(x);
393 fContent.append(" ");
394 fContent.appendScalar(y);
395 fContent.append(" l\n");
396}
397
398void SkPDFDevice::appendCubic(SkScalar ctl1X, SkScalar ctl1Y,
399 SkScalar ctl2X, SkScalar ctl2Y,
400 SkScalar dstX, SkScalar dstY) {
401 SkString cmd("y\n");
402 fContent.appendScalar(ctl1X);
403 fContent.append(" ");
404 fContent.appendScalar(ctl1Y);
405 fContent.append(" ");
406 if (ctl2X != dstX || ctl2Y != dstY) {
407 cmd.set("c\n");
408 fContent.appendScalar(ctl2X);
409 fContent.append(" ");
410 fContent.appendScalar(ctl2Y);
411 fContent.append(" ");
412 }
413 fContent.appendScalar(dstX);
414 fContent.append(" ");
415 fContent.appendScalar(dstY);
416 fContent.append(cmd);
417}
418
419void SkPDFDevice::appendRectangle(SkScalar x, SkScalar y,
420 SkScalar w, SkScalar h) {
421 fContent.appendScalar(x);
422 fContent.append(" ");
423 fContent.appendScalar(y);
424 fContent.append(" ");
425 fContent.appendScalar(w);
426 fContent.append(" ");
427 fContent.appendScalar(h);
428 fContent.append(" re\n");
429}
430
431void SkPDFDevice::closePath() {
432 fContent.append("h\n");
433}
434
435void SkPDFDevice::strokePath() {
436 fContent.append(StyleAndFillToPaintOperator(SkPaint::kStroke_Style,
437 SkPath::kWinding_FillType));
438}
439
440void SkPDFDevice::internalDrawBitmap(const SkMatrix& matrix,
441 const SkBitmap& bitmap,
442 const SkPaint& paint) {
443 setTransform(matrix);
444 SkPDFImage* image = new SkPDFImage(bitmap, paint);
445 fXObjectResources.push(image); // Transfer reference.
446 fContent.append("/X");
447 fContent.appendS32(fXObjectResources.count() - 1);
448 fContent.append(" Do\n");
449 setTransform(fCurTransform);
450}
451
452void SkPDFDevice::setTransform(const SkMatrix& m) {
453 setNoTransform();
454 applyTransform(m);
455}
456
457void SkPDFDevice::setNoTransform() {
458 if (fActiveTransform.getType() == SkMatrix::kIdentity_Mask)
459 return;
460 fContent.append("Q q "); // Restore the default transform and save it.
461 fCurrentGraphicState = NULL;
462 fActiveTransform.reset();
463}
464
465void SkPDFDevice::applyTempTransform(const SkMatrix& m) {
466 fContent.append("q ");
467 applyTransform(m);
468}
469
470void SkPDFDevice::removeTempTransform() {
471 fContent.append("Q\n");
472 fActiveTransform = fCurTransform;
473}
474
475void SkPDFDevice::applyTransform(const SkMatrix& m) {
476 if (m == fActiveTransform)
477 return;
478 SkASSERT((m.getType() & SkMatrix::kPerspective_Mask) == 0);
479
480 fContent.appendScalar(m[SkMatrix::kMScaleX]);
481 fContent.append(" ");
482 fContent.appendScalar(m[SkMatrix::kMSkewY]);
483 fContent.append(" ");
484 fContent.appendScalar(m[SkMatrix::kMSkewX]);
485 fContent.append(" ");
486 fContent.appendScalar(m[SkMatrix::kMScaleY]);
487 fContent.append(" ");
488 fContent.appendScalar(m[SkMatrix::kMTransX]);
489 fContent.append(" ");
490 fContent.appendScalar(m[SkMatrix::kMTransY]);
491 fContent.append(" cm\n");
492 fActiveTransform = m;
493}