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