SkPDFDevice.cpp revision 15a140599942f70e47380e3f700a825c7cece3b4
1/* 2 * Copyright 2011 Google Inc. 3 * 4 * Use of this source code is governed by a BSD-style license that can be 5 * found in the LICENSE file. 6 */ 7 8#include "SkPDFDevice.h" 9 10#include "SkAnnotation.h" 11#include "SkColor.h" 12#include "SkClipStack.h" 13#include "SkData.h" 14#include "SkDraw.h" 15#include "SkFontHost.h" 16#include "SkGlyphCache.h" 17#include "SkPaint.h" 18#include "SkPath.h" 19#include "SkPathOps.h" 20#include "SkPDFFont.h" 21#include "SkPDFFormXObject.h" 22#include "SkPDFGraphicState.h" 23#include "SkPDFImage.h" 24#include "SkPDFResourceDict.h" 25#include "SkPDFShader.h" 26#include "SkPDFStream.h" 27#include "SkPDFTypes.h" 28#include "SkPDFUtils.h" 29#include "SkRect.h" 30#include "SkRRect.h" 31#include "SkString.h" 32#include "SkTextFormatParams.h" 33#include "SkTemplates.h" 34#include "SkTypefacePriv.h" 35#include "SkTSet.h" 36 37#ifdef SK_BUILD_FOR_ANDROID 38#include "SkTypeface_android.h" 39 40struct TypefaceFallbackData { 41 SkTypeface* typeface; 42 int lowerBounds; 43 int upperBounds; 44 45 bool operator==(const TypefaceFallbackData& b) const { 46 return typeface == b.typeface && 47 lowerBounds == b.lowerBounds && 48 upperBounds == b.upperBounds; 49 } 50}; 51#endif 52 53#define DPI_FOR_RASTER_SCALE_ONE 72 54 55// Utility functions 56 57static void emit_pdf_color(SkColor color, SkWStream* result) { 58 SkASSERT(SkColorGetA(color) == 0xFF); // We handle alpha elsewhere. 59 SkScalar colorMax = SkIntToScalar(0xFF); 60 SkPDFScalar::Append( 61 SkScalarDiv(SkIntToScalar(SkColorGetR(color)), colorMax), result); 62 result->writeText(" "); 63 SkPDFScalar::Append( 64 SkScalarDiv(SkIntToScalar(SkColorGetG(color)), colorMax), result); 65 result->writeText(" "); 66 SkPDFScalar::Append( 67 SkScalarDiv(SkIntToScalar(SkColorGetB(color)), colorMax), result); 68 result->writeText(" "); 69} 70 71static SkPaint calculate_text_paint(const SkPaint& paint) { 72 SkPaint result = paint; 73 if (result.isFakeBoldText()) { 74 SkScalar fakeBoldScale = SkScalarInterpFunc(result.getTextSize(), 75 kStdFakeBoldInterpKeys, 76 kStdFakeBoldInterpValues, 77 kStdFakeBoldInterpLength); 78 SkScalar width = SkScalarMul(result.getTextSize(), fakeBoldScale); 79 if (result.getStyle() == SkPaint::kFill_Style) { 80 result.setStyle(SkPaint::kStrokeAndFill_Style); 81 } else { 82 width += result.getStrokeWidth(); 83 } 84 result.setStrokeWidth(width); 85 } 86 return result; 87} 88 89// Stolen from measure_text in SkDraw.cpp and then tweaked. 90static void align_text(SkDrawCacheProc glyphCacheProc, const SkPaint& paint, 91 const uint16_t* glyphs, size_t len, 92 SkScalar* x, SkScalar* y) { 93 if (paint.getTextAlign() == SkPaint::kLeft_Align) { 94 return; 95 } 96 97 SkMatrix ident; 98 ident.reset(); 99 SkAutoGlyphCache autoCache(paint, NULL, &ident); 100 SkGlyphCache* cache = autoCache.getCache(); 101 102 const char* start = reinterpret_cast<const char*>(glyphs); 103 const char* stop = reinterpret_cast<const char*>(glyphs + len); 104 SkFixed xAdv = 0, yAdv = 0; 105 106 // TODO(vandebo): This probably needs to take kerning into account. 107 while (start < stop) { 108 const SkGlyph& glyph = glyphCacheProc(cache, &start, 0, 0); 109 xAdv += glyph.fAdvanceX; 110 yAdv += glyph.fAdvanceY; 111 }; 112 if (paint.getTextAlign() == SkPaint::kLeft_Align) { 113 return; 114 } 115 116 SkScalar xAdj = SkFixedToScalar(xAdv); 117 SkScalar yAdj = SkFixedToScalar(yAdv); 118 if (paint.getTextAlign() == SkPaint::kCenter_Align) { 119 xAdj = SkScalarHalf(xAdj); 120 yAdj = SkScalarHalf(yAdj); 121 } 122 *x = *x - xAdj; 123 *y = *y - yAdj; 124} 125 126static int max_glyphid_for_typeface(SkTypeface* typeface) { 127 SkAutoResolveDefaultTypeface autoResolve(typeface); 128 typeface = autoResolve.get(); 129 return typeface->countGlyphs() - 1; 130} 131 132typedef SkAutoSTMalloc<128, uint16_t> SkGlyphStorage; 133 134static size_t force_glyph_encoding(const SkPaint& paint, const void* text, 135 size_t len, SkGlyphStorage* storage, 136 uint16_t** glyphIDs) { 137 // Make sure we have a glyph id encoding. 138 if (paint.getTextEncoding() != SkPaint::kGlyphID_TextEncoding) { 139 size_t numGlyphs = paint.textToGlyphs(text, len, NULL); 140 storage->reset(numGlyphs); 141 paint.textToGlyphs(text, len, storage->get()); 142 *glyphIDs = storage->get(); 143 return numGlyphs; 144 } 145 146 // For user supplied glyph ids we need to validate them. 147 SkASSERT((len & 1) == 0); 148 size_t numGlyphs = len / 2; 149 const uint16_t* input = 150 reinterpret_cast<uint16_t*>(const_cast<void*>((text))); 151 152 int maxGlyphID = max_glyphid_for_typeface(paint.getTypeface()); 153 size_t validated; 154 for (validated = 0; validated < numGlyphs; ++validated) { 155 if (input[validated] > maxGlyphID) { 156 break; 157 } 158 } 159 if (validated >= numGlyphs) { 160 *glyphIDs = reinterpret_cast<uint16_t*>(const_cast<void*>((text))); 161 return numGlyphs; 162 } 163 164 // Silently drop anything out of range. 165 storage->reset(numGlyphs); 166 if (validated > 0) { 167 memcpy(storage->get(), input, validated * sizeof(uint16_t)); 168 } 169 170 for (size_t i = validated; i < numGlyphs; ++i) { 171 storage->get()[i] = input[i]; 172 if (input[i] > maxGlyphID) { 173 storage->get()[i] = 0; 174 } 175 } 176 *glyphIDs = storage->get(); 177 return numGlyphs; 178} 179 180static void set_text_transform(SkScalar x, SkScalar y, SkScalar textSkewX, 181 SkWStream* content) { 182 // Flip the text about the x-axis to account for origin swap and include 183 // the passed parameters. 184 content->writeText("1 0 "); 185 SkPDFScalar::Append(0 - textSkewX, content); 186 content->writeText(" -1 "); 187 SkPDFScalar::Append(x, content); 188 content->writeText(" "); 189 SkPDFScalar::Append(y, content); 190 content->writeText(" Tm\n"); 191} 192 193// It is important to not confuse GraphicStateEntry with SkPDFGraphicState, the 194// later being our representation of an object in the PDF file. 195struct GraphicStateEntry { 196 GraphicStateEntry(); 197 198 // Compare the fields we care about when setting up a new content entry. 199 bool compareInitialState(const GraphicStateEntry& b); 200 201 SkMatrix fMatrix; 202 // We can't do set operations on Paths, though PDF natively supports 203 // intersect. If the clip stack does anything other than intersect, 204 // we have to fall back to the region. Treat fClipStack as authoritative. 205 // See http://code.google.com/p/skia/issues/detail?id=221 206 SkClipStack fClipStack; 207 SkRegion fClipRegion; 208 209 // When emitting the content entry, we will ensure the graphic state 210 // is set to these values first. 211 SkColor fColor; 212 SkScalar fTextScaleX; // Zero means we don't care what the value is. 213 SkPaint::Style fTextFill; // Only if TextScaleX is non-zero. 214 int fShaderIndex; 215 int fGraphicStateIndex; 216 217 // We may change the font (i.e. for Type1 support) within a 218 // ContentEntry. This is the one currently in effect, or NULL if none. 219 SkPDFFont* fFont; 220 // In PDF, text size has no default value. It is only valid if fFont is 221 // not NULL. 222 SkScalar fTextSize; 223}; 224 225GraphicStateEntry::GraphicStateEntry() : fColor(SK_ColorBLACK), 226 fTextScaleX(SK_Scalar1), 227 fTextFill(SkPaint::kFill_Style), 228 fShaderIndex(-1), 229 fGraphicStateIndex(-1), 230 fFont(NULL), 231 fTextSize(SK_ScalarNaN) { 232 fMatrix.reset(); 233} 234 235bool GraphicStateEntry::compareInitialState(const GraphicStateEntry& cur) { 236 return fColor == cur.fColor && 237 fShaderIndex == cur.fShaderIndex && 238 fGraphicStateIndex == cur.fGraphicStateIndex && 239 fMatrix == cur.fMatrix && 240 fClipStack == cur.fClipStack && 241 (fTextScaleX == 0 || 242 (fTextScaleX == cur.fTextScaleX && fTextFill == cur.fTextFill)); 243} 244 245class GraphicStackState { 246public: 247 GraphicStackState(const SkClipStack& existingClipStack, 248 const SkRegion& existingClipRegion, 249 SkWStream* contentStream) 250 : fStackDepth(0), 251 fContentStream(contentStream) { 252 fEntries[0].fClipStack = existingClipStack; 253 fEntries[0].fClipRegion = existingClipRegion; 254 } 255 256 void updateClip(const SkClipStack& clipStack, const SkRegion& clipRegion, 257 const SkPoint& translation); 258 void updateMatrix(const SkMatrix& matrix); 259 void updateDrawingState(const GraphicStateEntry& state); 260 261 void drainStack(); 262 263private: 264 void push(); 265 void pop(); 266 GraphicStateEntry* currentEntry() { return &fEntries[fStackDepth]; } 267 268 // Conservative limit on save depth, see impl. notes in PDF 1.4 spec. 269 static const int kMaxStackDepth = 12; 270 GraphicStateEntry fEntries[kMaxStackDepth + 1]; 271 int fStackDepth; 272 SkWStream* fContentStream; 273}; 274 275void GraphicStackState::drainStack() { 276 while (fStackDepth) { 277 pop(); 278 } 279} 280 281void GraphicStackState::push() { 282 SkASSERT(fStackDepth < kMaxStackDepth); 283 fContentStream->writeText("q\n"); 284 fStackDepth++; 285 fEntries[fStackDepth] = fEntries[fStackDepth - 1]; 286} 287 288void GraphicStackState::pop() { 289 SkASSERT(fStackDepth > 0); 290 fContentStream->writeText("Q\n"); 291 fStackDepth--; 292} 293 294// This function initializes iter to be an iterator on the "stack" argument 295// and then skips over the leading entries as specified in prefix. It requires 296// and asserts that "prefix" will be a prefix to "stack." 297static void skip_clip_stack_prefix(const SkClipStack& prefix, 298 const SkClipStack& stack, 299 SkClipStack::Iter* iter) { 300 SkClipStack::B2TIter prefixIter(prefix); 301 iter->reset(stack, SkClipStack::Iter::kBottom_IterStart); 302 303 const SkClipStack::Element* prefixEntry; 304 const SkClipStack::Element* iterEntry; 305 306 for (prefixEntry = prefixIter.next(); prefixEntry; 307 prefixEntry = prefixIter.next()) { 308 iterEntry = iter->next(); 309 SkASSERT(iterEntry); 310 // Because of SkClipStack does internal intersection, the last clip 311 // entry may differ. 312 if (*prefixEntry != *iterEntry) { 313 SkASSERT(prefixEntry->getOp() == SkRegion::kIntersect_Op); 314 SkASSERT(iterEntry->getOp() == SkRegion::kIntersect_Op); 315 SkASSERT(iterEntry->getType() == prefixEntry->getType()); 316 // back up the iterator by one 317 iter->prev(); 318 prefixEntry = prefixIter.next(); 319 break; 320 } 321 } 322 323 SkASSERT(prefixEntry == NULL); 324} 325 326static void emit_clip(SkPath* clipPath, SkRect* clipRect, 327 SkWStream* contentStream) { 328 SkASSERT(clipPath || clipRect); 329 330 SkPath::FillType clipFill; 331 if (clipPath) { 332 SkPDFUtils::EmitPath(*clipPath, SkPaint::kFill_Style, contentStream); 333 clipFill = clipPath->getFillType(); 334 } else { 335 SkPDFUtils::AppendRectangle(*clipRect, contentStream); 336 clipFill = SkPath::kWinding_FillType; 337 } 338 339 NOT_IMPLEMENTED(clipFill == SkPath::kInverseEvenOdd_FillType, false); 340 NOT_IMPLEMENTED(clipFill == SkPath::kInverseWinding_FillType, false); 341 if (clipFill == SkPath::kEvenOdd_FillType) { 342 contentStream->writeText("W* n\n"); 343 } else { 344 contentStream->writeText("W n\n"); 345 } 346} 347 348#ifdef SK_PDF_USE_PATHOPS 349/* Calculate an inverted path's equivalent non-inverted path, given the 350 * canvas bounds. 351 * outPath may alias with invPath (since this is supported by PathOps). 352 */ 353static bool calculate_inverse_path(const SkRect& bounds, const SkPath& invPath, 354 SkPath* outPath) { 355 SkASSERT(invPath.isInverseFillType()); 356 357 SkPath clipPath; 358 clipPath.addRect(bounds); 359 360 return Op(clipPath, invPath, kIntersect_PathOp, outPath); 361} 362 363// Sanity check the numerical values of the SkRegion ops and PathOps ops 364// enums so region_op_to_pathops_op can do a straight passthrough cast. 365// If these are failing, it may be necessary to make region_op_to_pathops_op 366// do more. 367SK_COMPILE_ASSERT(SkRegion::kDifference_Op == (int)kDifference_PathOp, 368 region_pathop_mismatch); 369SK_COMPILE_ASSERT(SkRegion::kIntersect_Op == (int)kIntersect_PathOp, 370 region_pathop_mismatch); 371SK_COMPILE_ASSERT(SkRegion::kUnion_Op == (int)kUnion_PathOp, 372 region_pathop_mismatch); 373SK_COMPILE_ASSERT(SkRegion::kXOR_Op == (int)kXOR_PathOp, 374 region_pathop_mismatch); 375SK_COMPILE_ASSERT(SkRegion::kReverseDifference_Op == 376 (int)kReverseDifference_PathOp, 377 region_pathop_mismatch); 378 379static SkPathOp region_op_to_pathops_op(SkRegion::Op op) { 380 SkASSERT(op >= 0); 381 SkASSERT(op <= SkRegion::kReverseDifference_Op); 382 return (SkPathOp)op; 383} 384 385/* Uses Path Ops to calculate a vector SkPath clip from a clip stack. 386 * Returns true if successful, or false if not successful. 387 * If successful, the resulting clip is stored in outClipPath. 388 * If not successful, outClipPath is undefined, and a fallback method 389 * should be used. 390 */ 391static bool get_clip_stack_path(const SkMatrix& transform, 392 const SkClipStack& clipStack, 393 const SkRegion& clipRegion, 394 SkPath* outClipPath) { 395 outClipPath->reset(); 396 outClipPath->setFillType(SkPath::kInverseWinding_FillType); 397 398 const SkClipStack::Element* clipEntry; 399 SkClipStack::Iter iter; 400 iter.reset(clipStack, SkClipStack::Iter::kBottom_IterStart); 401 for (clipEntry = iter.next(); clipEntry; clipEntry = iter.next()) { 402 SkPath entryPath; 403 if (SkClipStack::Element::kEmpty_Type == clipEntry->getType()) { 404 outClipPath->reset(); 405 outClipPath->setFillType(SkPath::kInverseWinding_FillType); 406 continue; 407 } else if (SkClipStack::Element::kRect_Type == clipEntry->getType()) { 408 entryPath.addRect(clipEntry->getRect()); 409 } else if (SkClipStack::Element::kPath_Type == clipEntry->getType()) { 410 entryPath = clipEntry->getPath(); 411 } 412 entryPath.transform(transform); 413 414 if (SkRegion::kReplace_Op == clipEntry->getOp()) { 415 *outClipPath = entryPath; 416 } else { 417 SkPathOp op = region_op_to_pathops_op(clipEntry->getOp()); 418 if (!Op(*outClipPath, entryPath, op, outClipPath)) { 419 return false; 420 } 421 } 422 } 423 424 if (outClipPath->isInverseFillType()) { 425 // The bounds are slightly outset to ensure this is correct in the 426 // face of floating-point accuracy and possible SkRegion bitmap 427 // approximations. 428 SkRect clipBounds = SkRect::Make(clipRegion.getBounds()); 429 clipBounds.outset(SK_Scalar1, SK_Scalar1); 430 if (!calculate_inverse_path(clipBounds, *outClipPath, outClipPath)) { 431 return false; 432 } 433 } 434 return true; 435} 436#endif 437 438// TODO(vandebo): Take advantage of SkClipStack::getSaveCount(), the PDF 439// graphic state stack, and the fact that we can know all the clips used 440// on the page to optimize this. 441void GraphicStackState::updateClip(const SkClipStack& clipStack, 442 const SkRegion& clipRegion, 443 const SkPoint& translation) { 444 if (clipStack == currentEntry()->fClipStack) { 445 return; 446 } 447 448 while (fStackDepth > 0) { 449 pop(); 450 if (clipStack == currentEntry()->fClipStack) { 451 return; 452 } 453 } 454 push(); 455 456 currentEntry()->fClipStack = clipStack; 457 currentEntry()->fClipRegion = clipRegion; 458 459 SkMatrix transform; 460 transform.setTranslate(translation.fX, translation.fY); 461 462#ifdef SK_PDF_USE_PATHOPS 463 SkPath clipPath; 464 if (get_clip_stack_path(transform, clipStack, clipRegion, &clipPath)) { 465 emit_clip(&clipPath, NULL, fContentStream); 466 return; 467 } 468#endif 469 // gsState->initialEntry()->fClipStack/Region specifies the clip that has 470 // already been applied. (If this is a top level device, then it specifies 471 // a clip to the content area. If this is a layer, then it specifies 472 // the clip in effect when the layer was created.) There's no need to 473 // reapply that clip; SKCanvas's SkDrawIter will draw anything outside the 474 // initial clip on the parent layer. (This means there's a bug if the user 475 // expands the clip and then uses any xfer mode that uses dst: 476 // http://code.google.com/p/skia/issues/detail?id=228 ) 477 SkClipStack::Iter iter; 478 skip_clip_stack_prefix(fEntries[0].fClipStack, clipStack, &iter); 479 480 // If the clip stack does anything other than intersect or if it uses 481 // an inverse fill type, we have to fall back to the clip region. 482 bool needRegion = false; 483 const SkClipStack::Element* clipEntry; 484 for (clipEntry = iter.next(); clipEntry; clipEntry = iter.next()) { 485 if (clipEntry->getOp() != SkRegion::kIntersect_Op || 486 clipEntry->isInverseFilled()) { 487 needRegion = true; 488 break; 489 } 490 } 491 492 if (needRegion) { 493 SkPath clipPath; 494 SkAssertResult(clipRegion.getBoundaryPath(&clipPath)); 495 emit_clip(&clipPath, NULL, fContentStream); 496 } else { 497 skip_clip_stack_prefix(fEntries[0].fClipStack, clipStack, &iter); 498 const SkClipStack::Element* clipEntry; 499 for (clipEntry = iter.next(); clipEntry; clipEntry = iter.next()) { 500 SkASSERT(clipEntry->getOp() == SkRegion::kIntersect_Op); 501 switch (clipEntry->getType()) { 502 case SkClipStack::Element::kRect_Type: { 503 SkRect translatedClip; 504 transform.mapRect(&translatedClip, clipEntry->getRect()); 505 emit_clip(NULL, &translatedClip, fContentStream); 506 break; 507 } 508 case SkClipStack::Element::kPath_Type: { 509 SkPath translatedPath; 510 clipEntry->getPath().transform(transform, &translatedPath); 511 emit_clip(&translatedPath, NULL, fContentStream); 512 break; 513 } 514 default: 515 SkASSERT(false); 516 } 517 } 518 } 519} 520 521void GraphicStackState::updateMatrix(const SkMatrix& matrix) { 522 if (matrix == currentEntry()->fMatrix) { 523 return; 524 } 525 526 if (currentEntry()->fMatrix.getType() != SkMatrix::kIdentity_Mask) { 527 SkASSERT(fStackDepth > 0); 528 SkASSERT(fEntries[fStackDepth].fClipStack == 529 fEntries[fStackDepth -1].fClipStack); 530 pop(); 531 532 SkASSERT(currentEntry()->fMatrix.getType() == SkMatrix::kIdentity_Mask); 533 } 534 if (matrix.getType() == SkMatrix::kIdentity_Mask) { 535 return; 536 } 537 538 push(); 539 SkPDFUtils::AppendTransform(matrix, fContentStream); 540 currentEntry()->fMatrix = matrix; 541} 542 543void GraphicStackState::updateDrawingState(const GraphicStateEntry& state) { 544 // PDF treats a shader as a color, so we only set one or the other. 545 if (state.fShaderIndex >= 0) { 546 if (state.fShaderIndex != currentEntry()->fShaderIndex) { 547 SkPDFUtils::ApplyPattern(state.fShaderIndex, fContentStream); 548 currentEntry()->fShaderIndex = state.fShaderIndex; 549 } 550 } else { 551 if (state.fColor != currentEntry()->fColor || 552 currentEntry()->fShaderIndex >= 0) { 553 emit_pdf_color(state.fColor, fContentStream); 554 fContentStream->writeText("RG "); 555 emit_pdf_color(state.fColor, fContentStream); 556 fContentStream->writeText("rg\n"); 557 currentEntry()->fColor = state.fColor; 558 currentEntry()->fShaderIndex = -1; 559 } 560 } 561 562 if (state.fGraphicStateIndex != currentEntry()->fGraphicStateIndex) { 563 SkPDFUtils::ApplyGraphicState(state.fGraphicStateIndex, fContentStream); 564 currentEntry()->fGraphicStateIndex = state.fGraphicStateIndex; 565 } 566 567 if (state.fTextScaleX) { 568 if (state.fTextScaleX != currentEntry()->fTextScaleX) { 569 SkScalar pdfScale = SkScalarMul(state.fTextScaleX, 570 SkIntToScalar(100)); 571 SkPDFScalar::Append(pdfScale, fContentStream); 572 fContentStream->writeText(" Tz\n"); 573 currentEntry()->fTextScaleX = state.fTextScaleX; 574 } 575 if (state.fTextFill != currentEntry()->fTextFill) { 576 SK_COMPILE_ASSERT(SkPaint::kFill_Style == 0, enum_must_match_value); 577 SK_COMPILE_ASSERT(SkPaint::kStroke_Style == 1, 578 enum_must_match_value); 579 SK_COMPILE_ASSERT(SkPaint::kStrokeAndFill_Style == 2, 580 enum_must_match_value); 581 fContentStream->writeDecAsText(state.fTextFill); 582 fContentStream->writeText(" Tr\n"); 583 currentEntry()->fTextFill = state.fTextFill; 584 } 585 } 586} 587 588SkBaseDevice* SkPDFDevice::onCreateDevice(const SkImageInfo& info, Usage usage) { 589 SkMatrix initialTransform; 590 initialTransform.reset(); 591 SkISize size = SkISize::Make(info.width(), info.height()); 592 return SkNEW_ARGS(SkPDFDevice, (size, size, initialTransform)); 593} 594 595 596struct ContentEntry { 597 GraphicStateEntry fState; 598 SkDynamicMemoryWStream fContent; 599 SkAutoTDelete<ContentEntry> fNext; 600 601 // If the stack is too deep we could get Stack Overflow. 602 // So we manually destruct the object. 603 ~ContentEntry() { 604 ContentEntry* val = fNext.detach(); 605 while (val != NULL) { 606 ContentEntry* valNext = val->fNext.detach(); 607 // When the destructor is called, fNext is NULL and exits. 608 delete val; 609 val = valNext; 610 } 611 } 612}; 613 614// A helper class to automatically finish a ContentEntry at the end of a 615// drawing method and maintain the state needed between set up and finish. 616class ScopedContentEntry { 617public: 618 ScopedContentEntry(SkPDFDevice* device, const SkDraw& draw, 619 const SkPaint& paint, bool hasText = false) 620 : fDevice(device), 621 fContentEntry(NULL), 622 fXfermode(SkXfermode::kSrcOver_Mode), 623 fDstFormXObject(NULL) { 624 init(draw.fClipStack, *draw.fClip, *draw.fMatrix, paint, hasText); 625 } 626 ScopedContentEntry(SkPDFDevice* device, const SkClipStack* clipStack, 627 const SkRegion& clipRegion, const SkMatrix& matrix, 628 const SkPaint& paint, bool hasText = false) 629 : fDevice(device), 630 fContentEntry(NULL), 631 fXfermode(SkXfermode::kSrcOver_Mode), 632 fDstFormXObject(NULL) { 633 init(clipStack, clipRegion, matrix, paint, hasText); 634 } 635 636 ~ScopedContentEntry() { 637 if (fContentEntry) { 638 SkPath* shape = &fShape; 639 if (shape->isEmpty()) { 640 shape = NULL; 641 } 642 fDevice->finishContentEntry(fXfermode, fDstFormXObject, shape); 643 } 644 SkSafeUnref(fDstFormXObject); 645 } 646 647 ContentEntry* entry() { return fContentEntry; } 648 649 /* Returns true when we explicitly need the shape of the drawing. */ 650 bool needShape() { 651 switch (fXfermode) { 652 case SkXfermode::kClear_Mode: 653 case SkXfermode::kSrc_Mode: 654 case SkXfermode::kSrcIn_Mode: 655 case SkXfermode::kSrcOut_Mode: 656 case SkXfermode::kDstIn_Mode: 657 case SkXfermode::kDstOut_Mode: 658 case SkXfermode::kSrcATop_Mode: 659 case SkXfermode::kDstATop_Mode: 660 case SkXfermode::kModulate_Mode: 661 return true; 662 default: 663 return false; 664 } 665 } 666 667 /* Returns true unless we only need the shape of the drawing. */ 668 bool needSource() { 669 if (fXfermode == SkXfermode::kClear_Mode) { 670 return false; 671 } 672 return true; 673 } 674 675 /* If the shape is different than the alpha component of the content, then 676 * setShape should be called with the shape. In particular, images and 677 * devices have rectangular shape. 678 */ 679 void setShape(const SkPath& shape) { 680 fShape = shape; 681 } 682 683private: 684 SkPDFDevice* fDevice; 685 ContentEntry* fContentEntry; 686 SkXfermode::Mode fXfermode; 687 SkPDFFormXObject* fDstFormXObject; 688 SkPath fShape; 689 690 void init(const SkClipStack* clipStack, const SkRegion& clipRegion, 691 const SkMatrix& matrix, const SkPaint& paint, bool hasText) { 692 // Shape has to be flatten before we get here. 693 if (matrix.hasPerspective()) { 694 NOT_IMPLEMENTED(!matrix.hasPerspective(), false); 695 return; 696 } 697 if (paint.getXfermode()) { 698 paint.getXfermode()->asMode(&fXfermode); 699 } 700 fContentEntry = fDevice->setUpContentEntry(clipStack, clipRegion, 701 matrix, paint, hasText, 702 &fDstFormXObject); 703 } 704}; 705 706//////////////////////////////////////////////////////////////////////////////// 707 708static inline SkBitmap makeContentBitmap(const SkISize& contentSize, 709 const SkMatrix* initialTransform) { 710 SkBitmap bitmap; 711 if (initialTransform) { 712 // Compute the size of the drawing area. 713 SkVector drawingSize; 714 SkMatrix inverse; 715 drawingSize.set(SkIntToScalar(contentSize.fWidth), 716 SkIntToScalar(contentSize.fHeight)); 717 if (!initialTransform->invert(&inverse)) { 718 // This shouldn't happen, initial transform should be invertible. 719 SkASSERT(false); 720 inverse.reset(); 721 } 722 inverse.mapVectors(&drawingSize, 1); 723 SkISize size = SkSize::Make(drawingSize.fX, drawingSize.fY).toRound(); 724 bitmap.setConfig(SkBitmap::kNo_Config, abs(size.fWidth), 725 abs(size.fHeight)); 726 } else { 727 bitmap.setConfig(SkBitmap::kNo_Config, abs(contentSize.fWidth), 728 abs(contentSize.fHeight)); 729 } 730 731 return bitmap; 732} 733 734// TODO(vandebo) change pageSize to SkSize. 735SkPDFDevice::SkPDFDevice(const SkISize& pageSize, const SkISize& contentSize, 736 const SkMatrix& initialTransform) 737 : SkBitmapDevice(makeContentBitmap(contentSize, &initialTransform)), 738 fPageSize(pageSize), 739 fContentSize(contentSize), 740 fLastContentEntry(NULL), 741 fLastMarginContentEntry(NULL), 742 fClipStack(NULL), 743 fEncoder(NULL), 744 fRasterDpi(72.0f) { 745 // Just report that PDF does not supports perspective in the 746 // initial transform. 747 NOT_IMPLEMENTED(initialTransform.hasPerspective(), true); 748 749 // Skia generally uses the top left as the origin but PDF natively has the 750 // origin at the bottom left. This matrix corrects for that. But that only 751 // needs to be done once, we don't do it when layering. 752 fInitialTransform.setTranslate(0, SkIntToScalar(pageSize.fHeight)); 753 fInitialTransform.preScale(SK_Scalar1, -SK_Scalar1); 754 fInitialTransform.preConcat(initialTransform); 755 756 SkIRect existingClip = SkIRect::MakeWH(this->width(), this->height()); 757 fExistingClipRegion.setRect(existingClip); 758 759 this->init(); 760} 761 762// TODO(vandebo) change layerSize to SkSize. 763SkPDFDevice::SkPDFDevice(const SkISize& layerSize, 764 const SkClipStack& existingClipStack, 765 const SkRegion& existingClipRegion) 766 : SkBitmapDevice(makeContentBitmap(layerSize, NULL)), 767 fPageSize(layerSize), 768 fContentSize(layerSize), 769 fExistingClipStack(existingClipStack), 770 fExistingClipRegion(existingClipRegion), 771 fLastContentEntry(NULL), 772 fLastMarginContentEntry(NULL), 773 fClipStack(NULL), 774 fEncoder(NULL), 775 fRasterDpi(72.0f) { 776 fInitialTransform.reset(); 777 this->init(); 778} 779 780SkPDFDevice::~SkPDFDevice() { 781 this->cleanUp(true); 782} 783 784void SkPDFDevice::init() { 785 fAnnotations = NULL; 786 fResourceDict = NULL; 787 fContentEntries.free(); 788 fLastContentEntry = NULL; 789 fMarginContentEntries.free(); 790 fLastMarginContentEntry = NULL; 791 fDrawingArea = kContent_DrawingArea; 792 if (fFontGlyphUsage.get() == NULL) { 793 fFontGlyphUsage.reset(new SkPDFGlyphSetMap()); 794 } 795} 796 797void SkPDFDevice::cleanUp(bool clearFontUsage) { 798 fGraphicStateResources.unrefAll(); 799 fXObjectResources.unrefAll(); 800 fFontResources.unrefAll(); 801 fShaderResources.unrefAll(); 802 SkSafeUnref(fAnnotations); 803 SkSafeUnref(fResourceDict); 804 fNamedDestinations.deleteAll(); 805 806 if (clearFontUsage) { 807 fFontGlyphUsage->reset(); 808 } 809} 810 811uint32_t SkPDFDevice::getDeviceCapabilities() { 812 return kVector_Capability; 813} 814 815void SkPDFDevice::clear(SkColor color) { 816 this->cleanUp(true); 817 this->init(); 818 819 SkPaint paint; 820 paint.setColor(color); 821 paint.setStyle(SkPaint::kFill_Style); 822 SkMatrix identity; 823 identity.reset(); 824 ScopedContentEntry content(this, &fExistingClipStack, fExistingClipRegion, 825 identity, paint); 826 internalDrawPaint(paint, content.entry()); 827} 828 829void SkPDFDevice::drawPaint(const SkDraw& d, const SkPaint& paint) { 830 SkPaint newPaint = paint; 831 newPaint.setStyle(SkPaint::kFill_Style); 832 ScopedContentEntry content(this, d, newPaint); 833 internalDrawPaint(newPaint, content.entry()); 834} 835 836void SkPDFDevice::internalDrawPaint(const SkPaint& paint, 837 ContentEntry* contentEntry) { 838 if (!contentEntry) { 839 return; 840 } 841 SkRect bbox = SkRect::MakeWH(SkIntToScalar(this->width()), 842 SkIntToScalar(this->height())); 843 SkMatrix inverse; 844 if (!contentEntry->fState.fMatrix.invert(&inverse)) { 845 return; 846 } 847 inverse.mapRect(&bbox); 848 849 SkPDFUtils::AppendRectangle(bbox, &contentEntry->fContent); 850 SkPDFUtils::PaintPath(paint.getStyle(), SkPath::kWinding_FillType, 851 &contentEntry->fContent); 852} 853 854void SkPDFDevice::drawPoints(const SkDraw& d, SkCanvas::PointMode mode, 855 size_t count, const SkPoint* points, 856 const SkPaint& passedPaint) { 857 if (count == 0) { 858 return; 859 } 860 861 if (handlePointAnnotation(points, count, *d.fMatrix, passedPaint)) { 862 return; 863 } 864 865 // SkDraw::drawPoints converts to multiple calls to fDevice->drawPath. 866 // We only use this when there's a path effect because of the overhead 867 // of multiple calls to setUpContentEntry it causes. 868 if (passedPaint.getPathEffect()) { 869 if (d.fClip->isEmpty()) { 870 return; 871 } 872 SkDraw pointDraw(d); 873 pointDraw.fDevice = this; 874 pointDraw.drawPoints(mode, count, points, passedPaint, true); 875 return; 876 } 877 878 const SkPaint* paint = &passedPaint; 879 SkPaint modifiedPaint; 880 881 if (mode == SkCanvas::kPoints_PointMode && 882 paint->getStrokeCap() != SkPaint::kRound_Cap) { 883 modifiedPaint = *paint; 884 paint = &modifiedPaint; 885 if (paint->getStrokeWidth()) { 886 // PDF won't draw a single point with square/butt caps because the 887 // orientation is ambiguous. Draw a rectangle instead. 888 modifiedPaint.setStyle(SkPaint::kFill_Style); 889 SkScalar strokeWidth = paint->getStrokeWidth(); 890 SkScalar halfStroke = SkScalarHalf(strokeWidth); 891 for (size_t i = 0; i < count; i++) { 892 SkRect r = SkRect::MakeXYWH(points[i].fX, points[i].fY, 0, 0); 893 r.inset(-halfStroke, -halfStroke); 894 drawRect(d, r, modifiedPaint); 895 } 896 return; 897 } else { 898 modifiedPaint.setStrokeCap(SkPaint::kRound_Cap); 899 } 900 } 901 902 ScopedContentEntry content(this, d, *paint); 903 if (!content.entry()) { 904 return; 905 } 906 907 switch (mode) { 908 case SkCanvas::kPolygon_PointMode: 909 SkPDFUtils::MoveTo(points[0].fX, points[0].fY, 910 &content.entry()->fContent); 911 for (size_t i = 1; i < count; i++) { 912 SkPDFUtils::AppendLine(points[i].fX, points[i].fY, 913 &content.entry()->fContent); 914 } 915 SkPDFUtils::StrokePath(&content.entry()->fContent); 916 break; 917 case SkCanvas::kLines_PointMode: 918 for (size_t i = 0; i < count/2; i++) { 919 SkPDFUtils::MoveTo(points[i * 2].fX, points[i * 2].fY, 920 &content.entry()->fContent); 921 SkPDFUtils::AppendLine(points[i * 2 + 1].fX, 922 points[i * 2 + 1].fY, 923 &content.entry()->fContent); 924 SkPDFUtils::StrokePath(&content.entry()->fContent); 925 } 926 break; 927 case SkCanvas::kPoints_PointMode: 928 SkASSERT(paint->getStrokeCap() == SkPaint::kRound_Cap); 929 for (size_t i = 0; i < count; i++) { 930 SkPDFUtils::MoveTo(points[i].fX, points[i].fY, 931 &content.entry()->fContent); 932 SkPDFUtils::ClosePath(&content.entry()->fContent); 933 SkPDFUtils::StrokePath(&content.entry()->fContent); 934 } 935 break; 936 default: 937 SkASSERT(false); 938 } 939} 940 941void SkPDFDevice::drawRect(const SkDraw& d, const SkRect& rect, 942 const SkPaint& paint) { 943 SkRect r = rect; 944 r.sort(); 945 946 if (paint.getPathEffect()) { 947 if (d.fClip->isEmpty()) { 948 return; 949 } 950 SkPath path; 951 path.addRect(r); 952 drawPath(d, path, paint, NULL, true); 953 return; 954 } 955 956 if (handleRectAnnotation(r, *d.fMatrix, paint)) { 957 return; 958 } 959 960 ScopedContentEntry content(this, d, paint); 961 if (!content.entry()) { 962 return; 963 } 964 SkPDFUtils::AppendRectangle(r, &content.entry()->fContent); 965 SkPDFUtils::PaintPath(paint.getStyle(), SkPath::kWinding_FillType, 966 &content.entry()->fContent); 967} 968 969void SkPDFDevice::drawRRect(const SkDraw& draw, const SkRRect& rrect, 970 const SkPaint& paint) { 971 SkPath path; 972 path.addRRect(rrect); 973 this->drawPath(draw, path, paint, NULL, true); 974} 975 976void SkPDFDevice::drawPath(const SkDraw& d, const SkPath& origPath, 977 const SkPaint& paint, const SkMatrix* prePathMatrix, 978 bool pathIsMutable) { 979 SkPath modifiedPath; 980 SkPath* pathPtr = const_cast<SkPath*>(&origPath); 981 982 SkMatrix matrix = *d.fMatrix; 983 if (prePathMatrix) { 984 if (paint.getPathEffect() || paint.getStyle() != SkPaint::kFill_Style) { 985 if (!pathIsMutable) { 986 pathPtr = &modifiedPath; 987 pathIsMutable = true; 988 } 989 origPath.transform(*prePathMatrix, pathPtr); 990 } else { 991 if (!matrix.preConcat(*prePathMatrix)) { 992 // TODO(edisonn): report somehow why we failed? 993 return; 994 } 995 } 996 } 997 998 if (paint.getPathEffect()) { 999 if (d.fClip->isEmpty()) { 1000 return; 1001 } 1002 if (!pathIsMutable) { 1003 pathPtr = &modifiedPath; 1004 pathIsMutable = true; 1005 } 1006 bool fill = paint.getFillPath(origPath, pathPtr); 1007 1008 SkPaint noEffectPaint(paint); 1009 noEffectPaint.setPathEffect(NULL); 1010 if (fill) { 1011 noEffectPaint.setStyle(SkPaint::kFill_Style); 1012 } else { 1013 noEffectPaint.setStyle(SkPaint::kStroke_Style); 1014 noEffectPaint.setStrokeWidth(0); 1015 } 1016 drawPath(d, *pathPtr, noEffectPaint, NULL, true); 1017 return; 1018 } 1019 1020#ifdef SK_PDF_USE_PATHOPS 1021 if (handleInversePath(d, origPath, paint, pathIsMutable, prePathMatrix)) { 1022 return; 1023 } 1024#endif 1025 1026 if (handleRectAnnotation(pathPtr->getBounds(), matrix, paint)) { 1027 return; 1028 } 1029 1030 ScopedContentEntry content(this, d.fClipStack, *d.fClip, matrix, paint); 1031 if (!content.entry()) { 1032 return; 1033 } 1034 SkPDFUtils::EmitPath(*pathPtr, paint.getStyle(), 1035 &content.entry()->fContent); 1036 SkPDFUtils::PaintPath(paint.getStyle(), pathPtr->getFillType(), 1037 &content.entry()->fContent); 1038} 1039 1040void SkPDFDevice::drawBitmapRect(const SkDraw& draw, const SkBitmap& bitmap, 1041 const SkRect* src, const SkRect& dst, 1042 const SkPaint& paint, 1043 SkCanvas::DrawBitmapRectFlags flags) { 1044 // TODO: this code path must be updated to respect the flags parameter 1045 SkMatrix matrix; 1046 SkRect bitmapBounds, tmpSrc, tmpDst; 1047 SkBitmap tmpBitmap; 1048 1049 bitmapBounds.isetWH(bitmap.width(), bitmap.height()); 1050 1051 // Compute matrix from the two rectangles 1052 if (src) { 1053 tmpSrc = *src; 1054 } else { 1055 tmpSrc = bitmapBounds; 1056 } 1057 matrix.setRectToRect(tmpSrc, dst, SkMatrix::kFill_ScaleToFit); 1058 1059 const SkBitmap* bitmapPtr = &bitmap; 1060 1061 // clip the tmpSrc to the bounds of the bitmap, and recompute dstRect if 1062 // needed (if the src was clipped). No check needed if src==null. 1063 if (src) { 1064 if (!bitmapBounds.contains(*src)) { 1065 if (!tmpSrc.intersect(bitmapBounds)) { 1066 return; // nothing to draw 1067 } 1068 // recompute dst, based on the smaller tmpSrc 1069 matrix.mapRect(&tmpDst, tmpSrc); 1070 } 1071 1072 // since we may need to clamp to the borders of the src rect within 1073 // the bitmap, we extract a subset. 1074 // TODO: make sure this is handled in drawBitmap and remove from here. 1075 SkIRect srcIR; 1076 tmpSrc.roundOut(&srcIR); 1077 if (!bitmap.extractSubset(&tmpBitmap, srcIR)) { 1078 return; 1079 } 1080 bitmapPtr = &tmpBitmap; 1081 1082 // Since we did an extract, we need to adjust the matrix accordingly 1083 SkScalar dx = 0, dy = 0; 1084 if (srcIR.fLeft > 0) { 1085 dx = SkIntToScalar(srcIR.fLeft); 1086 } 1087 if (srcIR.fTop > 0) { 1088 dy = SkIntToScalar(srcIR.fTop); 1089 } 1090 if (dx || dy) { 1091 matrix.preTranslate(dx, dy); 1092 } 1093 } 1094 this->drawBitmap(draw, *bitmapPtr, matrix, paint); 1095} 1096 1097void SkPDFDevice::drawBitmap(const SkDraw& d, const SkBitmap& bitmap, 1098 const SkMatrix& matrix, const SkPaint& paint) { 1099 if (d.fClip->isEmpty()) { 1100 return; 1101 } 1102 1103 SkMatrix transform = matrix; 1104 transform.postConcat(*d.fMatrix); 1105 this->internalDrawBitmap(transform, d.fClipStack, *d.fClip, bitmap, NULL, 1106 paint); 1107} 1108 1109void SkPDFDevice::drawSprite(const SkDraw& d, const SkBitmap& bitmap, 1110 int x, int y, const SkPaint& paint) { 1111 if (d.fClip->isEmpty()) { 1112 return; 1113 } 1114 1115 SkMatrix matrix; 1116 matrix.setTranslate(SkIntToScalar(x), SkIntToScalar(y)); 1117 this->internalDrawBitmap(matrix, d.fClipStack, *d.fClip, bitmap, NULL, 1118 paint); 1119} 1120 1121void SkPDFDevice::drawText(const SkDraw& d, const void* text, size_t len, 1122 SkScalar x, SkScalar y, const SkPaint& paint) { 1123 NOT_IMPLEMENTED(paint.getMaskFilter() != NULL, false); 1124 if (paint.getMaskFilter() != NULL) { 1125 // Don't pretend we support drawing MaskFilters, it makes for artifacts 1126 // making text unreadable (e.g. same text twice when using CSS shadows). 1127 return; 1128 } 1129 SkPaint textPaint = calculate_text_paint(paint); 1130 ScopedContentEntry content(this, d, textPaint, true); 1131 if (!content.entry()) { 1132 return; 1133 } 1134 1135 SkGlyphStorage storage(0); 1136 uint16_t* glyphIDs = NULL; 1137 size_t numGlyphs = force_glyph_encoding(paint, text, len, &storage, 1138 &glyphIDs); 1139 textPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); 1140 1141 SkDrawCacheProc glyphCacheProc = textPaint.getDrawCacheProc(); 1142 align_text(glyphCacheProc, textPaint, glyphIDs, numGlyphs, &x, &y); 1143 content.entry()->fContent.writeText("BT\n"); 1144 set_text_transform(x, y, textPaint.getTextSkewX(), 1145 &content.entry()->fContent); 1146 size_t consumedGlyphCount = 0; 1147 while (numGlyphs > consumedGlyphCount) { 1148 updateFont(textPaint, glyphIDs[consumedGlyphCount], content.entry()); 1149 SkPDFFont* font = content.entry()->fState.fFont; 1150 size_t availableGlyphs = 1151 font->glyphsToPDFFontEncoding(glyphIDs + consumedGlyphCount, 1152 numGlyphs - consumedGlyphCount); 1153 fFontGlyphUsage->noteGlyphUsage(font, glyphIDs + consumedGlyphCount, 1154 availableGlyphs); 1155 SkString encodedString = 1156 SkPDFString::FormatString(glyphIDs + consumedGlyphCount, 1157 availableGlyphs, font->multiByteGlyphs()); 1158 content.entry()->fContent.writeText(encodedString.c_str()); 1159 consumedGlyphCount += availableGlyphs; 1160 content.entry()->fContent.writeText(" Tj\n"); 1161 } 1162 content.entry()->fContent.writeText("ET\n"); 1163} 1164 1165void SkPDFDevice::drawPosText(const SkDraw& d, const void* text, size_t len, 1166 const SkScalar pos[], SkScalar constY, 1167 int scalarsPerPos, const SkPaint& paint) { 1168 NOT_IMPLEMENTED(paint.getMaskFilter() != NULL, false); 1169 if (paint.getMaskFilter() != NULL) { 1170 // Don't pretend we support drawing MaskFilters, it makes for artifacts 1171 // making text unreadable (e.g. same text twice when using CSS shadows). 1172 return; 1173 } 1174 SkASSERT(1 == scalarsPerPos || 2 == scalarsPerPos); 1175 SkPaint textPaint = calculate_text_paint(paint); 1176 ScopedContentEntry content(this, d, textPaint, true); 1177 if (!content.entry()) { 1178 return; 1179 } 1180 1181#ifdef SK_BUILD_FOR_ANDROID 1182 /* 1183 * In the case that we have enabled fallback fonts on Android we need to 1184 * take the following steps to ensure that the PDF draws all characters, 1185 * regardless of their underlying font file, correctly. 1186 * 1187 * 1. Convert input into GlyphID encoding if it currently is not 1188 * 2. Iterate over the glyphIDs and identify the actual typeface that each 1189 * glyph resolves to 1190 * 3. Iterate over those typefaces and recursively call this function with 1191 * only the glyphs (and their positions) that the typeface is capable of 1192 * resolving. 1193 */ 1194 if (paint.getPaintOptionsAndroid().isUsingFontFallbacks()) { 1195 uint16_t* glyphIDs = NULL; 1196 SkGlyphStorage tmpStorage(0); 1197 size_t numGlyphs = 0; 1198 1199 // convert to glyphIDs 1200 if (paint.getTextEncoding() == SkPaint::kGlyphID_TextEncoding) { 1201 numGlyphs = len / 2; 1202 glyphIDs = reinterpret_cast<uint16_t*>(const_cast<void*>(text)); 1203 } else { 1204 numGlyphs = paint.textToGlyphs(text, len, NULL); 1205 tmpStorage.reset(numGlyphs); 1206 paint.textToGlyphs(text, len, tmpStorage.get()); 1207 glyphIDs = tmpStorage.get(); 1208 } 1209 1210 // if no typeface is provided in the paint get the default 1211 SkAutoTUnref<SkTypeface> origFace(SkSafeRef(paint.getTypeface())); 1212 if (NULL == origFace.get()) { 1213 origFace.reset(SkTypeface::RefDefault()); 1214 } 1215 const uint16_t origGlyphCount = origFace->countGlyphs(); 1216 1217 // keep a list of the already visited typefaces and some data about them 1218 SkTDArray<TypefaceFallbackData> visitedTypefaces; 1219 1220 // find all the typefaces needed to resolve this run of text 1221 bool usesOriginalTypeface = false; 1222 for (uint16_t x = 0; x < numGlyphs; ++x) { 1223 // optimization that checks to see if original typeface can resolve 1224 // the glyph 1225 if (glyphIDs[x] < origGlyphCount) { 1226 usesOriginalTypeface = true; 1227 continue; 1228 } 1229 1230 // find the fallback typeface that supports this glyph 1231 TypefaceFallbackData data; 1232 data.typeface = 1233 SkGetTypefaceForGlyphID(glyphIDs[x], origFace.get(), 1234 paint.getPaintOptionsAndroid(), 1235 &data.lowerBounds, 1236 &data.upperBounds); 1237 // add the typeface and its data if we don't have it 1238 if (data.typeface && !visitedTypefaces.contains(data)) { 1239 visitedTypefaces.push(data); 1240 } 1241 } 1242 1243 // if the original font was used then add it to the list as well 1244 if (usesOriginalTypeface) { 1245 TypefaceFallbackData* data = visitedTypefaces.push(); 1246 data->typeface = origFace.get(); 1247 data->lowerBounds = 0; 1248 data->upperBounds = origGlyphCount; 1249 } 1250 1251 // keep a scratch glyph and pos storage 1252 SkAutoTMalloc<SkScalar> posStorage(len * scalarsPerPos); 1253 SkScalar* tmpPos = posStorage.get(); 1254 SkGlyphStorage glyphStorage(numGlyphs); 1255 uint16_t* tmpGlyphIDs = glyphStorage.get(); 1256 1257 // loop through all the valid typefaces, trim the glyphs to only those 1258 // resolved by the typeface, and then draw that run of glyphs 1259 for (int x = 0; x < visitedTypefaces.count(); ++x) { 1260 const TypefaceFallbackData& data = visitedTypefaces[x]; 1261 1262 int tmpGlyphCount = 0; 1263 for (uint16_t y = 0; y < numGlyphs; ++y) { 1264 if (glyphIDs[y] >= data.lowerBounds && 1265 glyphIDs[y] < data.upperBounds) { 1266 tmpGlyphIDs[tmpGlyphCount] = glyphIDs[y] - data.lowerBounds; 1267 memcpy(&(tmpPos[tmpGlyphCount * scalarsPerPos]), 1268 &(pos[y * scalarsPerPos]), 1269 scalarsPerPos * sizeof(SkScalar)); 1270 tmpGlyphCount++; 1271 } 1272 } 1273 1274 // recursively call this function with the right typeface 1275 SkPaint tmpPaint = paint; 1276 tmpPaint.setTypeface(data.typeface); 1277 tmpPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); 1278 1279 // turn off fallback chaining 1280 SkPaintOptionsAndroid paintOpts = tmpPaint.getPaintOptionsAndroid(); 1281 paintOpts.setUseFontFallbacks(false); 1282 tmpPaint.setPaintOptionsAndroid(paintOpts); 1283 1284 this->drawPosText(d, tmpGlyphIDs, tmpGlyphCount * 2, tmpPos, constY, 1285 scalarsPerPos, tmpPaint); 1286 } 1287 return; 1288 } 1289#endif 1290 1291 SkGlyphStorage storage(0); 1292 uint16_t* glyphIDs = NULL; 1293 size_t numGlyphs = force_glyph_encoding(paint, text, len, &storage, 1294 &glyphIDs); 1295 textPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); 1296 1297 SkDrawCacheProc glyphCacheProc = textPaint.getDrawCacheProc(); 1298 content.entry()->fContent.writeText("BT\n"); 1299 updateFont(textPaint, glyphIDs[0], content.entry()); 1300 for (size_t i = 0; i < numGlyphs; i++) { 1301 SkPDFFont* font = content.entry()->fState.fFont; 1302 uint16_t encodedValue = glyphIDs[i]; 1303 if (font->glyphsToPDFFontEncoding(&encodedValue, 1) != 1) { 1304 updateFont(textPaint, glyphIDs[i], content.entry()); 1305 i--; 1306 continue; 1307 } 1308 fFontGlyphUsage->noteGlyphUsage(font, &encodedValue, 1); 1309 SkScalar x = pos[i * scalarsPerPos]; 1310 SkScalar y = scalarsPerPos == 1 ? constY : pos[i * scalarsPerPos + 1]; 1311 align_text(glyphCacheProc, textPaint, glyphIDs + i, 1, &x, &y); 1312 set_text_transform(x, y, textPaint.getTextSkewX(), 1313 &content.entry()->fContent); 1314 SkString encodedString = 1315 SkPDFString::FormatString(&encodedValue, 1, 1316 font->multiByteGlyphs()); 1317 content.entry()->fContent.writeText(encodedString.c_str()); 1318 content.entry()->fContent.writeText(" Tj\n"); 1319 } 1320 content.entry()->fContent.writeText("ET\n"); 1321} 1322 1323void SkPDFDevice::drawTextOnPath(const SkDraw& d, const void* text, size_t len, 1324 const SkPath& path, const SkMatrix* matrix, 1325 const SkPaint& paint) { 1326 if (d.fClip->isEmpty()) { 1327 return; 1328 } 1329 d.drawTextOnPath((const char*)text, len, path, matrix, paint); 1330} 1331 1332void SkPDFDevice::drawVertices(const SkDraw& d, SkCanvas::VertexMode, 1333 int vertexCount, const SkPoint verts[], 1334 const SkPoint texs[], const SkColor colors[], 1335 SkXfermode* xmode, const uint16_t indices[], 1336 int indexCount, const SkPaint& paint) { 1337 if (d.fClip->isEmpty()) { 1338 return; 1339 } 1340 // TODO: implement drawVertices 1341} 1342 1343void SkPDFDevice::drawDevice(const SkDraw& d, SkBaseDevice* device, 1344 int x, int y, const SkPaint& paint) { 1345 if ((device->getDeviceCapabilities() & kVector_Capability) == 0) { 1346 // If we somehow get a raster device, do what our parent would do. 1347 INHERITED::drawDevice(d, device, x, y, paint); 1348 return; 1349 } 1350 1351 // Assume that a vector capable device means that it's a PDF Device. 1352 SkPDFDevice* pdfDevice = static_cast<SkPDFDevice*>(device); 1353 if (pdfDevice->isContentEmpty()) { 1354 return; 1355 } 1356 1357 SkMatrix matrix; 1358 matrix.setTranslate(SkIntToScalar(x), SkIntToScalar(y)); 1359 ScopedContentEntry content(this, d.fClipStack, *d.fClip, matrix, paint); 1360 if (!content.entry()) { 1361 return; 1362 } 1363 if (content.needShape()) { 1364 SkPath shape; 1365 shape.addRect(SkRect::MakeXYWH(SkIntToScalar(x), SkIntToScalar(y), 1366 SkIntToScalar(device->width()), 1367 SkIntToScalar(device->height()))); 1368 content.setShape(shape); 1369 } 1370 if (!content.needSource()) { 1371 return; 1372 } 1373 1374 SkAutoTUnref<SkPDFFormXObject> xObject(new SkPDFFormXObject(pdfDevice)); 1375 SkPDFUtils::DrawFormXObject(this->addXObjectResource(xObject.get()), 1376 &content.entry()->fContent); 1377 1378 // Merge glyph sets from the drawn device. 1379 fFontGlyphUsage->merge(pdfDevice->getFontGlyphUsage()); 1380} 1381 1382void SkPDFDevice::onAttachToCanvas(SkCanvas* canvas) { 1383 INHERITED::onAttachToCanvas(canvas); 1384 1385 // Canvas promises that this ptr is valid until onDetachFromCanvas is called 1386 fClipStack = canvas->getClipStack(); 1387} 1388 1389void SkPDFDevice::onDetachFromCanvas() { 1390 INHERITED::onDetachFromCanvas(); 1391 1392 fClipStack = NULL; 1393} 1394 1395ContentEntry* SkPDFDevice::getLastContentEntry() { 1396 if (fDrawingArea == kContent_DrawingArea) { 1397 return fLastContentEntry; 1398 } else { 1399 return fLastMarginContentEntry; 1400 } 1401} 1402 1403SkAutoTDelete<ContentEntry>* SkPDFDevice::getContentEntries() { 1404 if (fDrawingArea == kContent_DrawingArea) { 1405 return &fContentEntries; 1406 } else { 1407 return &fMarginContentEntries; 1408 } 1409} 1410 1411void SkPDFDevice::setLastContentEntry(ContentEntry* contentEntry) { 1412 if (fDrawingArea == kContent_DrawingArea) { 1413 fLastContentEntry = contentEntry; 1414 } else { 1415 fLastMarginContentEntry = contentEntry; 1416 } 1417} 1418 1419void SkPDFDevice::setDrawingArea(DrawingArea drawingArea) { 1420 // A ScopedContentEntry only exists during the course of a draw call, so 1421 // this can't be called while a ScopedContentEntry exists. 1422 fDrawingArea = drawingArea; 1423} 1424 1425SkPDFResourceDict* SkPDFDevice::getResourceDict() { 1426 if (NULL == fResourceDict) { 1427 fResourceDict = SkNEW(SkPDFResourceDict); 1428 1429 if (fGraphicStateResources.count()) { 1430 for (int i = 0; i < fGraphicStateResources.count(); i++) { 1431 fResourceDict->insertResourceAsReference( 1432 SkPDFResourceDict::kExtGState_ResourceType, 1433 i, fGraphicStateResources[i]); 1434 } 1435 } 1436 1437 if (fXObjectResources.count()) { 1438 for (int i = 0; i < fXObjectResources.count(); i++) { 1439 fResourceDict->insertResourceAsReference( 1440 SkPDFResourceDict::kXObject_ResourceType, 1441 i, fXObjectResources[i]); 1442 } 1443 } 1444 1445 if (fFontResources.count()) { 1446 for (int i = 0; i < fFontResources.count(); i++) { 1447 fResourceDict->insertResourceAsReference( 1448 SkPDFResourceDict::kFont_ResourceType, 1449 i, fFontResources[i]); 1450 } 1451 } 1452 1453 if (fShaderResources.count()) { 1454 SkAutoTUnref<SkPDFDict> patterns(new SkPDFDict()); 1455 for (int i = 0; i < fShaderResources.count(); i++) { 1456 fResourceDict->insertResourceAsReference( 1457 SkPDFResourceDict::kPattern_ResourceType, 1458 i, fShaderResources[i]); 1459 } 1460 } 1461 } 1462 return fResourceDict; 1463} 1464 1465const SkTDArray<SkPDFFont*>& SkPDFDevice::getFontResources() const { 1466 return fFontResources; 1467} 1468 1469SkPDFArray* SkPDFDevice::copyMediaBox() const { 1470 // should this be a singleton? 1471 SkAutoTUnref<SkPDFInt> zero(SkNEW_ARGS(SkPDFInt, (0))); 1472 1473 SkPDFArray* mediaBox = SkNEW(SkPDFArray); 1474 mediaBox->reserve(4); 1475 mediaBox->append(zero.get()); 1476 mediaBox->append(zero.get()); 1477 mediaBox->appendInt(fPageSize.fWidth); 1478 mediaBox->appendInt(fPageSize.fHeight); 1479 return mediaBox; 1480} 1481 1482SkStream* SkPDFDevice::content() const { 1483 SkMemoryStream* result = new SkMemoryStream; 1484 result->setData(this->copyContentToData())->unref(); 1485 return result; 1486} 1487 1488void SkPDFDevice::copyContentEntriesToData(ContentEntry* entry, 1489 SkWStream* data) const { 1490 // TODO(ctguil): For margins, I'm not sure fExistingClipStack/Region is the 1491 // right thing to pass here. 1492 GraphicStackState gsState(fExistingClipStack, fExistingClipRegion, data); 1493 while (entry != NULL) { 1494 SkPoint translation; 1495 translation.iset(this->getOrigin()); 1496 translation.negate(); 1497 gsState.updateClip(entry->fState.fClipStack, entry->fState.fClipRegion, 1498 translation); 1499 gsState.updateMatrix(entry->fState.fMatrix); 1500 gsState.updateDrawingState(entry->fState); 1501 1502 SkAutoDataUnref copy(entry->fContent.copyToData()); 1503 data->write(copy->data(), copy->size()); 1504 entry = entry->fNext.get(); 1505 } 1506 gsState.drainStack(); 1507} 1508 1509SkData* SkPDFDevice::copyContentToData() const { 1510 SkDynamicMemoryWStream data; 1511 if (fInitialTransform.getType() != SkMatrix::kIdentity_Mask) { 1512 SkPDFUtils::AppendTransform(fInitialTransform, &data); 1513 } 1514 1515 // TODO(aayushkumar): Apply clip along the margins. Currently, webkit 1516 // colors the contentArea white before it starts drawing into it and 1517 // that currently acts as our clip. 1518 // Also, think about adding a transform here (or assume that the values 1519 // sent across account for that) 1520 SkPDFDevice::copyContentEntriesToData(fMarginContentEntries.get(), &data); 1521 1522 // If the content area is the entire page, then we don't need to clip 1523 // the content area (PDF area clips to the page size). Otherwise, 1524 // we have to clip to the content area; we've already applied the 1525 // initial transform, so just clip to the device size. 1526 if (fPageSize != fContentSize) { 1527 SkRect r = SkRect::MakeWH(SkIntToScalar(this->width()), 1528 SkIntToScalar(this->height())); 1529 emit_clip(NULL, &r, &data); 1530 } 1531 1532 SkPDFDevice::copyContentEntriesToData(fContentEntries.get(), &data); 1533 1534 // potentially we could cache this SkData, and only rebuild it if we 1535 // see that our state has changed. 1536 return data.copyToData(); 1537} 1538 1539#ifdef SK_PDF_USE_PATHOPS 1540/* Draws an inverse filled path by using Path Ops to compute the positive 1541 * inverse using the current clip as the inverse bounds. 1542 * Return true if this was an inverse path and was properly handled, 1543 * otherwise returns false and the normal drawing routine should continue, 1544 * either as a (incorrect) fallback or because the path was not inverse 1545 * in the first place. 1546 */ 1547bool SkPDFDevice::handleInversePath(const SkDraw& d, const SkPath& origPath, 1548 const SkPaint& paint, bool pathIsMutable, 1549 const SkMatrix* prePathMatrix) { 1550 if (!origPath.isInverseFillType()) { 1551 return false; 1552 } 1553 1554 if (d.fClip->isEmpty()) { 1555 return false; 1556 } 1557 1558 SkPath modifiedPath; 1559 SkPath* pathPtr = const_cast<SkPath*>(&origPath); 1560 SkPaint noInversePaint(paint); 1561 1562 // Merge stroking operations into final path. 1563 if (SkPaint::kStroke_Style == paint.getStyle() || 1564 SkPaint::kStrokeAndFill_Style == paint.getStyle()) { 1565 bool doFillPath = paint.getFillPath(origPath, &modifiedPath); 1566 if (doFillPath) { 1567 noInversePaint.setStyle(SkPaint::kFill_Style); 1568 noInversePaint.setStrokeWidth(0); 1569 pathPtr = &modifiedPath; 1570 } else { 1571 // To be consistent with the raster output, hairline strokes 1572 // are rendered as non-inverted. 1573 modifiedPath.toggleInverseFillType(); 1574 drawPath(d, modifiedPath, paint, NULL, true); 1575 return true; 1576 } 1577 } 1578 1579 // Get bounds of clip in current transform space 1580 // (clip bounds are given in device space). 1581 SkRect bounds; 1582 SkMatrix transformInverse; 1583 SkMatrix totalMatrix = *d.fMatrix; 1584 if (prePathMatrix) { 1585 totalMatrix.preConcat(*prePathMatrix); 1586 } 1587 if (!totalMatrix.invert(&transformInverse)) { 1588 return false; 1589 } 1590 bounds.set(d.fClip->getBounds()); 1591 transformInverse.mapRect(&bounds); 1592 1593 // Extend the bounds by the line width (plus some padding) 1594 // so the edge doesn't cause a visible stroke. 1595 bounds.outset(paint.getStrokeWidth() + SK_Scalar1, 1596 paint.getStrokeWidth() + SK_Scalar1); 1597 1598 if (!calculate_inverse_path(bounds, *pathPtr, &modifiedPath)) { 1599 return false; 1600 } 1601 1602 drawPath(d, modifiedPath, noInversePaint, prePathMatrix, true); 1603 return true; 1604} 1605#endif 1606 1607bool SkPDFDevice::handleRectAnnotation(const SkRect& r, const SkMatrix& matrix, 1608 const SkPaint& p) { 1609 SkAnnotation* annotationInfo = p.getAnnotation(); 1610 if (!annotationInfo) { 1611 return false; 1612 } 1613 SkData* urlData = annotationInfo->find(SkAnnotationKeys::URL_Key()); 1614 if (urlData) { 1615 handleLinkToURL(urlData, r, matrix); 1616 return p.getAnnotation() != NULL; 1617 } 1618 SkData* linkToName = annotationInfo->find( 1619 SkAnnotationKeys::Link_Named_Dest_Key()); 1620 if (linkToName) { 1621 handleLinkToNamedDest(linkToName, r, matrix); 1622 return p.getAnnotation() != NULL; 1623 } 1624 return false; 1625} 1626 1627bool SkPDFDevice::handlePointAnnotation(const SkPoint* points, size_t count, 1628 const SkMatrix& matrix, 1629 const SkPaint& paint) { 1630 SkAnnotation* annotationInfo = paint.getAnnotation(); 1631 if (!annotationInfo) { 1632 return false; 1633 } 1634 SkData* nameData = annotationInfo->find( 1635 SkAnnotationKeys::Define_Named_Dest_Key()); 1636 if (nameData) { 1637 for (size_t i = 0; i < count; i++) { 1638 defineNamedDestination(nameData, points[i], matrix); 1639 } 1640 return paint.getAnnotation() != NULL; 1641 } 1642 return false; 1643} 1644 1645SkPDFDict* SkPDFDevice::createLinkAnnotation(const SkRect& r, 1646 const SkMatrix& matrix) { 1647 SkMatrix transform = matrix; 1648 transform.postConcat(fInitialTransform); 1649 SkRect translatedRect; 1650 transform.mapRect(&translatedRect, r); 1651 1652 if (NULL == fAnnotations) { 1653 fAnnotations = SkNEW(SkPDFArray); 1654 } 1655 SkPDFDict* annotation(SkNEW_ARGS(SkPDFDict, ("Annot"))); 1656 annotation->insertName("Subtype", "Link"); 1657 fAnnotations->append(annotation); 1658 1659 SkAutoTUnref<SkPDFArray> border(SkNEW(SkPDFArray)); 1660 border->reserve(3); 1661 border->appendInt(0); // Horizontal corner radius. 1662 border->appendInt(0); // Vertical corner radius. 1663 border->appendInt(0); // Width, 0 = no border. 1664 annotation->insert("Border", border.get()); 1665 1666 SkAutoTUnref<SkPDFArray> rect(SkNEW(SkPDFArray)); 1667 rect->reserve(4); 1668 rect->appendScalar(translatedRect.fLeft); 1669 rect->appendScalar(translatedRect.fTop); 1670 rect->appendScalar(translatedRect.fRight); 1671 rect->appendScalar(translatedRect.fBottom); 1672 annotation->insert("Rect", rect.get()); 1673 1674 return annotation; 1675} 1676 1677void SkPDFDevice::handleLinkToURL(SkData* urlData, const SkRect& r, 1678 const SkMatrix& matrix) { 1679 SkAutoTUnref<SkPDFDict> annotation(createLinkAnnotation(r, matrix)); 1680 1681 SkString url(static_cast<const char *>(urlData->data()), 1682 urlData->size() - 1); 1683 SkAutoTUnref<SkPDFDict> action(SkNEW_ARGS(SkPDFDict, ("Action"))); 1684 action->insertName("S", "URI"); 1685 action->insert("URI", SkNEW_ARGS(SkPDFString, (url)))->unref(); 1686 annotation->insert("A", action.get()); 1687} 1688 1689void SkPDFDevice::handleLinkToNamedDest(SkData* nameData, const SkRect& r, 1690 const SkMatrix& matrix) { 1691 SkAutoTUnref<SkPDFDict> annotation(createLinkAnnotation(r, matrix)); 1692 SkString name(static_cast<const char *>(nameData->data()), 1693 nameData->size() - 1); 1694 annotation->insert("Dest", SkNEW_ARGS(SkPDFName, (name)))->unref(); 1695} 1696 1697struct NamedDestination { 1698 const SkData* nameData; 1699 SkPoint point; 1700 1701 NamedDestination(const SkData* nameData, const SkPoint& point) 1702 : nameData(nameData), point(point) { 1703 nameData->ref(); 1704 } 1705 1706 ~NamedDestination() { 1707 nameData->unref(); 1708 } 1709}; 1710 1711void SkPDFDevice::defineNamedDestination(SkData* nameData, const SkPoint& point, 1712 const SkMatrix& matrix) { 1713 SkMatrix transform = matrix; 1714 transform.postConcat(fInitialTransform); 1715 SkPoint translatedPoint; 1716 transform.mapXY(point.x(), point.y(), &translatedPoint); 1717 fNamedDestinations.push( 1718 SkNEW_ARGS(NamedDestination, (nameData, translatedPoint))); 1719} 1720 1721void SkPDFDevice::appendDestinations(SkPDFDict* dict, SkPDFObject* page) { 1722 int nDest = fNamedDestinations.count(); 1723 for (int i = 0; i < nDest; i++) { 1724 NamedDestination* dest = fNamedDestinations[i]; 1725 SkAutoTUnref<SkPDFArray> pdfDest(SkNEW(SkPDFArray)); 1726 pdfDest->reserve(5); 1727 pdfDest->append(SkNEW_ARGS(SkPDFObjRef, (page)))->unref(); 1728 pdfDest->appendName("XYZ"); 1729 pdfDest->appendScalar(dest->point.x()); 1730 pdfDest->appendScalar(dest->point.y()); 1731 pdfDest->appendInt(0); // Leave zoom unchanged 1732 dict->insert(static_cast<const char *>(dest->nameData->data()), 1733 pdfDest); 1734 } 1735} 1736 1737SkPDFFormXObject* SkPDFDevice::createFormXObjectFromDevice() { 1738 SkPDFFormXObject* xobject = SkNEW_ARGS(SkPDFFormXObject, (this)); 1739 // We always draw the form xobjects that we create back into the device, so 1740 // we simply preserve the font usage instead of pulling it out and merging 1741 // it back in later. 1742 cleanUp(false); // Reset this device to have no content. 1743 init(); 1744 return xobject; 1745} 1746 1747void SkPDFDevice::drawFormXObjectWithMask(int xObjectIndex, 1748 SkPDFFormXObject* mask, 1749 const SkClipStack* clipStack, 1750 const SkRegion& clipRegion, 1751 SkXfermode::Mode mode, 1752 bool invertClip) { 1753 if (clipRegion.isEmpty() && !invertClip) { 1754 return; 1755 } 1756 1757 SkAutoTUnref<SkPDFGraphicState> sMaskGS( 1758 SkPDFGraphicState::GetSMaskGraphicState( 1759 mask, invertClip, SkPDFGraphicState::kAlpha_SMaskMode)); 1760 1761 SkMatrix identity; 1762 identity.reset(); 1763 SkPaint paint; 1764 paint.setXfermodeMode(mode); 1765 ScopedContentEntry content(this, clipStack, clipRegion, identity, paint); 1766 if (!content.entry()) { 1767 return; 1768 } 1769 SkPDFUtils::ApplyGraphicState(addGraphicStateResource(sMaskGS.get()), 1770 &content.entry()->fContent); 1771 SkPDFUtils::DrawFormXObject(xObjectIndex, &content.entry()->fContent); 1772 1773 sMaskGS.reset(SkPDFGraphicState::GetNoSMaskGraphicState()); 1774 SkPDFUtils::ApplyGraphicState(addGraphicStateResource(sMaskGS.get()), 1775 &content.entry()->fContent); 1776} 1777 1778ContentEntry* SkPDFDevice::setUpContentEntry(const SkClipStack* clipStack, 1779 const SkRegion& clipRegion, 1780 const SkMatrix& matrix, 1781 const SkPaint& paint, 1782 bool hasText, 1783 SkPDFFormXObject** dst) { 1784 *dst = NULL; 1785 if (clipRegion.isEmpty()) { 1786 return NULL; 1787 } 1788 1789 // The clip stack can come from an SkDraw where it is technically optional. 1790 SkClipStack synthesizedClipStack; 1791 if (clipStack == NULL) { 1792 if (clipRegion == fExistingClipRegion) { 1793 clipStack = &fExistingClipStack; 1794 } else { 1795 // GraphicStackState::updateClip expects the clip stack to have 1796 // fExistingClip as a prefix, so start there, then set the clip 1797 // to the passed region. 1798 synthesizedClipStack = fExistingClipStack; 1799 SkPath clipPath; 1800 clipRegion.getBoundaryPath(&clipPath); 1801 synthesizedClipStack.clipDevPath(clipPath, SkRegion::kReplace_Op, 1802 false); 1803 clipStack = &synthesizedClipStack; 1804 } 1805 } 1806 1807 SkXfermode::Mode xfermode = SkXfermode::kSrcOver_Mode; 1808 if (paint.getXfermode()) { 1809 paint.getXfermode()->asMode(&xfermode); 1810 } 1811 1812 // For the following modes, we want to handle source and destination 1813 // separately, so make an object of what's already there. 1814 if (xfermode == SkXfermode::kClear_Mode || 1815 xfermode == SkXfermode::kSrc_Mode || 1816 xfermode == SkXfermode::kSrcIn_Mode || 1817 xfermode == SkXfermode::kDstIn_Mode || 1818 xfermode == SkXfermode::kSrcOut_Mode || 1819 xfermode == SkXfermode::kDstOut_Mode || 1820 xfermode == SkXfermode::kSrcATop_Mode || 1821 xfermode == SkXfermode::kDstATop_Mode || 1822 xfermode == SkXfermode::kModulate_Mode) { 1823 if (!isContentEmpty()) { 1824 *dst = createFormXObjectFromDevice(); 1825 SkASSERT(isContentEmpty()); 1826 } else if (xfermode != SkXfermode::kSrc_Mode && 1827 xfermode != SkXfermode::kSrcOut_Mode) { 1828 // Except for Src and SrcOut, if there isn't anything already there, 1829 // then we're done. 1830 return NULL; 1831 } 1832 } 1833 // TODO(vandebo): Figure out how/if we can handle the following modes: 1834 // Xor, Plus. 1835 1836 // Dst xfer mode doesn't draw source at all. 1837 if (xfermode == SkXfermode::kDst_Mode) { 1838 return NULL; 1839 } 1840 1841 ContentEntry* entry; 1842 SkAutoTDelete<ContentEntry> newEntry; 1843 1844 ContentEntry* lastContentEntry = getLastContentEntry(); 1845 if (lastContentEntry && lastContentEntry->fContent.getOffset() == 0) { 1846 entry = lastContentEntry; 1847 } else { 1848 newEntry.reset(new ContentEntry); 1849 entry = newEntry.get(); 1850 } 1851 1852 populateGraphicStateEntryFromPaint(matrix, *clipStack, clipRegion, paint, 1853 hasText, &entry->fState); 1854 if (lastContentEntry && xfermode != SkXfermode::kDstOver_Mode && 1855 entry->fState.compareInitialState(lastContentEntry->fState)) { 1856 return lastContentEntry; 1857 } 1858 1859 SkAutoTDelete<ContentEntry>* contentEntries = getContentEntries(); 1860 if (!lastContentEntry) { 1861 contentEntries->reset(entry); 1862 setLastContentEntry(entry); 1863 } else if (xfermode == SkXfermode::kDstOver_Mode) { 1864 entry->fNext.reset(contentEntries->detach()); 1865 contentEntries->reset(entry); 1866 } else { 1867 lastContentEntry->fNext.reset(entry); 1868 setLastContentEntry(entry); 1869 } 1870 newEntry.detach(); 1871 return entry; 1872} 1873 1874void SkPDFDevice::finishContentEntry(SkXfermode::Mode xfermode, 1875 SkPDFFormXObject* dst, 1876 SkPath* shape) { 1877 if (xfermode != SkXfermode::kClear_Mode && 1878 xfermode != SkXfermode::kSrc_Mode && 1879 xfermode != SkXfermode::kDstOver_Mode && 1880 xfermode != SkXfermode::kSrcIn_Mode && 1881 xfermode != SkXfermode::kDstIn_Mode && 1882 xfermode != SkXfermode::kSrcOut_Mode && 1883 xfermode != SkXfermode::kDstOut_Mode && 1884 xfermode != SkXfermode::kSrcATop_Mode && 1885 xfermode != SkXfermode::kDstATop_Mode && 1886 xfermode != SkXfermode::kModulate_Mode) { 1887 SkASSERT(!dst); 1888 return; 1889 } 1890 if (xfermode == SkXfermode::kDstOver_Mode) { 1891 SkASSERT(!dst); 1892 ContentEntry* firstContentEntry = getContentEntries()->get(); 1893 if (firstContentEntry->fContent.getOffset() == 0) { 1894 // For DstOver, an empty content entry was inserted before the rest 1895 // of the content entries. If nothing was drawn, it needs to be 1896 // removed. 1897 SkAutoTDelete<ContentEntry>* contentEntries = getContentEntries(); 1898 contentEntries->reset(firstContentEntry->fNext.detach()); 1899 } 1900 return; 1901 } 1902 if (!dst) { 1903 SkASSERT(xfermode == SkXfermode::kSrc_Mode || 1904 xfermode == SkXfermode::kSrcOut_Mode); 1905 return; 1906 } 1907 1908 ContentEntry* contentEntries = getContentEntries()->get(); 1909 SkASSERT(dst); 1910 SkASSERT(!contentEntries->fNext.get()); 1911 // Changing the current content into a form-xobject will destroy the clip 1912 // objects which is fine since the xobject will already be clipped. However 1913 // if source has shape, we need to clip it too, so a copy of the clip is 1914 // saved. 1915 SkClipStack clipStack = contentEntries->fState.fClipStack; 1916 SkRegion clipRegion = contentEntries->fState.fClipRegion; 1917 1918 SkMatrix identity; 1919 identity.reset(); 1920 SkPaint stockPaint; 1921 1922 SkAutoTUnref<SkPDFFormXObject> srcFormXObject; 1923 if (isContentEmpty()) { 1924 // If nothing was drawn and there's no shape, then the draw was a 1925 // no-op, but dst needs to be restored for that to be true. 1926 // If there is shape, then an empty source with Src, SrcIn, SrcOut, 1927 // DstIn, DstAtop or Modulate reduces to Clear and DstOut or SrcAtop 1928 // reduces to Dst. 1929 if (shape == NULL || xfermode == SkXfermode::kDstOut_Mode || 1930 xfermode == SkXfermode::kSrcATop_Mode) { 1931 ScopedContentEntry content(this, &fExistingClipStack, 1932 fExistingClipRegion, identity, 1933 stockPaint); 1934 SkPDFUtils::DrawFormXObject(this->addXObjectResource(dst), 1935 &content.entry()->fContent); 1936 return; 1937 } else { 1938 xfermode = SkXfermode::kClear_Mode; 1939 } 1940 } else { 1941 SkASSERT(!fContentEntries->fNext.get()); 1942 srcFormXObject.reset(createFormXObjectFromDevice()); 1943 } 1944 1945 // TODO(vandebo) srcFormXObject may contain alpha, but here we want it 1946 // without alpha. 1947 if (xfermode == SkXfermode::kSrcATop_Mode) { 1948 // TODO(vandebo): In order to properly support SrcATop we have to track 1949 // the shape of what's been drawn at all times. It's the intersection of 1950 // the non-transparent parts of the device and the outlines (shape) of 1951 // all images and devices drawn. 1952 drawFormXObjectWithMask(addXObjectResource(srcFormXObject.get()), dst, 1953 &fExistingClipStack, fExistingClipRegion, 1954 SkXfermode::kSrcOver_Mode, true); 1955 } else { 1956 SkAutoTUnref<SkPDFFormXObject> dstMaskStorage; 1957 SkPDFFormXObject* dstMask = srcFormXObject.get(); 1958 if (shape != NULL) { 1959 // Draw shape into a form-xobject. 1960 SkDraw d; 1961 d.fMatrix = &identity; 1962 d.fClip = &clipRegion; 1963 d.fClipStack = &clipStack; 1964 SkPaint filledPaint; 1965 filledPaint.setColor(SK_ColorBLACK); 1966 filledPaint.setStyle(SkPaint::kFill_Style); 1967 this->drawPath(d, *shape, filledPaint, NULL, true); 1968 1969 dstMaskStorage.reset(createFormXObjectFromDevice()); 1970 dstMask = dstMaskStorage.get(); 1971 } 1972 drawFormXObjectWithMask(addXObjectResource(dst), dstMask, 1973 &fExistingClipStack, fExistingClipRegion, 1974 SkXfermode::kSrcOver_Mode, true); 1975 } 1976 1977 if (xfermode == SkXfermode::kClear_Mode) { 1978 return; 1979 } else if (xfermode == SkXfermode::kSrc_Mode || 1980 xfermode == SkXfermode::kDstATop_Mode) { 1981 ScopedContentEntry content(this, &fExistingClipStack, 1982 fExistingClipRegion, identity, stockPaint); 1983 if (content.entry()) { 1984 SkPDFUtils::DrawFormXObject( 1985 this->addXObjectResource(srcFormXObject.get()), 1986 &content.entry()->fContent); 1987 } 1988 if (xfermode == SkXfermode::kSrc_Mode) { 1989 return; 1990 } 1991 } else if (xfermode == SkXfermode::kSrcATop_Mode) { 1992 ScopedContentEntry content(this, &fExistingClipStack, 1993 fExistingClipRegion, identity, stockPaint); 1994 if (content.entry()) { 1995 SkPDFUtils::DrawFormXObject(this->addXObjectResource(dst), 1996 &content.entry()->fContent); 1997 } 1998 } 1999 2000 SkASSERT(xfermode == SkXfermode::kSrcIn_Mode || 2001 xfermode == SkXfermode::kDstIn_Mode || 2002 xfermode == SkXfermode::kSrcOut_Mode || 2003 xfermode == SkXfermode::kDstOut_Mode || 2004 xfermode == SkXfermode::kSrcATop_Mode || 2005 xfermode == SkXfermode::kDstATop_Mode || 2006 xfermode == SkXfermode::kModulate_Mode); 2007 2008 if (xfermode == SkXfermode::kSrcIn_Mode || 2009 xfermode == SkXfermode::kSrcOut_Mode || 2010 xfermode == SkXfermode::kSrcATop_Mode) { 2011 drawFormXObjectWithMask(addXObjectResource(srcFormXObject.get()), dst, 2012 &fExistingClipStack, fExistingClipRegion, 2013 SkXfermode::kSrcOver_Mode, 2014 xfermode == SkXfermode::kSrcOut_Mode); 2015 } else { 2016 SkXfermode::Mode mode = SkXfermode::kSrcOver_Mode; 2017 if (xfermode == SkXfermode::kModulate_Mode) { 2018 drawFormXObjectWithMask(addXObjectResource(srcFormXObject.get()), 2019 dst, &fExistingClipStack, 2020 fExistingClipRegion, 2021 SkXfermode::kSrcOver_Mode, false); 2022 mode = SkXfermode::kMultiply_Mode; 2023 } 2024 drawFormXObjectWithMask(addXObjectResource(dst), srcFormXObject.get(), 2025 &fExistingClipStack, fExistingClipRegion, mode, 2026 xfermode == SkXfermode::kDstOut_Mode); 2027 } 2028} 2029 2030bool SkPDFDevice::isContentEmpty() { 2031 ContentEntry* contentEntries = getContentEntries()->get(); 2032 if (!contentEntries || contentEntries->fContent.getOffset() == 0) { 2033 SkASSERT(!contentEntries || !contentEntries->fNext.get()); 2034 return true; 2035 } 2036 return false; 2037} 2038 2039void SkPDFDevice::populateGraphicStateEntryFromPaint( 2040 const SkMatrix& matrix, 2041 const SkClipStack& clipStack, 2042 const SkRegion& clipRegion, 2043 const SkPaint& paint, 2044 bool hasText, 2045 GraphicStateEntry* entry) { 2046 SkASSERT(paint.getPathEffect() == NULL); 2047 2048 NOT_IMPLEMENTED(paint.getMaskFilter() != NULL, false); 2049 NOT_IMPLEMENTED(paint.getColorFilter() != NULL, false); 2050 2051 entry->fMatrix = matrix; 2052 entry->fClipStack = clipStack; 2053 entry->fClipRegion = clipRegion; 2054 entry->fColor = SkColorSetA(paint.getColor(), 0xFF); 2055 entry->fShaderIndex = -1; 2056 2057 // PDF treats a shader as a color, so we only set one or the other. 2058 SkAutoTUnref<SkPDFObject> pdfShader; 2059 const SkShader* shader = paint.getShader(); 2060 SkColor color = paint.getColor(); 2061 if (shader) { 2062 // PDF positions patterns relative to the initial transform, so 2063 // we need to apply the current transform to the shader parameters. 2064 SkMatrix transform = matrix; 2065 transform.postConcat(fInitialTransform); 2066 2067 // PDF doesn't support kClamp_TileMode, so we simulate it by making 2068 // a pattern the size of the current clip. 2069 SkIRect bounds = clipRegion.getBounds(); 2070 2071 // We need to apply the initial transform to bounds in order to get 2072 // bounds in a consistent coordinate system. 2073 SkRect boundsTemp; 2074 boundsTemp.set(bounds); 2075 fInitialTransform.mapRect(&boundsTemp); 2076 boundsTemp.roundOut(&bounds); 2077 2078 pdfShader.reset(SkPDFShader::GetPDFShader(*shader, transform, bounds)); 2079 2080 if (pdfShader.get()) { 2081 // pdfShader has been canonicalized so we can directly compare 2082 // pointers. 2083 int resourceIndex = fShaderResources.find(pdfShader.get()); 2084 if (resourceIndex < 0) { 2085 resourceIndex = fShaderResources.count(); 2086 fShaderResources.push(pdfShader.get()); 2087 pdfShader.get()->ref(); 2088 } 2089 entry->fShaderIndex = resourceIndex; 2090 } else { 2091 // A color shader is treated as an invalid shader so we don't have 2092 // to set a shader just for a color. 2093 SkShader::GradientInfo gradientInfo; 2094 SkColor gradientColor; 2095 gradientInfo.fColors = &gradientColor; 2096 gradientInfo.fColorOffsets = NULL; 2097 gradientInfo.fColorCount = 1; 2098 if (shader->asAGradient(&gradientInfo) == 2099 SkShader::kColor_GradientType) { 2100 entry->fColor = SkColorSetA(gradientColor, 0xFF); 2101 color = gradientColor; 2102 } 2103 } 2104 } 2105 2106 SkAutoTUnref<SkPDFGraphicState> newGraphicState; 2107 if (color == paint.getColor()) { 2108 newGraphicState.reset( 2109 SkPDFGraphicState::GetGraphicStateForPaint(paint)); 2110 } else { 2111 SkPaint newPaint = paint; 2112 newPaint.setColor(color); 2113 newGraphicState.reset( 2114 SkPDFGraphicState::GetGraphicStateForPaint(newPaint)); 2115 } 2116 int resourceIndex = addGraphicStateResource(newGraphicState.get()); 2117 entry->fGraphicStateIndex = resourceIndex; 2118 2119 if (hasText) { 2120 entry->fTextScaleX = paint.getTextScaleX(); 2121 entry->fTextFill = paint.getStyle(); 2122 } else { 2123 entry->fTextScaleX = 0; 2124 } 2125} 2126 2127int SkPDFDevice::addGraphicStateResource(SkPDFGraphicState* gs) { 2128 // Assumes that gs has been canonicalized (so we can directly compare 2129 // pointers). 2130 int result = fGraphicStateResources.find(gs); 2131 if (result < 0) { 2132 result = fGraphicStateResources.count(); 2133 fGraphicStateResources.push(gs); 2134 gs->ref(); 2135 } 2136 return result; 2137} 2138 2139int SkPDFDevice::addXObjectResource(SkPDFObject* xObject) { 2140 // Assumes that xobject has been canonicalized (so we can directly compare 2141 // pointers). 2142 int result = fXObjectResources.find(xObject); 2143 if (result < 0) { 2144 result = fXObjectResources.count(); 2145 fXObjectResources.push(xObject); 2146 xObject->ref(); 2147 } 2148 return result; 2149} 2150 2151void SkPDFDevice::updateFont(const SkPaint& paint, uint16_t glyphID, 2152 ContentEntry* contentEntry) { 2153 SkTypeface* typeface = paint.getTypeface(); 2154 if (contentEntry->fState.fFont == NULL || 2155 contentEntry->fState.fTextSize != paint.getTextSize() || 2156 !contentEntry->fState.fFont->hasGlyph(glyphID)) { 2157 int fontIndex = getFontResourceIndex(typeface, glyphID); 2158 contentEntry->fContent.writeText("/"); 2159 contentEntry->fContent.writeText(SkPDFResourceDict::getResourceName( 2160 SkPDFResourceDict::kFont_ResourceType, 2161 fontIndex).c_str()); 2162 contentEntry->fContent.writeText(" "); 2163 SkPDFScalar::Append(paint.getTextSize(), &contentEntry->fContent); 2164 contentEntry->fContent.writeText(" Tf\n"); 2165 contentEntry->fState.fFont = fFontResources[fontIndex]; 2166 } 2167} 2168 2169int SkPDFDevice::getFontResourceIndex(SkTypeface* typeface, uint16_t glyphID) { 2170 SkAutoTUnref<SkPDFFont> newFont(SkPDFFont::GetFontResource(typeface, 2171 glyphID)); 2172 int resourceIndex = fFontResources.find(newFont.get()); 2173 if (resourceIndex < 0) { 2174 resourceIndex = fFontResources.count(); 2175 fFontResources.push(newFont.get()); 2176 newFont.get()->ref(); 2177 } 2178 return resourceIndex; 2179} 2180 2181void SkPDFDevice::internalDrawBitmap(const SkMatrix& origMatrix, 2182 const SkClipStack* clipStack, 2183 const SkRegion& origClipRegion, 2184 const SkBitmap& origBitmap, 2185 const SkIRect* srcRect, 2186 const SkPaint& paint) { 2187 SkMatrix matrix = origMatrix; 2188 SkRegion perspectiveBounds; 2189 const SkRegion* clipRegion = &origClipRegion; 2190 SkBitmap perspectiveBitmap; 2191 const SkBitmap* bitmap = &origBitmap; 2192 SkBitmap tmpSubsetBitmap; 2193 2194 // Rasterize the bitmap using perspective in a new bitmap. 2195 if (origMatrix.hasPerspective()) { 2196 if (fRasterDpi == 0) { 2197 return; 2198 } 2199 SkBitmap* subsetBitmap; 2200 if (srcRect) { 2201 if (!origBitmap.extractSubset(&tmpSubsetBitmap, *srcRect)) { 2202 return; 2203 } 2204 subsetBitmap = &tmpSubsetBitmap; 2205 } else { 2206 subsetBitmap = &tmpSubsetBitmap; 2207 *subsetBitmap = origBitmap; 2208 } 2209 srcRect = NULL; 2210 2211 // Transform the bitmap in the new space, without taking into 2212 // account the initial transform. 2213 SkPath perspectiveOutline; 2214 perspectiveOutline.addRect( 2215 SkRect::MakeWH(SkIntToScalar(subsetBitmap->width()), 2216 SkIntToScalar(subsetBitmap->height()))); 2217 perspectiveOutline.transform(origMatrix); 2218 2219 // TODO(edisonn): perf - use current clip too. 2220 // Retrieve the bounds of the new shape. 2221 SkRect bounds = perspectiveOutline.getBounds(); 2222 2223 // Transform the bitmap in the new space, taking into 2224 // account the initial transform. 2225 SkMatrix total = origMatrix; 2226 total.postConcat(fInitialTransform); 2227 total.postScale(SkIntToScalar(fRasterDpi) / 2228 SkIntToScalar(DPI_FOR_RASTER_SCALE_ONE), 2229 SkIntToScalar(fRasterDpi) / 2230 SkIntToScalar(DPI_FOR_RASTER_SCALE_ONE)); 2231 SkPath physicalPerspectiveOutline; 2232 physicalPerspectiveOutline.addRect( 2233 SkRect::MakeWH(SkIntToScalar(subsetBitmap->width()), 2234 SkIntToScalar(subsetBitmap->height()))); 2235 physicalPerspectiveOutline.transform(total); 2236 2237 SkScalar scaleX = physicalPerspectiveOutline.getBounds().width() / 2238 bounds.width(); 2239 SkScalar scaleY = physicalPerspectiveOutline.getBounds().height() / 2240 bounds.height(); 2241 2242 // TODO(edisonn): A better approach would be to use a bitmap shader 2243 // (in clamp mode) and draw a rect over the entire bounding box. Then 2244 // intersect perspectiveOutline to the clip. That will avoid introducing 2245 // alpha to the image while still giving good behavior at the edge of 2246 // the image. Avoiding alpha will reduce the pdf size and generation 2247 // CPU time some. 2248 2249 const int w = SkScalarCeilToInt(physicalPerspectiveOutline.getBounds().width()); 2250 const int h = SkScalarCeilToInt(physicalPerspectiveOutline.getBounds().height()); 2251 if (!perspectiveBitmap.allocPixels(SkImageInfo::MakeN32Premul(w, h))) { 2252 return; 2253 } 2254 perspectiveBitmap.eraseColor(SK_ColorTRANSPARENT); 2255 2256 SkBitmapDevice device(perspectiveBitmap); 2257 SkCanvas canvas(&device); 2258 2259 SkScalar deltaX = bounds.left(); 2260 SkScalar deltaY = bounds.top(); 2261 2262 SkMatrix offsetMatrix = origMatrix; 2263 offsetMatrix.postTranslate(-deltaX, -deltaY); 2264 offsetMatrix.postScale(scaleX, scaleY); 2265 2266 // Translate the draw in the new canvas, so we perfectly fit the 2267 // shape in the bitmap. 2268 canvas.setMatrix(offsetMatrix); 2269 2270 canvas.drawBitmap(*subsetBitmap, SkIntToScalar(0), SkIntToScalar(0)); 2271 2272 // Make sure the final bits are in the bitmap. 2273 canvas.flush(); 2274 2275 // In the new space, we use the identity matrix translated 2276 // and scaled to reflect DPI. 2277 matrix.setScale(1 / scaleX, 1 / scaleY); 2278 matrix.postTranslate(deltaX, deltaY); 2279 2280 perspectiveBounds.setRect( 2281 SkIRect::MakeXYWH(SkScalarFloorToInt(bounds.x()), 2282 SkScalarFloorToInt(bounds.y()), 2283 SkScalarCeilToInt(bounds.width()), 2284 SkScalarCeilToInt(bounds.height()))); 2285 clipRegion = &perspectiveBounds; 2286 srcRect = NULL; 2287 bitmap = &perspectiveBitmap; 2288 } 2289 2290 SkMatrix scaled; 2291 // Adjust for origin flip. 2292 scaled.setScale(SK_Scalar1, -SK_Scalar1); 2293 scaled.postTranslate(0, SK_Scalar1); 2294 // Scale the image up from 1x1 to WxH. 2295 SkIRect subset = SkIRect::MakeWH(bitmap->width(), bitmap->height()); 2296 scaled.postScale(SkIntToScalar(subset.width()), 2297 SkIntToScalar(subset.height())); 2298 scaled.postConcat(matrix); 2299 ScopedContentEntry content(this, clipStack, *clipRegion, scaled, paint); 2300 if (!content.entry() || (srcRect && !subset.intersect(*srcRect))) { 2301 return; 2302 } 2303 if (content.needShape()) { 2304 SkPath shape; 2305 shape.addRect(SkRect::MakeWH(SkIntToScalar(subset.width()), 2306 SkIntToScalar( subset.height()))); 2307 shape.transform(matrix); 2308 content.setShape(shape); 2309 } 2310 if (!content.needSource()) { 2311 return; 2312 } 2313 2314 SkAutoTUnref<SkPDFImage> image( 2315 SkPDFImage::CreateImage(*bitmap, subset, fEncoder)); 2316 if (!image) { 2317 return; 2318 } 2319 2320 SkPDFUtils::DrawFormXObject(this->addXObjectResource(image.get()), 2321 &content.entry()->fContent); 2322} 2323 2324bool SkPDFDevice::onReadPixels(const SkBitmap& bitmap, int x, int y, 2325 SkCanvas::Config8888) { 2326 return false; 2327} 2328 2329bool SkPDFDevice::allowImageFilter(const SkImageFilter*) { 2330 return false; 2331} 2332