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