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