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