SkPDFDevice.cpp revision b069c8cfcd5df285193eb334b3bc33438782e8da
1/* 2 * Copyright (C) 2011 Google Inc. 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 "SkClipStack.h" 21#include "SkDraw.h" 22#include "SkGlyphCache.h" 23#include "SkPaint.h" 24#include "SkPath.h" 25#include "SkPDFFont.h" 26#include "SkPDFFormXObject.h" 27#include "SkPDFGraphicState.h" 28#include "SkPDFImage.h" 29#include "SkPDFShader.h" 30#include "SkPDFStream.h" 31#include "SkPDFTypes.h" 32#include "SkPDFUtils.h" 33#include "SkRect.h" 34#include "SkString.h" 35#include "SkTextFormatParams.h" 36#include "SkTypeface.h" 37#include "SkTypes.h" 38 39// Utility functions 40 41static void emit_pdf_color(SkColor color, SkWStream* result) { 42 SkASSERT(SkColorGetA(color) == 0xFF); // We handle alpha elsewhere. 43 SkScalar colorMax = SkIntToScalar(0xFF); 44 SkPDFScalar::Append( 45 SkScalarDiv(SkIntToScalar(SkColorGetR(color)), colorMax), result); 46 result->writeText(" "); 47 SkPDFScalar::Append( 48 SkScalarDiv(SkIntToScalar(SkColorGetG(color)), colorMax), result); 49 result->writeText(" "); 50 SkPDFScalar::Append( 51 SkScalarDiv(SkIntToScalar(SkColorGetB(color)), colorMax), result); 52 result->writeText(" "); 53} 54 55static SkPaint calculate_text_paint(const SkPaint& paint) { 56 SkPaint result = paint; 57 if (result.isFakeBoldText()) { 58 SkScalar fakeBoldScale = SkScalarInterpFunc(result.getTextSize(), 59 kStdFakeBoldInterpKeys, 60 kStdFakeBoldInterpValues, 61 kStdFakeBoldInterpLength); 62 SkScalar width = SkScalarMul(result.getTextSize(), fakeBoldScale); 63 if (result.getStyle() == SkPaint::kFill_Style) 64 result.setStyle(SkPaint::kStrokeAndFill_Style); 65 else 66 width += result.getStrokeWidth(); 67 result.setStrokeWidth(width); 68 } 69 return result; 70} 71 72// Stolen from measure_text in SkDraw.cpp and then tweaked. 73static void align_text(SkDrawCacheProc glyphCacheProc, const SkPaint& paint, 74 const uint16_t* glyphs, size_t len, SkScalar* x, 75 SkScalar* y, SkScalar* width) { 76 if (paint.getTextAlign() == SkPaint::kLeft_Align && width == NULL) 77 return; 78 79 SkMatrix ident; 80 ident.reset(); 81 SkAutoGlyphCache autoCache(paint, &ident); 82 SkGlyphCache* cache = autoCache.getCache(); 83 84 const char* start = (char*)glyphs; 85 const char* stop = (char*)(glyphs + len); 86 SkFixed xAdv = 0, yAdv = 0; 87 88 // TODO(vandebo) This probably needs to take kerning into account. 89 while (start < stop) { 90 const SkGlyph& glyph = glyphCacheProc(cache, &start, 0, 0); 91 xAdv += glyph.fAdvanceX; 92 yAdv += glyph.fAdvanceY; 93 }; 94 if (width) 95 *width = SkFixedToScalar(xAdv); 96 if (paint.getTextAlign() == SkPaint::kLeft_Align) 97 return; 98 99 SkScalar xAdj = SkFixedToScalar(xAdv); 100 SkScalar yAdj = SkFixedToScalar(yAdv); 101 if (paint.getTextAlign() == SkPaint::kCenter_Align) { 102 xAdj = SkScalarHalf(xAdj); 103 yAdj = SkScalarHalf(yAdj); 104 } 105 *x = *x - xAdj; 106 *y = *y - yAdj; 107} 108 109static void set_text_transform(SkScalar x, SkScalar y, SkScalar textSkewX, 110 SkWStream* content) { 111 // Flip the text about the x-axis to account for origin swap and include 112 // the passed parameters. 113 content->writeText("1 0 "); 114 SkPDFScalar::Append(0 - textSkewX, content); 115 content->writeText(" -1 "); 116 SkPDFScalar::Append(x, content); 117 content->writeText(" "); 118 SkPDFScalar::Append(y, content); 119 content->writeText(" Tm\n"); 120} 121 122// It is important to not confuse GraphicStateEntry with SkPDFGraphicState, the 123// later being our representation of an object in the PDF file. 124struct GraphicStateEntry { 125 GraphicStateEntry(); 126 127 // Compare the fields we care about when setting up a new content entry. 128 bool compareInitialState(const GraphicStateEntry& b); 129 130 SkMatrix fMatrix; 131 // We can't do set operations on Paths, though PDF natively supports 132 // intersect. If the clip stack does anything other than intersect, 133 // we have to fall back to the region. Treat fClipStack as authoritative. 134 // See http://code.google.com/p/skia/issues/detail?id=221 135 SkClipStack fClipStack; 136 SkRegion fClipRegion; 137 138 // When emitting the content entry, we will ensure the graphic state 139 // is set to these values first. 140 SkColor fColor; 141 SkScalar fTextScaleX; // Zero means we don't care what the value is. 142 SkPaint::Style fTextFill; // Only if TextScaleX is non-zero. 143 int fShaderIndex; 144 int fGraphicStateIndex; 145 146 // We may change the font (i.e. for Type1 support) within a 147 // ContentEntry. This is the one currently in effect, or NULL if none. 148 SkPDFFont* fFont; 149 // In PDF, text size has no default value. It is only valid if fFont is 150 // not NULL. 151 SkScalar fTextSize; 152}; 153 154GraphicStateEntry::GraphicStateEntry() : fColor(SK_ColorBLACK), 155 fTextScaleX(SK_Scalar1), 156 fTextFill(SkPaint::kFill_Style), 157 fShaderIndex(-1), 158 fGraphicStateIndex(-1), 159 fFont(NULL), 160 fTextSize(SK_ScalarNaN) { 161 fMatrix.reset(); 162} 163 164bool GraphicStateEntry::compareInitialState(const GraphicStateEntry& b) { 165 return fColor == b.fColor && 166 fShaderIndex == b.fShaderIndex && 167 fGraphicStateIndex == b.fGraphicStateIndex && 168 fMatrix == b.fMatrix && 169 fClipStack == b.fClipStack && 170 (fTextScaleX == 0 || 171 b.fTextScaleX == 0 || 172 (fTextScaleX == b.fTextScaleX && fTextFill == b.fTextFill)); 173} 174 175class GraphicStackState { 176public: 177 GraphicStackState(const SkClipStack& existingClipStack, 178 const SkRegion& existingClipRegion, 179 SkWStream* contentStream) 180 : fStackDepth(0), 181 fContentStream(contentStream) { 182 fEntries[0].fClipStack = existingClipStack; 183 fEntries[0].fClipRegion = existingClipRegion; 184 } 185 186 void updateClip(const SkClipStack& clipStack, const SkRegion& clipRegion, 187 const SkIPoint& translation); 188 void updateMatrix(const SkMatrix& matrix); 189 void updateDrawingState(const GraphicStateEntry& state); 190 191 void drainStack(); 192 193private: 194 void push(); 195 void pop(); 196 GraphicStateEntry* currentEntry() { return &fEntries[fStackDepth]; } 197 198 // Conservative limit on save depth, see impl. notes in PDF 1.4 spec. 199 static const int kMaxStackDepth = 12; 200 GraphicStateEntry fEntries[kMaxStackDepth + 1]; 201 int fStackDepth; 202 SkWStream* fContentStream; 203}; 204 205void GraphicStackState::drainStack() { 206 while (fStackDepth) { 207 pop(); 208 } 209} 210 211void GraphicStackState::push() { 212 SkASSERT(fStackDepth < kMaxStackDepth); 213 fContentStream->writeText("q\n"); 214 fStackDepth++; 215 fEntries[fStackDepth] = fEntries[fStackDepth - 1]; 216} 217 218void GraphicStackState::pop() { 219 SkASSERT(fStackDepth > 0); 220 fContentStream->writeText("Q\n"); 221 fStackDepth--; 222} 223 224// This function initializes iter to be an interator on the "stack" argument 225// and then skips over the leading entries as specified in prefix. It requires 226// and asserts that "prefix" will be a prefix to "stack." 227static void skip_clip_stack_prefix(const SkClipStack& prefix, 228 const SkClipStack& stack, 229 SkClipStack::B2FIter* iter) { 230 SkClipStack::B2FIter prefixIter(prefix); 231 iter->reset(stack); 232 233 const SkClipStack::B2FIter::Clip* prefixEntry; 234 const SkClipStack::B2FIter::Clip* iterEntry; 235 236 for (prefixEntry = prefixIter.next(); prefixEntry; 237 prefixEntry = prefixIter.next()) { 238 iterEntry = iter->next(); 239 SkASSERT(iterEntry); 240 SkASSERT(*prefixEntry == *iterEntry); 241 } 242 243 SkASSERT(prefixEntry == NULL); 244} 245 246static void emit_clip(SkPath* clipPath, SkRect* clipRect, 247 SkWStream* contentStream) { 248 SkASSERT(clipPath || clipRect); 249 250 SkPath::FillType clipFill; 251 if (clipPath) { 252 SkPDFUtils::EmitPath(*clipPath, contentStream); 253 clipFill = clipPath->getFillType(); 254 } else if (clipRect) { 255 SkPDFUtils::AppendRectangle(*clipRect, contentStream); 256 clipFill = SkPath::kWinding_FillType; 257 } 258 259 NOT_IMPLEMENTED(clipFill == SkPath::kInverseEvenOdd_FillType, false); 260 NOT_IMPLEMENTED(clipFill == SkPath::kInverseWinding_FillType, false); 261 if (clipFill == SkPath::kEvenOdd_FillType) { 262 contentStream->writeText("W* n\n"); 263 } else { 264 contentStream->writeText("W n\n"); 265 } 266} 267 268// TODO(vandebo) Take advantage of SkClipStack::getSaveCount(), the PDF 269// graphic state stack, and the fact that we can know all the clips used 270// on the page to optimize this. 271void GraphicStackState::updateClip(const SkClipStack& clipStack, 272 const SkRegion& clipRegion, 273 const SkIPoint& translation) { 274 if (clipStack == currentEntry()->fClipStack) { 275 return; 276 } 277 278 while (fStackDepth > 0) { 279 pop(); 280 if (clipStack == currentEntry()->fClipStack) { 281 return; 282 } 283 } 284 push(); 285 286 // gsState->initialEntry()->fClipStack/Region specifies the clip that has 287 // already been applied. (If this is a top level device, then it specifies 288 // a clip to the content area. If this is a layer, then it specifies 289 // the clip in effect when the layer was created.) There's no need to 290 // reapply that clip; SKCanvas's SkDrawIter will draw anything outside the 291 // initial clip on the parent layer. (This means there's a bug if the user 292 // expands the clip and then uses any xfer mode that uses dst: 293 // http://code.google.com/p/skia/issues/detail?id=228 ) 294 SkClipStack::B2FIter iter; 295 skip_clip_stack_prefix(fEntries[0].fClipStack, clipStack, &iter); 296 297 // If the clip stack does anything other than intersect or if it uses 298 // an inverse fill type, we have to fall back to the clip region. 299 bool needRegion = false; 300 const SkClipStack::B2FIter::Clip* clipEntry; 301 for (clipEntry = iter.next(); clipEntry; clipEntry = iter.next()) { 302 if (clipEntry->fOp != SkRegion::kIntersect_Op || 303 (clipEntry->fPath && clipEntry->fPath->isInverseFillType())) { 304 needRegion = true; 305 break; 306 } 307 } 308 309 if (needRegion) { 310 SkPath clipPath; 311 SkAssertResult(clipRegion.getBoundaryPath(&clipPath)); 312 emit_clip(&clipPath, NULL, fContentStream); 313 } else { 314 skip_clip_stack_prefix(fEntries[0].fClipStack, clipStack, &iter); 315 SkMatrix transform; 316 transform.setTranslate(translation.fX, translation.fY); 317 const SkClipStack::B2FIter::Clip* clipEntry; 318 for (clipEntry = iter.next(); clipEntry; clipEntry = iter.next()) { 319 SkASSERT(clipEntry->fOp == SkRegion::kIntersect_Op); 320 if (clipEntry->fRect) { 321 SkRect translatedClip; 322 transform.mapRect(&translatedClip, *clipEntry->fRect); 323 emit_clip(NULL, &translatedClip, fContentStream); 324 } else if (clipEntry->fPath) { 325 SkPath translatedPath; 326 clipEntry->fPath->transform(transform, &translatedPath); 327 emit_clip(&translatedPath, NULL, fContentStream); 328 } else { 329 SkASSERT(false); 330 } 331 } 332 } 333 currentEntry()->fClipStack = clipStack; 334 currentEntry()->fClipRegion = clipRegion; 335} 336 337void GraphicStackState::updateMatrix(const SkMatrix& matrix) { 338 if (matrix == currentEntry()->fMatrix) { 339 return; 340 } 341 342 if (currentEntry()->fMatrix.getType() != SkMatrix::kIdentity_Mask) { 343 SkASSERT(fStackDepth > 0); 344 SkASSERT(fEntries[fStackDepth].fClipStack == 345 fEntries[fStackDepth -1].fClipStack); 346 pop(); 347 348 SkASSERT(currentEntry()->fMatrix.getType() == SkMatrix::kIdentity_Mask); 349 } 350 if (matrix.getType() == SkMatrix::kIdentity_Mask) { 351 return; 352 } 353 354 push(); 355 SkPDFUtils::AppendTransform(matrix, fContentStream); 356 currentEntry()->fMatrix = matrix; 357} 358 359void GraphicStackState::updateDrawingState(const GraphicStateEntry& state) { 360 // PDF treats a shader as a color, so we only set one or the other. 361 if (state.fShaderIndex >= 0) { 362 if (state.fShaderIndex != currentEntry()->fShaderIndex) { 363 fContentStream->writeText("/Pattern CS /Pattern cs /P"); 364 fContentStream->writeDecAsText(state.fShaderIndex); 365 fContentStream->writeText(" SCN /P"); 366 fContentStream->writeDecAsText(state.fShaderIndex); 367 fContentStream->writeText(" scn\n"); 368 currentEntry()->fShaderIndex = state.fShaderIndex; 369 } 370 } else { 371 if (state.fColor != currentEntry()->fColor || 372 currentEntry()->fShaderIndex >= 0) { 373 emit_pdf_color(state.fColor, fContentStream); 374 fContentStream->writeText("RG "); 375 emit_pdf_color(state.fColor, fContentStream); 376 fContentStream->writeText("rg\n"); 377 currentEntry()->fColor = state.fColor; 378 currentEntry()->fShaderIndex = -1; 379 } 380 } 381 382 if (state.fGraphicStateIndex != currentEntry()->fGraphicStateIndex) { 383 SkPDFUtils::ApplyGraphicState(state.fGraphicStateIndex, fContentStream); 384 currentEntry()->fGraphicStateIndex = state.fGraphicStateIndex; 385 } 386 387 if (state.fTextScaleX) { 388 if (state.fTextScaleX != currentEntry()->fTextScaleX) { 389 SkScalar pdfScale = SkScalarMul(state.fTextScaleX, 390 SkIntToScalar(100)); 391 SkPDFScalar::Append(pdfScale, fContentStream); 392 fContentStream->writeText(" Tz\n"); 393 currentEntry()->fTextScaleX = state.fTextScaleX; 394 } 395 if (state.fTextFill != currentEntry()->fTextFill) { 396 SK_COMPILE_ASSERT(SkPaint::kFill_Style == 0, enum_must_match_value); 397 SK_COMPILE_ASSERT(SkPaint::kStroke_Style == 1, 398 enum_must_match_value); 399 SK_COMPILE_ASSERT(SkPaint::kStrokeAndFill_Style == 2, 400 enum_must_match_value); 401 fContentStream->writeDecAsText(state.fTextFill); 402 fContentStream->writeText(" Tr\n"); 403 currentEntry()->fTextFill = state.fTextFill; 404 } 405 } 406} 407 408struct ContentEntry { 409 GraphicStateEntry fState; 410 SkDynamicMemoryWStream fContent; 411 SkTScopedPtr<ContentEntry> fNext; 412}; 413 414// A helper class to automatically finish a ContentEntry at the end of a 415// drawing method and maintain the state needed between set up and finish. 416class ContentEntryAccessor { 417public: 418 ContentEntryAccessor(SkPDFDevice* device, const SkDraw& draw, 419 const SkPaint& paint, bool hasText = false) 420 : fDevice(device), 421 fContentEntry(NULL), 422 fXfermode(SkXfermode::kSrcOver_Mode) { 423 init(draw.fClipStack, *draw.fClip, *draw.fMatrix, paint, hasText); 424 } 425 ContentEntryAccessor(SkPDFDevice* device, const SkClipStack* clipStack, 426 const SkRegion& clipRegion, const SkMatrix& matrix, 427 const SkPaint& paint, bool hasText = false) 428 : fDevice(device), 429 fContentEntry(NULL), 430 fXfermode(SkXfermode::kSrcOver_Mode) { 431 init(clipStack, clipRegion, matrix, paint, hasText); 432 } 433 434 ~ContentEntryAccessor() { 435 if (fContentEntry) { 436 fDevice->finishContentEntry(fXfermode, fDstFormXObject.get()); 437 } 438 } 439 440 ContentEntry* entry() { return fContentEntry; } 441private: 442 SkPDFDevice* fDevice; 443 ContentEntry* fContentEntry; 444 SkXfermode::Mode fXfermode; 445 SkRefPtr<SkPDFFormXObject> fDstFormXObject; 446 447 void init(const SkClipStack* clipStack, const SkRegion& clipRegion, 448 const SkMatrix& matrix, const SkPaint& paint, bool hasText) { 449 if (paint.getXfermode()) { 450 paint.getXfermode()->asMode(&fXfermode); 451 } 452 fContentEntry = fDevice->setUpContentEntry(clipStack, clipRegion, 453 matrix, paint, hasText, 454 &fDstFormXObject); 455 } 456}; 457 458//////////////////////////////////////////////////////////////////////////////// 459 460SkDevice* SkPDFDeviceFactory::newDevice(SkCanvas* c, SkBitmap::Config config, 461 int width, int height, bool isOpaque, 462 bool isForLayer) { 463 SkMatrix initialTransform; 464 initialTransform.reset(); 465 SkISize size = SkISize::Make(width, height); 466 if (isForLayer) { 467 return SkNEW_ARGS(SkPDFDevice, (size, c->getTotalClipStack(), 468 c->getTotalClip())); 469 } else { 470 return SkNEW_ARGS(SkPDFDevice, (size, size, initialTransform)); 471 } 472} 473 474static inline SkBitmap makeContentBitmap(const SkISize& contentSize, 475 const SkMatrix* initialTransform) { 476 SkBitmap bitmap; 477 if (initialTransform) { 478 // Compute the size of the drawing area. 479 SkVector drawingSize; 480 SkMatrix inverse; 481 drawingSize.set(contentSize.fWidth, contentSize.fHeight); 482 initialTransform->invert(&inverse); 483 inverse.mapVectors(&drawingSize, 1); 484 SkISize size = SkSize::Make(drawingSize.fX, drawingSize.fY).toRound(); 485 bitmap.setConfig(SkBitmap::kNo_Config, abs(size.fWidth), 486 abs(size.fHeight)); 487 } else { 488 bitmap.setConfig(SkBitmap::kNo_Config, abs(contentSize.fWidth), 489 abs(contentSize.fHeight)); 490 } 491 492 return bitmap; 493} 494 495SkPDFDevice::SkPDFDevice(const SkISize& pageSize, const SkISize& contentSize, 496 const SkMatrix& initialTransform) 497 : SkDevice(NULL, makeContentBitmap(contentSize, &initialTransform), false), 498 fPageSize(pageSize), 499 fContentSize(contentSize), 500 fLastContentEntry(NULL) { 501 // Skia generally uses the top left as the origin but PDF natively has the 502 // origin at the bottom left. This matrix corrects for that. But that only 503 // needs to be done once, we don't do it when layering. 504 fInitialTransform.setTranslate(0, pageSize.fHeight); 505 fInitialTransform.preScale(1, -1); 506 fInitialTransform.preConcat(initialTransform); 507 508 SkIRect existingClip = SkIRect::MakeWH(this->width(), this->height()); 509 fExistingClipStack.clipDevRect(existingClip); 510 fExistingClipRegion.setRect(existingClip); 511 512 this->init(); 513} 514 515SkPDFDevice::SkPDFDevice(const SkISize& layerSize, 516 const SkClipStack& existingClipStack, 517 const SkRegion& existingClipRegion) 518 : SkDevice(NULL, makeContentBitmap(layerSize, NULL), false), 519 fPageSize(layerSize), 520 fContentSize(layerSize), 521 fExistingClipStack(existingClipStack), 522 fExistingClipRegion(existingClipRegion), 523 fLastContentEntry(NULL) { 524 fInitialTransform.reset(); 525 this->init(); 526} 527 528SkPDFDevice::~SkPDFDevice() { 529 this->cleanUp(); 530} 531 532void SkPDFDevice::init() { 533 fResourceDict = NULL; 534 fContentEntries.reset(); 535 fLastContentEntry = NULL; 536} 537 538SkDeviceFactory* SkPDFDevice::onNewDeviceFactory() { 539 return SkNEW(SkPDFDeviceFactory); 540} 541 542void SkPDFDevice::cleanUp() { 543 fGraphicStateResources.unrefAll(); 544 fXObjectResources.unrefAll(); 545 fFontResources.unrefAll(); 546 fShaderResources.unrefAll(); 547} 548 549void SkPDFDevice::clear(SkColor color) { 550 this->cleanUp(); 551 this->init(); 552 553 SkPaint paint; 554 paint.setColor(color); 555 paint.setStyle(SkPaint::kFill_Style); 556 SkMatrix identity; 557 identity.reset(); 558 ContentEntryAccessor content(this, &fExistingClipStack, fExistingClipRegion, 559 identity, paint); 560 internalDrawPaint(paint, content.entry()); 561} 562 563void SkPDFDevice::drawPaint(const SkDraw& d, const SkPaint& paint) { 564 SkPaint newPaint = paint; 565 newPaint.setStyle(SkPaint::kFill_Style); 566 ContentEntryAccessor content(this, d, newPaint); 567 internalDrawPaint(newPaint, content.entry()); 568} 569 570void SkPDFDevice::internalDrawPaint(const SkPaint& paint, 571 ContentEntry* contentEntry) { 572 if (!contentEntry) { 573 return; 574 } 575 SkRect bbox = SkRect::MakeWH(SkIntToScalar(this->width()), 576 SkIntToScalar(this->height())); 577 SkMatrix totalTransform = fInitialTransform; 578 totalTransform.preConcat(contentEntry->fState.fMatrix); 579 SkMatrix inverse; 580 inverse.reset(); 581 totalTransform.invert(&inverse); 582 inverse.mapRect(&bbox); 583 584 SkPDFUtils::AppendRectangle(bbox, &contentEntry->fContent); 585 SkPDFUtils::PaintPath(paint.getStyle(), SkPath::kWinding_FillType, 586 &contentEntry->fContent); 587} 588 589void SkPDFDevice::drawPoints(const SkDraw& d, SkCanvas::PointMode mode, 590 size_t count, const SkPoint* points, 591 const SkPaint& passedPaint) { 592 if (count == 0) { 593 return; 594 } 595 596 // SkDraw::drawPoints converts to multiple calls to fDevice->drawPath. 597 // We only use this when there's a path effect because of the overhead 598 // of multiple calls to setUpContentEntry it causes. 599 if (passedPaint.getPathEffect()) { 600 if (d.fClip->isEmpty()) { 601 return; 602 } 603 SkDraw pointDraw(d); 604 pointDraw.fDevice = this; 605 pointDraw.drawPoints(mode, count, points, passedPaint, true); 606 return; 607 } 608 609 const SkPaint* paint = &passedPaint; 610 SkPaint modifiedPaint; 611 612 if (mode == SkCanvas::kPoints_PointMode && 613 paint->getStrokeCap() != SkPaint::kRound_Cap) { 614 modifiedPaint = *paint; 615 paint = &modifiedPaint; 616 if (paint->getStrokeWidth()) { 617 // PDF won't draw a single point with square/butt caps because the 618 // orientation is ambiguous. Draw a rectangle instead. 619 modifiedPaint.setStyle(SkPaint::kFill_Style); 620 SkScalar strokeWidth = paint->getStrokeWidth(); 621 SkScalar halfStroke = SkScalarHalf(strokeWidth); 622 for (size_t i = 0; i < count; i++) { 623 SkRect r = SkRect::MakeXYWH(points[i].fX, points[i].fY, 0, 0); 624 r.inset(-halfStroke, -halfStroke); 625 drawRect(d, r, modifiedPaint); 626 } 627 return; 628 } else { 629 modifiedPaint.setStrokeCap(SkPaint::kRound_Cap); 630 } 631 } 632 633 ContentEntryAccessor content(this, d, *paint); 634 if (!content.entry()) { 635 return; 636 } 637 638 switch (mode) { 639 case SkCanvas::kPolygon_PointMode: 640 SkPDFUtils::MoveTo(points[0].fX, points[0].fY, 641 &content.entry()->fContent); 642 for (size_t i = 1; i < count; i++) { 643 SkPDFUtils::AppendLine(points[i].fX, points[i].fY, 644 &content.entry()->fContent); 645 } 646 SkPDFUtils::StrokePath(&content.entry()->fContent); 647 break; 648 case SkCanvas::kLines_PointMode: 649 for (size_t i = 0; i < count/2; i++) { 650 SkPDFUtils::MoveTo(points[i * 2].fX, points[i * 2].fY, 651 &content.entry()->fContent); 652 SkPDFUtils::AppendLine(points[i * 2 + 1].fX, 653 points[i * 2 + 1].fY, 654 &content.entry()->fContent); 655 SkPDFUtils::StrokePath(&content.entry()->fContent); 656 } 657 break; 658 case SkCanvas::kPoints_PointMode: 659 SkASSERT(paint->getStrokeCap() == SkPaint::kRound_Cap); 660 for (size_t i = 0; i < count; i++) { 661 SkPDFUtils::MoveTo(points[i].fX, points[i].fY, 662 &content.entry()->fContent); 663 SkPDFUtils::ClosePath(&content.entry()->fContent); 664 SkPDFUtils::StrokePath(&content.entry()->fContent); 665 } 666 break; 667 default: 668 SkASSERT(false); 669 } 670} 671 672void SkPDFDevice::drawRect(const SkDraw& d, const SkRect& r, 673 const SkPaint& paint) { 674 if (paint.getPathEffect()) { 675 if (d.fClip->isEmpty()) { 676 return; 677 } 678 SkPath path; 679 path.addRect(r); 680 drawPath(d, path, paint, NULL, true); 681 return; 682 } 683 684 ContentEntryAccessor content(this, d, paint); 685 if (!content.entry()) { 686 return; 687 } 688 SkPDFUtils::AppendRectangle(r, &content.entry()->fContent); 689 SkPDFUtils::PaintPath(paint.getStyle(), SkPath::kWinding_FillType, 690 &content.entry()->fContent); 691} 692 693void SkPDFDevice::drawPath(const SkDraw& d, const SkPath& origPath, 694 const SkPaint& paint, const SkMatrix* prePathMatrix, 695 bool pathIsMutable) { 696 SkPath modifiedPath; 697 SkPath* pathPtr = const_cast<SkPath*>(&origPath); 698 699 SkMatrix matrix = *d.fMatrix; 700 if (prePathMatrix) { 701 if (paint.getPathEffect() || paint.getStyle() != SkPaint::kFill_Style) { 702 if (!pathIsMutable) { 703 pathPtr = &modifiedPath; 704 pathIsMutable = true; 705 } 706 origPath.transform(*prePathMatrix, pathPtr); 707 } else { 708 if (!matrix.preConcat(*prePathMatrix)) { 709 return; 710 } 711 } 712 } 713 714 if (paint.getPathEffect()) { 715 if (d.fClip->isEmpty()) { 716 return; 717 } 718 if (!pathIsMutable) { 719 pathPtr = &modifiedPath; 720 pathIsMutable = true; 721 } 722 bool fill = paint.getFillPath(origPath, pathPtr); 723 724 SkPaint noEffectPaint(paint); 725 noEffectPaint.setPathEffect(NULL); 726 if (fill) { 727 noEffectPaint.setStyle(SkPaint::kFill_Style); 728 } else { 729 noEffectPaint.setStyle(SkPaint::kStroke_Style); 730 noEffectPaint.setStrokeWidth(0); 731 } 732 drawPath(d, *pathPtr, noEffectPaint, NULL, true); 733 return; 734 } 735 736 ContentEntryAccessor content(this, d, paint); 737 if (!content.entry()) { 738 return; 739 } 740 SkPDFUtils::EmitPath(*pathPtr, &content.entry()->fContent); 741 SkPDFUtils::PaintPath(paint.getStyle(), pathPtr->getFillType(), 742 &content.entry()->fContent); 743} 744 745void SkPDFDevice::drawBitmap(const SkDraw& d, const SkBitmap& bitmap, 746 const SkIRect* srcRect, const SkMatrix& matrix, 747 const SkPaint& paint) { 748 if (d.fClip->isEmpty()) { 749 return; 750 } 751 752 SkMatrix transform = matrix; 753 transform.postConcat(*d.fMatrix); 754 internalDrawBitmap(transform, d.fClipStack, *d.fClip, bitmap, srcRect, 755 paint); 756} 757 758void SkPDFDevice::drawSprite(const SkDraw& d, const SkBitmap& bitmap, 759 int x, int y, const SkPaint& paint) { 760 if (d.fClip->isEmpty()) { 761 return; 762 } 763 764 SkMatrix matrix; 765 matrix.setTranslate(SkIntToScalar(x), SkIntToScalar(y)); 766 internalDrawBitmap(matrix, d.fClipStack, *d.fClip, bitmap, NULL, paint); 767} 768 769void SkPDFDevice::drawText(const SkDraw& d, const void* text, size_t len, 770 SkScalar x, SkScalar y, const SkPaint& paint) { 771 SkPaint textPaint = calculate_text_paint(paint); 772 ContentEntryAccessor content(this, d, textPaint, true); 773 if (!content.entry()) { 774 return; 775 } 776 777 // We want the text in glyph id encoding and a writable buffer, so we end 778 // up making a copy either way. 779 size_t numGlyphs = paint.textToGlyphs(text, len, NULL); 780 uint16_t* glyphIDs = 781 (uint16_t*)sk_malloc_flags(numGlyphs * 2, 782 SK_MALLOC_TEMP | SK_MALLOC_THROW); 783 SkAutoFree autoFreeGlyphIDs(glyphIDs); 784 if (paint.getTextEncoding() != SkPaint::kGlyphID_TextEncoding) { 785 paint.textToGlyphs(text, len, glyphIDs); 786 textPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); 787 } else { 788 SkASSERT((len & 1) == 0); 789 SkASSERT(len / 2 == numGlyphs); 790 memcpy(glyphIDs, text, len); 791 } 792 793 SkScalar width; 794 SkScalar* widthPtr = NULL; 795 if (textPaint.isUnderlineText() || textPaint.isStrikeThruText()) 796 widthPtr = &width; 797 798 SkDrawCacheProc glyphCacheProc = textPaint.getDrawCacheProc(); 799 align_text(glyphCacheProc, textPaint, glyphIDs, numGlyphs, &x, &y, 800 widthPtr); 801 content.entry()->fContent.writeText("BT\n"); 802 set_text_transform(x, y, textPaint.getTextSkewX(), 803 &content.entry()->fContent); 804 size_t consumedGlyphCount = 0; 805 while (numGlyphs > consumedGlyphCount) { 806 updateFont(textPaint, glyphIDs[consumedGlyphCount], content.entry()); 807 SkPDFFont* font = content.entry()->fState.fFont; 808 size_t availableGlyphs = 809 font->glyphsToPDFFontEncoding(glyphIDs + consumedGlyphCount, 810 numGlyphs - consumedGlyphCount); 811 SkString encodedString = 812 SkPDFString::formatString(glyphIDs + consumedGlyphCount, 813 availableGlyphs, font->multiByteGlyphs()); 814 content.entry()->fContent.writeText(encodedString.c_str()); 815 consumedGlyphCount += availableGlyphs; 816 content.entry()->fContent.writeText(" Tj\n"); 817 } 818 content.entry()->fContent.writeText("ET\n"); 819 820 // Draw underline and/or strikethrough if the paint has them. 821 // drawPosText() and drawTextOnPath() don't draw underline or strikethrough 822 // because the raster versions don't. Use paint instead of textPaint 823 // because we may have changed strokeWidth to do fakeBold text. 824 if (paint.isUnderlineText() || paint.isStrikeThruText()) { 825 SkScalar textSize = paint.getTextSize(); 826 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness); 827 828 if (paint.isUnderlineText()) { 829 SkScalar top = SkScalarMulAdd(textSize, kStdUnderline_Offset, y); 830 SkRect r = SkRect::MakeXYWH(x, top - height, width, height); 831 drawRect(d, r, paint); 832 } 833 if (paint.isStrikeThruText()) { 834 SkScalar top = SkScalarMulAdd(textSize, kStdStrikeThru_Offset, y); 835 SkRect r = SkRect::MakeXYWH(x, top - height, width, height); 836 drawRect(d, r, paint); 837 } 838 } 839} 840 841void SkPDFDevice::drawPosText(const SkDraw& d, const void* text, size_t len, 842 const SkScalar pos[], SkScalar constY, 843 int scalarsPerPos, const SkPaint& paint) { 844 SkASSERT(1 == scalarsPerPos || 2 == scalarsPerPos); 845 SkPaint textPaint = calculate_text_paint(paint); 846 ContentEntryAccessor content(this, d, textPaint, true); 847 if (!content.entry()) { 848 return; 849 } 850 851 // Make sure we have a glyph id encoding. 852 SkAutoFree glyphStorage; 853 uint16_t* glyphIDs; 854 size_t numGlyphs; 855 if (paint.getTextEncoding() != SkPaint::kGlyphID_TextEncoding) { 856 numGlyphs = paint.textToGlyphs(text, len, NULL); 857 glyphIDs = (uint16_t*)sk_malloc_flags(numGlyphs * 2, 858 SK_MALLOC_TEMP | SK_MALLOC_THROW); 859 glyphStorage.set(glyphIDs); 860 paint.textToGlyphs(text, len, glyphIDs); 861 textPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); 862 } else { 863 SkASSERT((len & 1) == 0); 864 numGlyphs = len / 2; 865 glyphIDs = (uint16_t*)text; 866 } 867 868 SkDrawCacheProc glyphCacheProc = textPaint.getDrawCacheProc(); 869 content.entry()->fContent.writeText("BT\n"); 870 updateFont(textPaint, glyphIDs[0], content.entry()); 871 for (size_t i = 0; i < numGlyphs; i++) { 872 SkPDFFont* font = content.entry()->fState.fFont; 873 uint16_t encodedValue = glyphIDs[i]; 874 if (font->glyphsToPDFFontEncoding(&encodedValue, 1) != 1) { 875 updateFont(textPaint, glyphIDs[i], content.entry()); 876 i--; 877 continue; 878 } 879 SkScalar x = pos[i * scalarsPerPos]; 880 SkScalar y = scalarsPerPos == 1 ? constY : pos[i * scalarsPerPos + 1]; 881 align_text(glyphCacheProc, textPaint, glyphIDs + i, 1, &x, &y, NULL); 882 set_text_transform(x, y, textPaint.getTextSkewX(), 883 &content.entry()->fContent); 884 SkString encodedString = 885 SkPDFString::formatString(&encodedValue, 1, 886 font->multiByteGlyphs()); 887 content.entry()->fContent.writeText(encodedString.c_str()); 888 content.entry()->fContent.writeText(" Tj\n"); 889 } 890 content.entry()->fContent.writeText("ET\n"); 891} 892 893void SkPDFDevice::drawTextOnPath(const SkDraw& d, const void* text, size_t len, 894 const SkPath& path, const SkMatrix* matrix, 895 const SkPaint& paint) { 896 if (d.fClip->isEmpty()) { 897 return; 898 } 899 NOT_IMPLEMENTED("drawTextOnPath", true); 900} 901 902void SkPDFDevice::drawVertices(const SkDraw& d, SkCanvas::VertexMode, 903 int vertexCount, const SkPoint verts[], 904 const SkPoint texs[], const SkColor colors[], 905 SkXfermode* xmode, const uint16_t indices[], 906 int indexCount, const SkPaint& paint) { 907 if (d.fClip->isEmpty()) { 908 return; 909 } 910 NOT_IMPLEMENTED("drawVerticies", true); 911} 912 913void SkPDFDevice::drawDevice(const SkDraw& d, SkDevice* device, int x, int y, 914 const SkPaint& paint) { 915 if ((device->getDeviceCapabilities() & kVector_Capability) == 0) { 916 // If we somehow get a raster device, do what our parent would do. 917 SkDevice::drawDevice(d, device, x, y, paint); 918 return; 919 } 920 921 SkMatrix matrix; 922 matrix.setTranslate(SkIntToScalar(x), SkIntToScalar(y)); 923 ContentEntryAccessor content(this, d.fClipStack, *d.fClip, matrix, paint); 924 if (!content.entry()) { 925 return; 926 } 927 928 // Assume that a vector capable device means that it's a PDF Device. 929 SkPDFDevice* pdfDevice = static_cast<SkPDFDevice*>(device); 930 SkPDFFormXObject* xobject = new SkPDFFormXObject(pdfDevice); 931 fXObjectResources.push(xobject); // Transfer reference. 932 SkPDFUtils::DrawFormXObject(fXObjectResources.count() - 1, 933 &content.entry()->fContent); 934} 935 936const SkRefPtr<SkPDFDict>& SkPDFDevice::getResourceDict() { 937 if (fResourceDict.get() == NULL) { 938 fResourceDict = new SkPDFDict; 939 fResourceDict->unref(); // SkRefPtr and new both took a reference. 940 941 if (fGraphicStateResources.count()) { 942 SkRefPtr<SkPDFDict> extGState = new SkPDFDict(); 943 extGState->unref(); // SkRefPtr and new both took a reference. 944 for (int i = 0; i < fGraphicStateResources.count(); i++) { 945 SkString nameString("G"); 946 nameString.appendS32(i); 947 extGState->insert( 948 nameString.c_str(), 949 new SkPDFObjRef(fGraphicStateResources[i]))->unref(); 950 } 951 fResourceDict->insert("ExtGState", extGState.get()); 952 } 953 954 if (fXObjectResources.count()) { 955 SkRefPtr<SkPDFDict> xObjects = new SkPDFDict(); 956 xObjects->unref(); // SkRefPtr and new both took a reference. 957 for (int i = 0; i < fXObjectResources.count(); i++) { 958 SkString nameString("X"); 959 nameString.appendS32(i); 960 xObjects->insert( 961 nameString.c_str(), 962 new SkPDFObjRef(fXObjectResources[i]))->unref(); 963 } 964 fResourceDict->insert("XObject", xObjects.get()); 965 } 966 967 if (fFontResources.count()) { 968 SkRefPtr<SkPDFDict> fonts = new SkPDFDict(); 969 fonts->unref(); // SkRefPtr and new both took a reference. 970 for (int i = 0; i < fFontResources.count(); i++) { 971 SkString nameString("F"); 972 nameString.appendS32(i); 973 fonts->insert(nameString.c_str(), 974 new SkPDFObjRef(fFontResources[i]))->unref(); 975 } 976 fResourceDict->insert("Font", fonts.get()); 977 } 978 979 if (fShaderResources.count()) { 980 SkRefPtr<SkPDFDict> patterns = new SkPDFDict(); 981 patterns->unref(); // SkRefPtr and new both took a reference. 982 for (int i = 0; i < fShaderResources.count(); i++) { 983 SkString nameString("P"); 984 nameString.appendS32(i); 985 patterns->insert(nameString.c_str(), 986 new SkPDFObjRef(fShaderResources[i]))->unref(); 987 } 988 fResourceDict->insert("Pattern", patterns.get()); 989 } 990 991 // For compatibility, add all proc sets (only used for output to PS 992 // devices). 993 const char procs[][7] = {"PDF", "Text", "ImageB", "ImageC", "ImageI"}; 994 SkRefPtr<SkPDFArray> procSets = new SkPDFArray(); 995 procSets->unref(); // SkRefPtr and new both took a reference. 996 procSets->reserve(SK_ARRAY_COUNT(procs)); 997 for (size_t i = 0; i < SK_ARRAY_COUNT(procs); i++) 998 procSets->append(new SkPDFName(procs[i]))->unref(); 999 fResourceDict->insert("ProcSet", procSets.get()); 1000 } 1001 return fResourceDict; 1002} 1003 1004void SkPDFDevice::getResources(SkTDArray<SkPDFObject*>* resourceList) const { 1005 resourceList->setReserve(resourceList->count() + 1006 fGraphicStateResources.count() + 1007 fXObjectResources.count() + 1008 fFontResources.count() + 1009 fShaderResources.count()); 1010 for (int i = 0; i < fGraphicStateResources.count(); i++) { 1011 resourceList->push(fGraphicStateResources[i]); 1012 fGraphicStateResources[i]->ref(); 1013 fGraphicStateResources[i]->getResources(resourceList); 1014 } 1015 for (int i = 0; i < fXObjectResources.count(); i++) { 1016 resourceList->push(fXObjectResources[i]); 1017 fXObjectResources[i]->ref(); 1018 fXObjectResources[i]->getResources(resourceList); 1019 } 1020 for (int i = 0; i < fFontResources.count(); i++) { 1021 resourceList->push(fFontResources[i]); 1022 fFontResources[i]->ref(); 1023 fFontResources[i]->getResources(resourceList); 1024 } 1025 for (int i = 0; i < fShaderResources.count(); i++) { 1026 resourceList->push(fShaderResources[i]); 1027 fShaderResources[i]->ref(); 1028 fShaderResources[i]->getResources(resourceList); 1029 } 1030} 1031 1032SkRefPtr<SkPDFArray> SkPDFDevice::getMediaBox() const { 1033 SkRefPtr<SkPDFInt> zero = new SkPDFInt(0); 1034 zero->unref(); // SkRefPtr and new both took a reference. 1035 1036 SkRefPtr<SkPDFArray> mediaBox = new SkPDFArray(); 1037 mediaBox->unref(); // SkRefPtr and new both took a reference. 1038 mediaBox->reserve(4); 1039 mediaBox->append(zero.get()); 1040 mediaBox->append(zero.get()); 1041 mediaBox->append(new SkPDFInt(fPageSize.fWidth))->unref(); 1042 mediaBox->append(new SkPDFInt(fPageSize.fHeight))->unref(); 1043 return mediaBox; 1044} 1045 1046SkStream* SkPDFDevice::content() const { 1047 SkDynamicMemoryWStream data; 1048 if (fInitialTransform.getType() != SkMatrix::kIdentity_Mask) { 1049 SkPDFUtils::AppendTransform(fInitialTransform, &data); 1050 } 1051 // If the content area is the entire page, then we don't need to clip 1052 // the content area (PDF area clips to the page size). Otherwise, 1053 // we have to clip to the content area; we've already applied the 1054 // initial transform, so just clip to the device size. 1055 if (fPageSize != fContentSize) { 1056 SkRect r = SkRect::MakeWH(this->width(), this->height()); 1057 emit_clip(NULL, &r, &data); 1058 } 1059 1060 GraphicStackState gsState(fExistingClipStack, fExistingClipRegion, &data); 1061 for (ContentEntry* entry = fContentEntries.get(); 1062 entry != NULL; 1063 entry = entry->fNext.get()) { 1064 SkIPoint translation = this->getOrigin(); 1065 translation.negate(); 1066 gsState.updateClip(entry->fState.fClipStack, entry->fState.fClipRegion, 1067 translation); 1068 gsState.updateMatrix(entry->fState.fMatrix); 1069 gsState.updateDrawingState(entry->fState); 1070 data.write(entry->fContent.getStream(), entry->fContent.getOffset()); 1071 } 1072 gsState.drainStack(); 1073 1074 SkMemoryStream* result = new SkMemoryStream; 1075 result->setMemoryOwned(data.detach(), data.getOffset()); 1076 return result; 1077} 1078 1079void SkPDFDevice::createFormXObjectFromDevice( 1080 SkRefPtr<SkPDFFormXObject>* xobject) { 1081 *xobject = new SkPDFFormXObject(this); 1082 (*xobject)->unref(); // SkRefPtr and new both took a reference. 1083 cleanUp(); // Reset this device to have no content. 1084 init(); 1085} 1086 1087void SkPDFDevice::clearClipFromContent(const SkClipStack* clipStack, 1088 const SkRegion& clipRegion) { 1089 if (clipRegion.isEmpty() || isContentEmpty()) { 1090 return; 1091 } 1092 SkRefPtr<SkPDFFormXObject> curContent; 1093 createFormXObjectFromDevice(&curContent); 1094 1095 // Redraw what we already had, but with the clip as a mask. 1096 drawFormXObjectWithClip(curContent.get(), clipStack, clipRegion, true); 1097} 1098 1099void SkPDFDevice::drawFormXObjectWithClip(SkPDFFormXObject* xobject, 1100 const SkClipStack* clipStack, 1101 const SkRegion& clipRegion, 1102 bool invertClip) { 1103 if (clipRegion.isEmpty() && !invertClip) { 1104 return; 1105 } 1106 1107 // Create the mask. 1108 SkMatrix identity; 1109 identity.reset(); 1110 SkDraw draw; 1111 draw.fMatrix = &identity; 1112 draw.fClip = &clipRegion; 1113 draw.fClipStack = clipStack; 1114 SkPaint stockPaint; 1115 this->drawPaint(draw, stockPaint); 1116 SkRefPtr<SkPDFFormXObject> maskFormXObject; 1117 createFormXObjectFromDevice(&maskFormXObject); 1118 SkRefPtr<SkPDFGraphicState> sMaskGS = 1119 SkPDFGraphicState::getSMaskGraphicState(maskFormXObject.get(), 1120 invertClip); 1121 sMaskGS->unref(); // SkRefPtr and getSMaskGraphicState both took a ref. 1122 1123 // Draw the xobject with the clip as a mask. 1124 ContentEntryAccessor content(this, &fExistingClipStack, fExistingClipRegion, 1125 identity, stockPaint); 1126 if (!content.entry()) { 1127 return; 1128 } 1129 SkPDFUtils::ApplyGraphicState(addGraphicStateResource(sMaskGS.get()), 1130 &content.entry()->fContent); 1131 SkPDFUtils::DrawFormXObject(fXObjectResources.count(), 1132 &content.entry()->fContent); 1133 fXObjectResources.push(xobject); 1134 xobject->ref(); 1135 1136 sMaskGS = SkPDFGraphicState::getNoSMaskGraphicState(); 1137 sMaskGS->unref(); // SkRefPtr and getSMaskGraphicState both took a ref. 1138 SkPDFUtils::ApplyGraphicState(addGraphicStateResource(sMaskGS.get()), 1139 &content.entry()->fContent); 1140} 1141 1142ContentEntry* SkPDFDevice::setUpContentEntry(const SkClipStack* clipStack, 1143 const SkRegion& clipRegion, 1144 const SkMatrix& matrix, 1145 const SkPaint& paint, 1146 bool hasText, 1147 SkRefPtr<SkPDFFormXObject>* dst) { 1148 if (clipRegion.isEmpty()) { 1149 return NULL; 1150 } 1151 1152 // The clip stack can come from an SkDraw where it is technically optional. 1153 SkClipStack synthesizedClipStack; 1154 if (clipStack == NULL) { 1155 if (clipRegion == fExistingClipRegion) { 1156 clipStack = &fExistingClipStack; 1157 } else { 1158 // GraphicStackState::updateClip expects the clip stack to have 1159 // fExistingClip as a prefix, so start there, then set the clip 1160 // to the passed region. 1161 synthesizedClipStack = fExistingClipStack; 1162 SkPath clipPath; 1163 clipRegion.getBoundaryPath(&clipPath); 1164 synthesizedClipStack.clipDevPath(clipPath, SkRegion::kReplace_Op); 1165 clipStack = &synthesizedClipStack; 1166 } 1167 } 1168 1169 SkXfermode::Mode xfermode = SkXfermode::kSrcOver_Mode; 1170 if (paint.getXfermode()) { 1171 paint.getXfermode()->asMode(&xfermode); 1172 } 1173 1174 if (xfermode == SkXfermode::kClear_Mode || 1175 xfermode == SkXfermode::kSrc_Mode) { 1176 this->clearClipFromContent(clipStack, clipRegion); 1177 } else if (xfermode == SkXfermode::kSrcIn_Mode || 1178 xfermode == SkXfermode::kDstIn_Mode || 1179 xfermode == SkXfermode::kSrcOut_Mode || 1180 xfermode == SkXfermode::kDstOut_Mode) { 1181 // For the following modes, we use both source and destination, but 1182 // we use one as a smask for the other, so we have to make form xobjects 1183 // out of both of them: SrcIn, DstIn, SrcOut, DstOut. 1184 if (isContentEmpty()) { 1185 return NULL; 1186 } else { 1187 createFormXObjectFromDevice(dst); 1188 } 1189 } 1190 // TODO(vandebo) Figure out how/if we can handle the following modes: 1191 // SrcAtop, DestAtop, Xor, Plus. 1192 1193 // These xfer modes don't draw source at all. 1194 if (xfermode == SkXfermode::kClear_Mode || 1195 xfermode == SkXfermode::kDst_Mode) { 1196 return NULL; 1197 } 1198 1199 ContentEntry* entry; 1200 SkTScopedPtr<ContentEntry> newEntry; 1201 if (fLastContentEntry && fLastContentEntry->fContent.getOffset() == 0) { 1202 entry = fLastContentEntry; 1203 } else { 1204 newEntry.reset(new ContentEntry); 1205 entry = newEntry.get(); 1206 } 1207 1208 populateGraphicStateEntryFromPaint(matrix, *clipStack, clipRegion, paint, 1209 hasText, &entry->fState); 1210 if (fLastContentEntry && xfermode != SkXfermode::kDstOver_Mode && 1211 entry->fState.compareInitialState(fLastContentEntry->fState)) { 1212 return fLastContentEntry; 1213 } 1214 1215 if (!fLastContentEntry) { 1216 fContentEntries.reset(entry); 1217 fLastContentEntry = entry; 1218 } else if (xfermode == SkXfermode::kDstOver_Mode) { 1219 entry->fNext.reset(fContentEntries.release()); 1220 fContentEntries.reset(entry); 1221 } else { 1222 fLastContentEntry->fNext.reset(entry); 1223 fLastContentEntry = entry; 1224 } 1225 newEntry.release(); 1226 return entry; 1227} 1228 1229void SkPDFDevice::finishContentEntry(const SkXfermode::Mode xfermode, 1230 SkPDFFormXObject* dst) { 1231 if (xfermode != SkXfermode::kSrcIn_Mode && 1232 xfermode != SkXfermode::kDstIn_Mode && 1233 xfermode != SkXfermode::kSrcOut_Mode && 1234 xfermode != SkXfermode::kDstOut_Mode) { 1235 SkASSERT(!dst); 1236 return; 1237 } 1238 SkASSERT(dst); 1239 SkASSERT(!fContentEntries->fNext.get()); 1240 1241 // We have to make a copy of these here because changing the current 1242 // content into a form xobject will destroy them. 1243 SkClipStack clipStack = fContentEntries->fState.fClipStack; 1244 SkRegion clipRegion = fContentEntries->fState.fClipRegion; 1245 1246 SkRefPtr<SkPDFFormXObject> srcFormXObject; 1247 if (!isContentEmpty()) { 1248 createFormXObjectFromDevice(&srcFormXObject); 1249 } 1250 1251 drawFormXObjectWithClip(dst, &clipStack, clipRegion, true); 1252 1253 // We've redrawn dst minus the clip area, if there's no src, we're done. 1254 if (!srcFormXObject.get()) { 1255 return; 1256 } 1257 1258 SkMatrix identity; 1259 identity.reset(); 1260 SkPaint stockPaint; 1261 ContentEntryAccessor inClipContentEntry(this, &fExistingClipStack, 1262 fExistingClipRegion, identity, 1263 stockPaint); 1264 if (!inClipContentEntry.entry()) { 1265 return; 1266 } 1267 1268 SkRefPtr<SkPDFGraphicState> sMaskGS; 1269 if (xfermode == SkXfermode::kSrcIn_Mode || 1270 xfermode == SkXfermode::kSrcOut_Mode) { 1271 sMaskGS = SkPDFGraphicState::getSMaskGraphicState( 1272 dst, xfermode == SkXfermode::kSrcOut_Mode); 1273 fXObjectResources.push(srcFormXObject.get()); 1274 srcFormXObject->ref(); 1275 } else { 1276 sMaskGS = SkPDFGraphicState::getSMaskGraphicState( 1277 srcFormXObject.get(), xfermode == SkXfermode::kDstOut_Mode); 1278 // dst already added to fXObjectResources in drawFormXObjectWithClip. 1279 } 1280 sMaskGS->unref(); // SkRefPtr and getSMaskGraphicState both took a ref. 1281 SkPDFUtils::ApplyGraphicState(addGraphicStateResource(sMaskGS.get()), 1282 &inClipContentEntry.entry()->fContent); 1283 1284 SkPDFUtils::DrawFormXObject(fXObjectResources.count() - 1, 1285 &inClipContentEntry.entry()->fContent); 1286 1287 sMaskGS = SkPDFGraphicState::getNoSMaskGraphicState(); 1288 sMaskGS->unref(); // SkRefPtr and getSMaskGraphicState both took a ref. 1289 SkPDFUtils::ApplyGraphicState(addGraphicStateResource(sMaskGS.get()), 1290 &inClipContentEntry.entry()->fContent); 1291} 1292 1293bool SkPDFDevice::isContentEmpty() { 1294 if (!fContentEntries.get() || fContentEntries->fContent.getOffset() == 0) { 1295 SkASSERT(!fContentEntries.get() || !fContentEntries->fNext.get()); 1296 return true; 1297 } 1298 return false; 1299} 1300 1301 1302void SkPDFDevice::populateGraphicStateEntryFromPaint( 1303 const SkMatrix& matrix, 1304 const SkClipStack& clipStack, 1305 const SkRegion& clipRegion, 1306 const SkPaint& paint, 1307 bool hasText, 1308 GraphicStateEntry* entry) { 1309 SkASSERT(paint.getPathEffect() == NULL); 1310 1311 NOT_IMPLEMENTED(paint.getMaskFilter() != NULL, false); 1312 NOT_IMPLEMENTED(paint.getColorFilter() != NULL, false); 1313 1314 entry->fMatrix = matrix; 1315 entry->fClipStack = clipStack; 1316 entry->fClipRegion = clipRegion; 1317 1318 // PDF treats a shader as a color, so we only set one or the other. 1319 SkRefPtr<SkPDFShader> pdfShader; 1320 const SkShader* shader = paint.getShader(); 1321 SkColor color = paint.getColor(); 1322 if (shader) { 1323 // PDF positions patterns relative to the initial transform, so 1324 // we need to apply the current transform to the shader parameters. 1325 SkMatrix transform = matrix; 1326 transform.postConcat(fInitialTransform); 1327 1328 // PDF doesn't support kClamp_TileMode, so we simulate it by making 1329 // a pattern the size of the current clip. 1330 SkIRect bounds = clipRegion.getBounds(); 1331 pdfShader = SkPDFShader::getPDFShader(*shader, transform, bounds); 1332 SkSafeUnref(pdfShader.get()); // getShader and SkRefPtr both took a ref 1333 1334 // A color shader is treated as an invalid shader so we don't have 1335 // to set a shader just for a color. 1336 if (pdfShader.get() == NULL) { 1337 entry->fColor = 0; 1338 color = 0; 1339 1340 // Check for a color shader. 1341 SkShader::GradientInfo gradientInfo; 1342 SkColor gradientColor; 1343 gradientInfo.fColors = &gradientColor; 1344 gradientInfo.fColorOffsets = NULL; 1345 gradientInfo.fColorCount = 1; 1346 if (shader->asAGradient(&gradientInfo) == 1347 SkShader::kColor_GradientType) { 1348 entry->fColor = SkColorSetA(gradientColor, 0xFF); 1349 color = gradientColor; 1350 } 1351 } 1352 } 1353 1354 if (pdfShader) { 1355 // pdfShader has been canonicalized so we can directly compare 1356 // pointers. 1357 int resourceIndex = fShaderResources.find(pdfShader.get()); 1358 if (resourceIndex < 0) { 1359 resourceIndex = fShaderResources.count(); 1360 fShaderResources.push(pdfShader.get()); 1361 pdfShader->ref(); 1362 } 1363 entry->fShaderIndex = resourceIndex; 1364 } else { 1365 entry->fShaderIndex = -1; 1366 entry->fColor = SkColorSetA(paint.getColor(), 0xFF); 1367 color = paint.getColor(); 1368 } 1369 1370 SkRefPtr<SkPDFGraphicState> newGraphicState; 1371 if (color == paint.getColor()) { 1372 newGraphicState = SkPDFGraphicState::getGraphicStateForPaint(paint); 1373 } else { 1374 SkPaint newPaint = paint; 1375 newPaint.setColor(color); 1376 newGraphicState = SkPDFGraphicState::getGraphicStateForPaint(newPaint); 1377 } 1378 newGraphicState->unref(); // getGraphicState and SkRefPtr both took a ref. 1379 int resourceIndex = addGraphicStateResource(newGraphicState.get()); 1380 entry->fGraphicStateIndex = resourceIndex; 1381 1382 if (hasText) { 1383 entry->fTextScaleX = paint.getTextScaleX(); 1384 entry->fTextFill = paint.getStyle(); 1385 } else { 1386 entry->fTextScaleX = 0; 1387 } 1388} 1389 1390int SkPDFDevice::addGraphicStateResource(SkPDFGraphicState* gs) { 1391 // Assumes that gs has been canonicalized (so we can directly compare 1392 // pointers). 1393 int result = fGraphicStateResources.find(gs); 1394 if (result < 0) { 1395 result = fGraphicStateResources.count(); 1396 fGraphicStateResources.push(gs); 1397 gs->ref(); 1398 } 1399 return result; 1400} 1401 1402void SkPDFDevice::updateFont(const SkPaint& paint, uint16_t glyphID, 1403 ContentEntry* contentEntry) { 1404 SkTypeface* typeface = paint.getTypeface(); 1405 if (contentEntry->fState.fFont == NULL || 1406 contentEntry->fState.fTextSize != paint.getTextSize() || 1407 !contentEntry->fState.fFont->hasGlyph(glyphID)) { 1408 int fontIndex = getFontResourceIndex(typeface, glyphID); 1409 contentEntry->fContent.writeText("/F"); 1410 contentEntry->fContent.writeDecAsText(fontIndex); 1411 contentEntry->fContent.writeText(" "); 1412 SkPDFScalar::Append(paint.getTextSize(), &contentEntry->fContent); 1413 contentEntry->fContent.writeText(" Tf\n"); 1414 contentEntry->fState.fFont = fFontResources[fontIndex]; 1415 } 1416} 1417 1418int SkPDFDevice::getFontResourceIndex(SkTypeface* typeface, uint16_t glyphID) { 1419 SkRefPtr<SkPDFFont> newFont = SkPDFFont::getFontResource(typeface, glyphID); 1420 newFont->unref(); // getFontResource and SkRefPtr both took a ref. 1421 int resourceIndex = fFontResources.find(newFont.get()); 1422 if (resourceIndex < 0) { 1423 resourceIndex = fFontResources.count(); 1424 fFontResources.push(newFont.get()); 1425 newFont->ref(); 1426 } 1427 return resourceIndex; 1428} 1429 1430void SkPDFDevice::internalDrawBitmap(const SkMatrix& matrix, 1431 const SkClipStack* clipStack, 1432 const SkRegion& clipRegion, 1433 const SkBitmap& bitmap, 1434 const SkIRect* srcRect, 1435 const SkPaint& paint) { 1436 SkMatrix scaled; 1437 // Adjust for origin flip. 1438 scaled.setScale(1, -1); 1439 scaled.postTranslate(0, 1); 1440 // Scale the image up from 1x1 to WxH. 1441 SkIRect subset = SkIRect::MakeWH(bitmap.width(), bitmap.height()); 1442 scaled.postScale(SkIntToScalar(subset.width()), 1443 SkIntToScalar(subset.height())); 1444 scaled.postConcat(matrix); 1445 ContentEntryAccessor content(this, clipStack, clipRegion, scaled, paint); 1446 if (!content.entry()) { 1447 return; 1448 } 1449 1450 if (srcRect && !subset.intersect(*srcRect)) { 1451 return; 1452 } 1453 1454 SkPDFImage* image = SkPDFImage::CreateImage(bitmap, subset, paint); 1455 if (!image) { 1456 return; 1457 } 1458 1459 fXObjectResources.push(image); // Transfer reference. 1460 SkPDFUtils::DrawFormXObject(fXObjectResources.count() - 1, 1461 &content.entry()->fContent); 1462} 1463