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