1 /* 2 * Copyright 2011 Google Inc. 3 * 4 * Use of this source code is governed by a BSD-style license that can be 5 * found in the LICENSE file. 6 */ 7 8 #include "SkPDFDevice.h" 9 10 #include "SkAdvancedTypefaceMetrics.h" 11 #include "SkAnnotationKeys.h" 12 #include "SkBitmapDevice.h" 13 #include "SkBitmapKey.h" 14 #include "SkCanvas.h" 15 #include "SkClipOpPriv.h" 16 #include "SkColor.h" 17 #include "SkColorFilter.h" 18 #include "SkDraw.h" 19 #include "SkDrawFilter.h" 20 #include "SkGlyphCache.h" 21 #include "SkImageFilterCache.h" 22 #include "SkJpegEncoder.h" 23 #include "SkMakeUnique.h" 24 #include "SkMaskFilter.h" 25 #include "SkPDFBitmap.h" 26 #include "SkPDFCanon.h" 27 #include "SkPDFDocument.h" 28 #include "SkPDFFont.h" 29 #include "SkPDFFormXObject.h" 30 #include "SkPDFGraphicState.h" 31 #include "SkPDFResourceDict.h" 32 #include "SkPDFShader.h" 33 #include "SkPDFTypes.h" 34 #include "SkPDFUtils.h" 35 #include "SkPath.h" 36 #include "SkPathEffect.h" 37 #include "SkPathOps.h" 38 #include "SkPixelRef.h" 39 #include "SkRRect.h" 40 #include "SkRasterClip.h" 41 #include "SkScopeExit.h" 42 #include "SkString.h" 43 #include "SkSurface.h" 44 #include "SkTemplates.h" 45 #include "SkTextBlobRunIterator.h" 46 #include "SkTextFormatParams.h" 47 #include "SkUtils.h" 48 #include "SkXfermodeInterpretation.h" 49 50 #ifndef SK_PDF_MASK_QUALITY 51 // If MASK_QUALITY is in [0,100], will be used for JpegEncoder. 52 // Otherwise, just encode masks losslessly. 53 #define SK_PDF_MASK_QUALITY 50 54 // Since these masks are used for blurry shadows, we shouldn't need 55 // high quality. Raise this value if your shadows have visible JPEG 56 // artifacts. 57 // If SkJpegEncoder::Encode fails, we will fall back to the lossless 58 // encoding. 59 #endif 60 61 // Utility functions 62 63 // This function destroys the mask and either frees or takes the pixels. 64 sk_sp<SkImage> mask_to_greyscale_image(SkMask* mask) { 65 sk_sp<SkImage> img; 66 SkPixmap pm(SkImageInfo::Make(mask->fBounds.width(), mask->fBounds.height(), 67 kGray_8_SkColorType, kOpaque_SkAlphaType), 68 mask->fImage, mask->fRowBytes); 69 const int imgQuality = SK_PDF_MASK_QUALITY; 70 if (imgQuality <= 100 && imgQuality >= 0) { 71 SkDynamicMemoryWStream buffer; 72 SkJpegEncoder::Options jpegOptions; 73 jpegOptions.fQuality = imgQuality; 74 if (SkJpegEncoder::Encode(&buffer, pm, jpegOptions)) { 75 img = SkImage::MakeFromEncoded(buffer.detachAsData()); 76 SkASSERT(img); 77 if (img) { 78 SkMask::FreeImage(mask->fImage); 79 } 80 } 81 } 82 if (!img) { 83 img = SkImage::MakeFromRaster(pm, [](const void* p, void*) { SkMask::FreeImage((void*)p); }, 84 nullptr); 85 } 86 *mask = SkMask(); // destructive; 87 return img; 88 } 89 90 sk_sp<SkImage> alpha_image_to_greyscale_image(const SkImage* mask) { 91 int w = mask->width(), h = mask->height(); 92 SkBitmap greyBitmap; 93 greyBitmap.allocPixels(SkImageInfo::Make(w, h, kGray_8_SkColorType, kOpaque_SkAlphaType)); 94 if (!mask->readPixels(SkImageInfo::MakeA8(w, h), 95 greyBitmap.getPixels(), greyBitmap.rowBytes(), 0, 0)) { 96 return nullptr; 97 } 98 return SkImage::MakeFromBitmap(greyBitmap); 99 } 100 101 static void draw_points(SkCanvas::PointMode mode, 102 size_t count, 103 const SkPoint* points, 104 const SkPaint& paint, 105 const SkIRect& bounds, 106 const SkMatrix& ctm, 107 SkBaseDevice* device) { 108 SkRasterClip rc(bounds); 109 SkDraw draw; 110 draw.fDst = SkPixmap(SkImageInfo::MakeUnknown(bounds.right(), bounds.bottom()), nullptr, 0); 111 draw.fMatrix = &ctm; 112 draw.fRC = &rc; 113 draw.drawPoints(mode, count, points, paint, device); 114 } 115 116 static SkIRect size(const SkBaseDevice& dev) { return {0, 0, dev.width(), dev.height()}; } 117 118 // If the paint will definitely draw opaquely, replace kSrc with 119 // kSrcOver. http://crbug.com/473572 120 static void replace_srcmode_on_opaque_paint(SkPaint* paint) { 121 if (kSrcOver_SkXfermodeInterpretation == SkInterpretXfermode(*paint, false)) { 122 paint->setBlendMode(SkBlendMode::kSrcOver); 123 } 124 } 125 126 // A shader's matrix is: CTMM x LocalMatrix x WrappingLocalMatrix. We want to 127 // switch to device space, where CTM = I, while keeping the original behavior. 128 // 129 // I * LocalMatrix * NewWrappingMatrix = CTM * LocalMatrix 130 // LocalMatrix * NewWrappingMatrix = CTM * LocalMatrix 131 // InvLocalMatrix * LocalMatrix * NewWrappingMatrix = InvLocalMatrix * CTM * LocalMatrix 132 // NewWrappingMatrix = InvLocalMatrix * CTM * LocalMatrix 133 // 134 static void transform_shader(SkPaint* paint, const SkMatrix& ctm) { 135 SkMatrix lm = SkPDFUtils::GetShaderLocalMatrix(paint->getShader()); 136 SkMatrix lmInv; 137 if (lm.invert(&lmInv)) { 138 SkMatrix m = SkMatrix::Concat(SkMatrix::Concat(lmInv, ctm), lm); 139 paint->setShader(paint->getShader()->makeWithLocalMatrix(m)); 140 } 141 } 142 143 static void emit_pdf_color(SkColor color, SkWStream* result) { 144 SkASSERT(SkColorGetA(color) == 0xFF); // We handle alpha elsewhere. 145 SkPDFUtils::AppendColorComponent(SkColorGetR(color), result); 146 result->writeText(" "); 147 SkPDFUtils::AppendColorComponent(SkColorGetG(color), result); 148 result->writeText(" "); 149 SkPDFUtils::AppendColorComponent(SkColorGetB(color), result); 150 result->writeText(" "); 151 } 152 153 static SkPaint calculate_text_paint(const SkPaint& paint) { 154 SkPaint result = paint; 155 if (result.isFakeBoldText()) { 156 SkScalar fakeBoldScale = SkScalarInterpFunc(result.getTextSize(), 157 kStdFakeBoldInterpKeys, 158 kStdFakeBoldInterpValues, 159 kStdFakeBoldInterpLength); 160 SkScalar width = result.getTextSize() * fakeBoldScale; 161 if (result.getStyle() == SkPaint::kFill_Style) { 162 result.setStyle(SkPaint::kStrokeAndFill_Style); 163 } else { 164 width += result.getStrokeWidth(); 165 } 166 result.setStrokeWidth(width); 167 } 168 return result; 169 } 170 171 172 // If the paint has a color filter, apply the color filter to the shader or the 173 // paint color. Remove the color filter. 174 void remove_color_filter(SkPaint* paint) { 175 if (SkColorFilter* cf = paint->getColorFilter()) { 176 if (SkShader* shader = paint->getShader()) { 177 paint->setShader(shader->makeWithColorFilter(paint->refColorFilter())); 178 } else { 179 paint->setColor(cf->filterColor(paint->getColor())); 180 } 181 paint->setColorFilter(nullptr); 182 } 183 } 184 185 SkPDFDevice::GraphicStateEntry::GraphicStateEntry() 186 : fColor(SK_ColorBLACK) 187 , fTextScaleX(SK_Scalar1) 188 , fTextFill(SkPaint::kFill_Style) 189 , fShaderIndex(-1) 190 , fGraphicStateIndex(-1) { 191 fMatrix.reset(); 192 } 193 194 bool SkPDFDevice::GraphicStateEntry::compareInitialState( 195 const GraphicStateEntry& cur) { 196 return fColor == cur.fColor && 197 fShaderIndex == cur.fShaderIndex && 198 fGraphicStateIndex == cur.fGraphicStateIndex && 199 fMatrix == cur.fMatrix && 200 fClipStack == cur.fClipStack && 201 (fTextScaleX == 0 || 202 (fTextScaleX == cur.fTextScaleX && fTextFill == cur.fTextFill)); 203 } 204 205 class GraphicStackState { 206 public: 207 GraphicStackState(const SkClipStack& existingClipStack, 208 SkWStream* contentStream) 209 : fStackDepth(0), 210 fContentStream(contentStream) { 211 fEntries[0].fClipStack = existingClipStack; 212 } 213 214 void updateClip(const SkClipStack& clipStack, 215 const SkPoint& translation, const SkRect& bounds); 216 void updateMatrix(const SkMatrix& matrix); 217 void updateDrawingState(const SkPDFDevice::GraphicStateEntry& state); 218 219 void drainStack(); 220 221 private: 222 void push(); 223 void pop(); 224 SkPDFDevice::GraphicStateEntry* currentEntry() { return &fEntries[fStackDepth]; } 225 226 // Conservative limit on save depth, see impl. notes in PDF 1.4 spec. 227 static const int kMaxStackDepth = 12; 228 SkPDFDevice::GraphicStateEntry fEntries[kMaxStackDepth + 1]; 229 int fStackDepth; 230 SkWStream* fContentStream; 231 }; 232 233 void GraphicStackState::drainStack() { 234 while (fStackDepth) { 235 pop(); 236 } 237 } 238 239 void GraphicStackState::push() { 240 SkASSERT(fStackDepth < kMaxStackDepth); 241 fContentStream->writeText("q\n"); 242 fStackDepth++; 243 fEntries[fStackDepth] = fEntries[fStackDepth - 1]; 244 } 245 246 void GraphicStackState::pop() { 247 SkASSERT(fStackDepth > 0); 248 fContentStream->writeText("Q\n"); 249 fStackDepth--; 250 } 251 252 /* Calculate an inverted path's equivalent non-inverted path, given the 253 * canvas bounds. 254 * outPath may alias with invPath (since this is supported by PathOps). 255 */ 256 static bool calculate_inverse_path(const SkRect& bounds, const SkPath& invPath, 257 SkPath* outPath) { 258 SkASSERT(invPath.isInverseFillType()); 259 260 SkPath clipPath; 261 clipPath.addRect(bounds); 262 263 return Op(clipPath, invPath, kIntersect_SkPathOp, outPath); 264 } 265 266 bool apply_clip(SkClipOp op, const SkPath& u, const SkPath& v, SkPath* r) { 267 switch (op) { 268 case SkClipOp::kDifference: 269 return Op(u, v, kDifference_SkPathOp, r); 270 case SkClipOp::kIntersect: 271 return Op(u, v, kIntersect_SkPathOp, r); 272 #ifdef SK_SUPPORT_DEPRECATED_CLIPOPS 273 case SkClipOp::kUnion_deprecated: 274 return Op(u, v, kUnion_SkPathOp, r); 275 case SkClipOp::kXOR_deprecated: 276 return Op(u, v, kXOR_SkPathOp, r); 277 case SkClipOp::kReverseDifference_deprecated: 278 return Op(u, v, kReverseDifference_SkPathOp, r); 279 case SkClipOp::kReplace_deprecated: 280 *r = v; 281 return true; 282 #endif 283 default: 284 return false; 285 } 286 } 287 288 /* Uses Path Ops to calculate a vector SkPath clip from a clip stack. 289 * Returns true if successful, or false if not successful. 290 * If successful, the resulting clip is stored in outClipPath. 291 * If not successful, outClipPath is undefined, and a fallback method 292 * should be used. 293 */ 294 static bool get_clip_stack_path(const SkMatrix& transform, 295 const SkClipStack& clipStack, 296 const SkRect& bounds, 297 SkPath* outClipPath) { 298 outClipPath->reset(); 299 outClipPath->setFillType(SkPath::kInverseWinding_FillType); 300 301 const SkClipStack::Element* clipEntry; 302 SkClipStack::Iter iter; 303 iter.reset(clipStack, SkClipStack::Iter::kBottom_IterStart); 304 for (clipEntry = iter.next(); clipEntry; clipEntry = iter.next()) { 305 SkPath entryPath; 306 if (SkClipStack::Element::kEmpty_Type == clipEntry->getType()) { 307 outClipPath->reset(); 308 outClipPath->setFillType(SkPath::kInverseWinding_FillType); 309 continue; 310 } else { 311 clipEntry->asPath(&entryPath); 312 } 313 entryPath.transform(transform); 314 if (!apply_clip(clipEntry->getOp(), *outClipPath, entryPath, outClipPath)) { 315 return false; 316 } 317 } 318 319 if (outClipPath->isInverseFillType()) { 320 // The bounds are slightly outset to ensure this is correct in the 321 // face of floating-point accuracy and possible SkRegion bitmap 322 // approximations. 323 SkRect clipBounds = bounds; 324 clipBounds.outset(SK_Scalar1, SK_Scalar1); 325 if (!calculate_inverse_path(clipBounds, *outClipPath, outClipPath)) { 326 return false; 327 } 328 } 329 return true; 330 } 331 332 // TODO(vandebo): Take advantage of SkClipStack::getSaveCount(), the PDF 333 // graphic state stack, and the fact that we can know all the clips used 334 // on the page to optimize this. 335 void GraphicStackState::updateClip(const SkClipStack& clipStack, 336 const SkPoint& translation, 337 const SkRect& bounds) { 338 if (clipStack == currentEntry()->fClipStack) { 339 return; 340 } 341 342 while (fStackDepth > 0) { 343 pop(); 344 if (clipStack == currentEntry()->fClipStack) { 345 return; 346 } 347 } 348 push(); 349 350 currentEntry()->fClipStack = clipStack; 351 352 SkMatrix transform; 353 transform.setTranslate(translation.fX, translation.fY); 354 355 SkPath clipPath; 356 if (get_clip_stack_path(transform, clipStack, bounds, &clipPath)) { 357 SkPDFUtils::EmitPath(clipPath, SkPaint::kFill_Style, fContentStream); 358 SkPath::FillType clipFill = clipPath.getFillType(); 359 NOT_IMPLEMENTED(clipFill == SkPath::kInverseEvenOdd_FillType, false); 360 NOT_IMPLEMENTED(clipFill == SkPath::kInverseWinding_FillType, false); 361 if (clipFill == SkPath::kEvenOdd_FillType) { 362 fContentStream->writeText("W* n\n"); 363 } else { 364 fContentStream->writeText("W n\n"); 365 } 366 } 367 // If Op() fails (pathological case; e.g. input values are 368 // extremely large or NaN), emit no clip at all. 369 } 370 371 void GraphicStackState::updateMatrix(const SkMatrix& matrix) { 372 if (matrix == currentEntry()->fMatrix) { 373 return; 374 } 375 376 if (currentEntry()->fMatrix.getType() != SkMatrix::kIdentity_Mask) { 377 SkASSERT(fStackDepth > 0); 378 SkASSERT(fEntries[fStackDepth].fClipStack == 379 fEntries[fStackDepth -1].fClipStack); 380 pop(); 381 382 SkASSERT(currentEntry()->fMatrix.getType() == SkMatrix::kIdentity_Mask); 383 } 384 if (matrix.getType() == SkMatrix::kIdentity_Mask) { 385 return; 386 } 387 388 push(); 389 SkPDFUtils::AppendTransform(matrix, fContentStream); 390 currentEntry()->fMatrix = matrix; 391 } 392 393 void GraphicStackState::updateDrawingState(const SkPDFDevice::GraphicStateEntry& state) { 394 // PDF treats a shader as a color, so we only set one or the other. 395 if (state.fShaderIndex >= 0) { 396 if (state.fShaderIndex != currentEntry()->fShaderIndex) { 397 SkPDFUtils::ApplyPattern(state.fShaderIndex, fContentStream); 398 currentEntry()->fShaderIndex = state.fShaderIndex; 399 } 400 } else { 401 if (state.fColor != currentEntry()->fColor || 402 currentEntry()->fShaderIndex >= 0) { 403 emit_pdf_color(state.fColor, fContentStream); 404 fContentStream->writeText("RG "); 405 emit_pdf_color(state.fColor, fContentStream); 406 fContentStream->writeText("rg\n"); 407 currentEntry()->fColor = state.fColor; 408 currentEntry()->fShaderIndex = -1; 409 } 410 } 411 412 if (state.fGraphicStateIndex != currentEntry()->fGraphicStateIndex) { 413 SkPDFUtils::ApplyGraphicState(state.fGraphicStateIndex, fContentStream); 414 currentEntry()->fGraphicStateIndex = state.fGraphicStateIndex; 415 } 416 417 if (state.fTextScaleX) { 418 if (state.fTextScaleX != currentEntry()->fTextScaleX) { 419 SkScalar pdfScale = state.fTextScaleX * 1000; 420 SkPDFUtils::AppendScalar(pdfScale, fContentStream); 421 fContentStream->writeText(" Tz\n"); 422 currentEntry()->fTextScaleX = state.fTextScaleX; 423 } 424 if (state.fTextFill != currentEntry()->fTextFill) { 425 static_assert(SkPaint::kFill_Style == 0, "enum_must_match_value"); 426 static_assert(SkPaint::kStroke_Style == 1, "enum_must_match_value"); 427 static_assert(SkPaint::kStrokeAndFill_Style == 2, "enum_must_match_value"); 428 fContentStream->writeDecAsText(state.fTextFill); 429 fContentStream->writeText(" Tr\n"); 430 currentEntry()->fTextFill = state.fTextFill; 431 } 432 } 433 } 434 435 static bool not_supported_for_layers(const SkPaint& layerPaint) { 436 // PDF does not support image filters, so render them on CPU. 437 // Note that this rendering is done at "screen" resolution (100dpi), not 438 // printer resolution. 439 // TODO: It may be possible to express some filters natively using PDF 440 // to improve quality and file size (https://bug.skia.org/3043) 441 442 // TODO: should we return true if there is a colorfilter? 443 return layerPaint.getImageFilter() != nullptr; 444 } 445 446 SkBaseDevice* SkPDFDevice::onCreateDevice(const CreateInfo& cinfo, const SkPaint* layerPaint) { 447 if (layerPaint && not_supported_for_layers(*layerPaint)) { 448 // need to return a raster device, which we will detect in drawDevice() 449 return SkBitmapDevice::Create(cinfo.fInfo, SkSurfaceProps(0, kUnknown_SkPixelGeometry)); 450 } 451 SkISize size = SkISize::Make(cinfo.fInfo.width(), cinfo.fInfo.height()); 452 return new SkPDFDevice(size, fDocument); 453 } 454 455 SkPDFCanon* SkPDFDevice::getCanon() const { return fDocument->canon(); } 456 457 // A helper class to automatically finish a ContentEntry at the end of a 458 // drawing method and maintain the state needed between set up and finish. 459 class ScopedContentEntry { 460 public: 461 ScopedContentEntry(SkPDFDevice* device, 462 const SkClipStack& clipStack, 463 const SkMatrix& matrix, 464 const SkPaint& paint, 465 bool hasText = false) 466 : fDevice(device) 467 , fContentEntry(nullptr) 468 , fBlendMode(SkBlendMode::kSrcOver) 469 , fDstFormXObject(nullptr) 470 { 471 if (matrix.hasPerspective()) { 472 NOT_IMPLEMENTED(!matrix.hasPerspective(), false); 473 return; 474 } 475 fBlendMode = paint.getBlendMode(); 476 fContentEntry = 477 fDevice->setUpContentEntry(clipStack, matrix, paint, hasText, &fDstFormXObject); 478 } 479 ScopedContentEntry(SkPDFDevice* dev, const SkPaint& paint, bool hasText = false) 480 : ScopedContentEntry(dev, dev->cs(), dev->ctm(), paint, hasText) {} 481 482 ~ScopedContentEntry() { 483 if (fContentEntry) { 484 SkPath* shape = &fShape; 485 if (shape->isEmpty()) { 486 shape = nullptr; 487 } 488 fDevice->finishContentEntry(fBlendMode, std::move(fDstFormXObject), shape); 489 } 490 } 491 492 SkPDFDevice::ContentEntry* entry() { return fContentEntry; } 493 SkDynamicMemoryWStream* stream() { return &fContentEntry->fContent; } 494 495 /* Returns true when we explicitly need the shape of the drawing. */ 496 bool needShape() { 497 switch (fBlendMode) { 498 case SkBlendMode::kClear: 499 case SkBlendMode::kSrc: 500 case SkBlendMode::kSrcIn: 501 case SkBlendMode::kSrcOut: 502 case SkBlendMode::kDstIn: 503 case SkBlendMode::kDstOut: 504 case SkBlendMode::kSrcATop: 505 case SkBlendMode::kDstATop: 506 case SkBlendMode::kModulate: 507 return true; 508 default: 509 return false; 510 } 511 } 512 513 /* Returns true unless we only need the shape of the drawing. */ 514 bool needSource() { 515 if (fBlendMode == SkBlendMode::kClear) { 516 return false; 517 } 518 return true; 519 } 520 521 /* If the shape is different than the alpha component of the content, then 522 * setShape should be called with the shape. In particular, images and 523 * devices have rectangular shape. 524 */ 525 void setShape(const SkPath& shape) { 526 fShape = shape; 527 } 528 529 private: 530 SkPDFDevice* fDevice; 531 SkPDFDevice::ContentEntry* fContentEntry; 532 SkBlendMode fBlendMode; 533 sk_sp<SkPDFObject> fDstFormXObject; 534 SkPath fShape; 535 }; 536 537 //////////////////////////////////////////////////////////////////////////////// 538 539 SkPDFDevice::SkPDFDevice(SkISize pageSize, SkPDFDocument* doc) 540 : INHERITED(SkImageInfo::MakeUnknown(pageSize.width(), pageSize.height()), 541 SkSurfaceProps(0, kUnknown_SkPixelGeometry)) 542 , fPageSize(pageSize) 543 , fInitialTransform(SkMatrix::I()) 544 , fDocument(doc) 545 { 546 SkASSERT(pageSize.width() > 0); 547 SkASSERT(pageSize.height() > 0); 548 } 549 550 void SkPDFDevice::setFlip() { 551 // Skia generally uses the top left as the origin but PDF 552 // natively has the origin at the bottom left. This matrix 553 // corrects for that. But that only needs to be done once, we 554 // don't do it when layering. 555 fInitialTransform.setTranslate(0, SkIntToScalar(fPageSize.fHeight)); 556 fInitialTransform.preScale(SK_Scalar1, -SK_Scalar1); 557 } 558 559 SkPDFDevice::~SkPDFDevice() { 560 this->cleanUp(); 561 } 562 563 void SkPDFDevice::init() { 564 fContentEntries.reset(); 565 } 566 567 void SkPDFDevice::cleanUp() { 568 fGraphicStateResources.unrefAll(); 569 fXObjectResources.unrefAll(); 570 fFontResources.unrefAll(); 571 fShaderResources.unrefAll(); 572 } 573 574 void SkPDFDevice::drawAnnotation(const SkRect& rect, const char key[], SkData* value) { 575 if (!value) { 576 return; 577 } 578 if (rect.isEmpty()) { 579 if (!strcmp(SkAnnotationKeys::Define_Named_Dest_Key(), key)) { 580 SkPoint transformedPoint; 581 this->ctm().mapXY(rect.x(), rect.y(), &transformedPoint); 582 fNamedDestinations.emplace_back(NamedDestination{sk_ref_sp(value), transformedPoint}); 583 } 584 return; 585 } 586 // Convert to path to handle non-90-degree rotations. 587 SkPath path; 588 path.addRect(rect); 589 path.transform(this->ctm(), &path); 590 SkPath clip; 591 (void)this->cs().asPath(&clip); 592 Op(clip, path, kIntersect_SkPathOp, &path); 593 // PDF wants a rectangle only. 594 SkRect transformedRect = path.getBounds(); 595 if (transformedRect.isEmpty()) { 596 return; 597 } 598 if (!strcmp(SkAnnotationKeys::URL_Key(), key)) { 599 fLinkToURLs.emplace_back(RectWithData{transformedRect, sk_ref_sp(value)}); 600 } else if (!strcmp(SkAnnotationKeys::Link_Named_Dest_Key(), key)) { 601 fLinkToDestinations.emplace_back(RectWithData{transformedRect, sk_ref_sp(value)}); 602 } 603 } 604 605 void SkPDFDevice::drawPaint(const SkPaint& srcPaint) { 606 SkPaint newPaint = srcPaint; 607 remove_color_filter(&newPaint); 608 replace_srcmode_on_opaque_paint(&newPaint); 609 newPaint.setStyle(SkPaint::kFill_Style); 610 611 SkMatrix ctm = this->ctm(); 612 if (ctm.getType() & SkMatrix::kPerspective_Mask) { 613 if (newPaint.getShader()) { 614 transform_shader(&newPaint, ctm); 615 } 616 ctm = SkMatrix::I(); 617 } 618 ScopedContentEntry content(this, this->cs(), ctm, newPaint); 619 this->internalDrawPaint(newPaint, content.entry()); 620 } 621 622 void SkPDFDevice::internalDrawPaint(const SkPaint& paint, 623 SkPDFDevice::ContentEntry* contentEntry) { 624 if (!contentEntry) { 625 return; 626 } 627 SkRect bbox = SkRect::Make(fPageSize); 628 SkMatrix inverse; 629 if (!contentEntry->fState.fMatrix.invert(&inverse)) { 630 return; 631 } 632 inverse.mapRect(&bbox); 633 634 SkPDFUtils::AppendRectangle(bbox, &contentEntry->fContent); 635 SkPDFUtils::PaintPath(paint.getStyle(), SkPath::kWinding_FillType, 636 &contentEntry->fContent); 637 } 638 639 void SkPDFDevice::drawPoints(SkCanvas::PointMode mode, 640 size_t count, 641 const SkPoint* points, 642 const SkPaint& srcPaint) { 643 SkPaint passedPaint = srcPaint; 644 remove_color_filter(&passedPaint); 645 replace_srcmode_on_opaque_paint(&passedPaint); 646 if (SkCanvas::kPoints_PointMode != mode) { 647 passedPaint.setStyle(SkPaint::kStroke_Style); 648 } 649 if (count == 0) { 650 return; 651 } 652 653 // SkDraw::drawPoints converts to multiple calls to fDevice->drawPath. 654 // We only use this when there's a path effect because of the overhead 655 // of multiple calls to setUpContentEntry it causes. 656 if (passedPaint.getPathEffect()) { 657 if (this->cs().isEmpty(size(*this))) { 658 return; 659 } 660 draw_points(mode, count, points, passedPaint, 661 this->devClipBounds(), this->ctm(), this); 662 return; 663 } 664 665 const SkPaint* paint = &passedPaint; 666 SkPaint modifiedPaint; 667 668 if (mode == SkCanvas::kPoints_PointMode && 669 paint->getStrokeCap() != SkPaint::kRound_Cap) { 670 modifiedPaint = *paint; 671 paint = &modifiedPaint; 672 if (paint->getStrokeWidth()) { 673 // PDF won't draw a single point with square/butt caps because the 674 // orientation is ambiguous. Draw a rectangle instead. 675 modifiedPaint.setStyle(SkPaint::kFill_Style); 676 SkScalar strokeWidth = paint->getStrokeWidth(); 677 SkScalar halfStroke = SkScalarHalf(strokeWidth); 678 for (size_t i = 0; i < count; i++) { 679 SkRect r = SkRect::MakeXYWH(points[i].fX, points[i].fY, 0, 0); 680 r.inset(-halfStroke, -halfStroke); 681 this->drawRect(r, modifiedPaint); 682 } 683 return; 684 } else { 685 modifiedPaint.setStrokeCap(SkPaint::kRound_Cap); 686 } 687 } 688 689 ScopedContentEntry content(this, *paint); 690 if (!content.entry()) { 691 return; 692 } 693 SkDynamicMemoryWStream* contentStream = content.stream(); 694 switch (mode) { 695 case SkCanvas::kPolygon_PointMode: 696 SkPDFUtils::MoveTo(points[0].fX, points[0].fY, contentStream); 697 for (size_t i = 1; i < count; i++) { 698 SkPDFUtils::AppendLine(points[i].fX, points[i].fY, contentStream); 699 } 700 SkPDFUtils::StrokePath(contentStream); 701 break; 702 case SkCanvas::kLines_PointMode: 703 for (size_t i = 0; i < count/2; i++) { 704 SkPDFUtils::MoveTo(points[i * 2].fX, points[i * 2].fY, contentStream); 705 SkPDFUtils::AppendLine(points[i * 2 + 1].fX, points[i * 2 + 1].fY, contentStream); 706 SkPDFUtils::StrokePath(contentStream); 707 } 708 break; 709 case SkCanvas::kPoints_PointMode: 710 SkASSERT(paint->getStrokeCap() == SkPaint::kRound_Cap); 711 for (size_t i = 0; i < count; i++) { 712 SkPDFUtils::MoveTo(points[i].fX, points[i].fY, contentStream); 713 SkPDFUtils::ClosePath(contentStream); 714 SkPDFUtils::StrokePath(contentStream); 715 } 716 break; 717 default: 718 SkASSERT(false); 719 } 720 } 721 722 static sk_sp<SkPDFDict> create_link_annotation(const SkRect& translatedRect) { 723 auto annotation = sk_make_sp<SkPDFDict>("Annot"); 724 annotation->insertName("Subtype", "Link"); 725 annotation->insertInt("F", 4); // required by ISO 19005 726 727 auto border = sk_make_sp<SkPDFArray>(); 728 border->reserve(3); 729 border->appendInt(0); // Horizontal corner radius. 730 border->appendInt(0); // Vertical corner radius. 731 border->appendInt(0); // Width, 0 = no border. 732 annotation->insertObject("Border", std::move(border)); 733 734 auto rect = sk_make_sp<SkPDFArray>(); 735 rect->reserve(4); 736 rect->appendScalar(translatedRect.fLeft); 737 rect->appendScalar(translatedRect.fTop); 738 rect->appendScalar(translatedRect.fRight); 739 rect->appendScalar(translatedRect.fBottom); 740 annotation->insertObject("Rect", std::move(rect)); 741 742 return annotation; 743 } 744 745 static sk_sp<SkPDFDict> create_link_to_url(const SkData* urlData, const SkRect& r) { 746 sk_sp<SkPDFDict> annotation = create_link_annotation(r); 747 SkString url(static_cast<const char *>(urlData->data()), 748 urlData->size() - 1); 749 auto action = sk_make_sp<SkPDFDict>("Action"); 750 action->insertName("S", "URI"); 751 action->insertString("URI", url); 752 annotation->insertObject("A", std::move(action)); 753 return annotation; 754 } 755 756 static sk_sp<SkPDFDict> create_link_named_dest(const SkData* nameData, 757 const SkRect& r) { 758 sk_sp<SkPDFDict> annotation = create_link_annotation(r); 759 SkString name(static_cast<const char *>(nameData->data()), 760 nameData->size() - 1); 761 annotation->insertName("Dest", name); 762 return annotation; 763 } 764 765 void SkPDFDevice::drawRect(const SkRect& rect, 766 const SkPaint& srcPaint) { 767 SkPaint paint = srcPaint; 768 remove_color_filter(&paint); 769 replace_srcmode_on_opaque_paint(&paint); 770 SkRect r = rect; 771 r.sort(); 772 773 if (paint.getPathEffect() || paint.getMaskFilter()) { 774 if (this->cs().isEmpty(size(*this))) { 775 return; 776 } 777 SkPath path; 778 path.addRect(r); 779 this->drawPath(path, paint, nullptr, true); 780 return; 781 } 782 783 ScopedContentEntry content(this, paint); 784 if (!content.entry()) { 785 return; 786 } 787 SkPDFUtils::AppendRectangle(r, content.stream()); 788 SkPDFUtils::PaintPath(paint.getStyle(), SkPath::kWinding_FillType, content.stream()); 789 } 790 791 void SkPDFDevice::drawRRect(const SkRRect& rrect, 792 const SkPaint& srcPaint) { 793 SkPaint paint = srcPaint; 794 remove_color_filter(&paint); 795 replace_srcmode_on_opaque_paint(&paint); 796 SkPath path; 797 path.addRRect(rrect); 798 this->drawPath(path, paint, nullptr, true); 799 } 800 801 void SkPDFDevice::drawOval(const SkRect& oval, 802 const SkPaint& srcPaint) { 803 SkPaint paint = srcPaint; 804 remove_color_filter(&paint); 805 replace_srcmode_on_opaque_paint(&paint); 806 SkPath path; 807 path.addOval(oval); 808 this->drawPath(path, paint, nullptr, true); 809 } 810 811 void SkPDFDevice::drawPath(const SkPath& origPath, 812 const SkPaint& srcPaint, 813 const SkMatrix* prePathMatrix, 814 bool pathIsMutable) { 815 this->internalDrawPath( 816 this->cs(), this->ctm(), origPath, srcPaint, prePathMatrix, pathIsMutable); 817 } 818 819 void SkPDFDevice::internalDrawPathWithFilter(const SkClipStack& clipStack, 820 const SkMatrix& ctm, 821 const SkPath& origPath, 822 const SkPaint& origPaint, 823 const SkMatrix* prePathMatrix) { 824 SkASSERT(origPaint.getMaskFilter()); 825 SkPath path(origPath); 826 SkTCopyOnFirstWrite<SkPaint> paint(origPaint); 827 if (prePathMatrix) { 828 path.transform(*prePathMatrix, &path); 829 } 830 SkStrokeRec::InitStyle initStyle = paint->getFillPath(path, &path) 831 ? SkStrokeRec::kFill_InitStyle 832 : SkStrokeRec::kHairline_InitStyle; 833 path.transform(ctm, &path); 834 835 // TODO(halcanary): respect fDocument->rasterDpi(). 836 // SkScalar rasterScale = (float)rasterDpi / SkPDFUtils::kDpiForRasterScaleOne; 837 // Would it be easier to just change the device size (and pre-scale the canvas)? 838 SkIRect bounds = clipStack.bounds(size(*this)).roundOut(); 839 SkMask sourceMask; 840 if (!SkDraw::DrawToMask(path, &bounds, paint->getMaskFilter(), &SkMatrix::I(), 841 &sourceMask, SkMask::kComputeBoundsAndRenderImage_CreateMode, 842 initStyle)) { 843 return; 844 } 845 SkAutoMaskFreeImage srcAutoMaskFreeImage(sourceMask.fImage); 846 SkMask dstMask; 847 SkIPoint margin; 848 if (!paint->getMaskFilter()->filterMask(&dstMask, sourceMask, ctm, &margin)) { 849 return; 850 } 851 SkIRect dstMaskBounds = dstMask.fBounds; 852 sk_sp<SkImage> mask = mask_to_greyscale_image(&dstMask); 853 // PDF doesn't seem to allow masking vector graphics with an Image XObject. 854 // Must mask with a Form XObject. 855 sk_sp<SkPDFDevice> maskDevice = this->makeCongruentDevice(); 856 { 857 SkCanvas canvas(maskDevice.get()); 858 canvas.drawImage(mask, dstMaskBounds.x(), dstMaskBounds.y()); 859 } 860 if (!ctm.isIdentity() && paint->getShader()) { 861 transform_shader(paint.writable(), ctm); // Since we are using identity matrix. 862 } 863 ScopedContentEntry content(this, clipStack, SkMatrix::I(), *paint); 864 if (!content.entry()) { 865 return; 866 } 867 this->addSMaskGraphicState(std::move(maskDevice), content.stream()); 868 SkPDFUtils::AppendRectangle(SkRect::Make(dstMaskBounds), content.stream()); 869 SkPDFUtils::PaintPath(SkPaint::kFill_Style, path.getFillType(), content.stream()); 870 this->clearMaskOnGraphicState(content.stream()); 871 } 872 873 void SkPDFDevice::addSMaskGraphicState(sk_sp<SkPDFDevice> maskDevice, 874 SkDynamicMemoryWStream* contentStream) { 875 sk_sp<SkPDFDict> sMaskGS = SkPDFGraphicState::GetSMaskGraphicState( 876 maskDevice->makeFormXObjectFromDevice(), false, 877 SkPDFGraphicState::kLuminosity_SMaskMode, this->getCanon()); 878 SkPDFUtils::ApplyGraphicState(this->addGraphicStateResource(sMaskGS.get()), contentStream); 879 } 880 881 void SkPDFDevice::clearMaskOnGraphicState(SkDynamicMemoryWStream* contentStream) { 882 // The no-softmask graphic state is used to "turn off" the mask for later draw calls. 883 sk_sp<SkPDFDict>& noSMaskGS = this->getCanon()->fNoSmaskGraphicState; 884 if (!noSMaskGS) { 885 noSMaskGS = sk_make_sp<SkPDFDict>("ExtGState"); 886 noSMaskGS->insertName("SMask", "None"); 887 } 888 SkPDFUtils::ApplyGraphicState(this->addGraphicStateResource(noSMaskGS.get()), contentStream); 889 } 890 891 void SkPDFDevice::internalDrawPath(const SkClipStack& clipStack, 892 const SkMatrix& ctm, 893 const SkPath& origPath, 894 const SkPaint& srcPaint, 895 const SkMatrix* prePathMatrix, 896 bool pathIsMutable) { 897 SkPaint paint = srcPaint; 898 remove_color_filter(&paint); 899 replace_srcmode_on_opaque_paint(&paint); 900 SkPath modifiedPath; 901 SkPath* pathPtr = const_cast<SkPath*>(&origPath); 902 903 if (paint.getMaskFilter()) { 904 this->internalDrawPathWithFilter(clipStack, ctm, origPath, paint, prePathMatrix); 905 return; 906 } 907 908 SkMatrix matrix = ctm; 909 if (prePathMatrix) { 910 if (paint.getPathEffect() || paint.getStyle() != SkPaint::kFill_Style) { 911 if (!pathIsMutable) { 912 pathPtr = &modifiedPath; 913 pathIsMutable = true; 914 } 915 origPath.transform(*prePathMatrix, pathPtr); 916 } else { 917 matrix.preConcat(*prePathMatrix); 918 } 919 } 920 921 if (paint.getPathEffect()) { 922 if (clipStack.isEmpty(size(*this))) { 923 return; 924 } 925 if (!pathIsMutable) { 926 modifiedPath = origPath; 927 pathPtr = &modifiedPath; 928 pathIsMutable = true; 929 } 930 if (paint.getFillPath(*pathPtr, pathPtr)) { 931 paint.setStyle(SkPaint::kFill_Style); 932 } else { 933 paint.setStyle(SkPaint::kStroke_Style); 934 paint.setStrokeWidth(0); 935 } 936 paint.setPathEffect(nullptr); 937 } 938 939 if (this->handleInversePath(*pathPtr, paint, pathIsMutable, prePathMatrix)) { 940 return; 941 } 942 if (matrix.getType() & SkMatrix::kPerspective_Mask) { 943 if (!pathIsMutable) { 944 modifiedPath = origPath; 945 pathPtr = &modifiedPath; 946 pathIsMutable = true; 947 } 948 pathPtr->transform(matrix); 949 if (paint.getShader()) { 950 transform_shader(&paint, matrix); 951 } 952 matrix = SkMatrix::I(); 953 } 954 955 ScopedContentEntry content(this, clipStack, matrix, paint); 956 if (!content.entry()) { 957 return; 958 } 959 SkScalar matrixScale = matrix.mapRadius(1.0f); 960 SkScalar tolerance = matrixScale > 0.0f ? 0.25f / matrixScale : 0.25f; 961 bool consumeDegeratePathSegments = 962 paint.getStyle() == SkPaint::kFill_Style || 963 (paint.getStrokeCap() != SkPaint::kRound_Cap && 964 paint.getStrokeCap() != SkPaint::kSquare_Cap); 965 SkPDFUtils::EmitPath(*pathPtr, paint.getStyle(), consumeDegeratePathSegments, content.stream(), 966 tolerance); 967 SkPDFUtils::PaintPath(paint.getStyle(), pathPtr->getFillType(), content.stream()); 968 } 969 970 //////////////////////////////////////////////////////////////////////////////// 971 972 void SkPDFDevice::drawImageRect(const SkImage* image, 973 const SkRect* src, 974 const SkRect& dst, 975 const SkPaint& paint, 976 SkCanvas::SrcRectConstraint) { 977 SkASSERT(image); 978 this->internalDrawImageRect(SkKeyedImage(sk_ref_sp(const_cast<SkImage*>(image))), 979 src, dst, paint, this->ctm()); 980 } 981 982 void SkPDFDevice::drawBitmapRect(const SkBitmap& bm, 983 const SkRect* src, 984 const SkRect& dst, 985 const SkPaint& paint, 986 SkCanvas::SrcRectConstraint) { 987 SkASSERT(!bm.drawsNothing()); 988 this->internalDrawImageRect(SkKeyedImage(bm), src, dst, paint, this->ctm()); 989 } 990 991 void SkPDFDevice::drawBitmap(const SkBitmap& bm, SkScalar x, SkScalar y, const SkPaint& paint) { 992 SkASSERT(!bm.drawsNothing()); 993 auto r = SkRect::MakeXYWH(x, y, bm.width(), bm.height()); 994 this->internalDrawImageRect(SkKeyedImage(bm), nullptr, r, paint, this->ctm()); 995 } 996 997 void SkPDFDevice::drawSprite(const SkBitmap& bm, int x, int y, const SkPaint& paint) { 998 SkASSERT(!bm.drawsNothing()); 999 auto r = SkRect::MakeXYWH(x, y, bm.width(), bm.height()); 1000 this->internalDrawImageRect(SkKeyedImage(bm), nullptr, r, paint, SkMatrix::I()); 1001 } 1002 1003 void SkPDFDevice::drawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint& paint) { 1004 SkASSERT(image); 1005 auto r = SkRect::MakeXYWH(x, y, image->width(), image->height()); 1006 this->internalDrawImageRect(SkKeyedImage(sk_ref_sp(const_cast<SkImage*>(image))), 1007 nullptr, r, paint, this->ctm()); 1008 } 1009 1010 //////////////////////////////////////////////////////////////////////////////// 1011 1012 namespace { 1013 class GlyphPositioner { 1014 public: 1015 GlyphPositioner(SkDynamicMemoryWStream* content, 1016 SkScalar textSkewX, 1017 bool wideChars, 1018 bool defaultPositioning, 1019 SkPoint origin) 1020 : fContent(content) 1021 , fCurrentMatrixOrigin(origin) 1022 , fTextSkewX(textSkewX) 1023 , fWideChars(wideChars) 1024 , fDefaultPositioning(defaultPositioning) { 1025 } 1026 ~GlyphPositioner() { this->flush(); } 1027 void flush() { 1028 if (fInText) { 1029 fContent->writeText("> Tj\n"); 1030 fInText = false; 1031 } 1032 } 1033 void writeGlyph(SkPoint xy, 1034 SkScalar advanceWidth, 1035 uint16_t glyph) { 1036 if (!fInitialized) { 1037 // Flip the text about the x-axis to account for origin swap and include 1038 // the passed parameters. 1039 fContent->writeText("1 0 "); 1040 SkPDFUtils::AppendScalar(-fTextSkewX, fContent); 1041 fContent->writeText(" -1 "); 1042 SkPDFUtils::AppendScalar(fCurrentMatrixOrigin.x(), fContent); 1043 fContent->writeText(" "); 1044 SkPDFUtils::AppendScalar(fCurrentMatrixOrigin.y(), fContent); 1045 fContent->writeText(" Tm\n"); 1046 fCurrentMatrixOrigin.set(0.0f, 0.0f); 1047 fInitialized = true; 1048 } 1049 if (!fDefaultPositioning) { 1050 SkPoint position = xy - fCurrentMatrixOrigin; 1051 if (position != SkPoint{fXAdvance, 0}) { 1052 this->flush(); 1053 SkPDFUtils::AppendScalar(position.x(), fContent); 1054 fContent->writeText(" "); 1055 SkPDFUtils::AppendScalar(-position.y(), fContent); 1056 fContent->writeText(" Td "); 1057 fCurrentMatrixOrigin = xy; 1058 fXAdvance = 0; 1059 } 1060 fXAdvance += advanceWidth; 1061 } 1062 if (!fInText) { 1063 fContent->writeText("<"); 1064 fInText = true; 1065 } 1066 if (fWideChars) { 1067 SkPDFUtils::WriteUInt16BE(fContent, glyph); 1068 } else { 1069 SkASSERT(0 == glyph >> 8); 1070 SkPDFUtils::WriteUInt8(fContent, static_cast<uint8_t>(glyph)); 1071 } 1072 } 1073 1074 private: 1075 SkDynamicMemoryWStream* fContent; 1076 SkPoint fCurrentMatrixOrigin; 1077 SkScalar fXAdvance = 0.0f; 1078 SkScalar fTextSkewX; 1079 bool fWideChars; 1080 bool fInText = false; 1081 bool fInitialized = false; 1082 const bool fDefaultPositioning; 1083 }; 1084 1085 /** Given the m-to-n glyph-to-character mapping data (as returned by 1086 harfbuzz), iterate over the clusters. */ 1087 class Clusterator { 1088 public: 1089 Clusterator() : fClusters(nullptr), fUtf8Text(nullptr), fGlyphCount(0), fTextByteLength(0) {} 1090 explicit Clusterator(uint32_t glyphCount) 1091 : fClusters(nullptr) 1092 , fUtf8Text(nullptr) 1093 , fGlyphCount(glyphCount) 1094 , fTextByteLength(0) {} 1095 // The clusters[] array is an array of offsets into utf8Text[], 1096 // one offset for each glyph. See SkTextBlobBuilder for more info. 1097 Clusterator(const uint32_t* clusters, 1098 const char* utf8Text, 1099 uint32_t glyphCount, 1100 uint32_t textByteLength) 1101 : fClusters(clusters) 1102 , fUtf8Text(utf8Text) 1103 , fGlyphCount(glyphCount) 1104 , fTextByteLength(textByteLength) { 1105 // This is a cheap heuristic for /ReversedChars which seems to 1106 // work for clusters produced by HarfBuzz, which either 1107 // increase from zero (LTR) or decrease to zero (RTL). 1108 // "ReversedChars" is how PDF deals with RTL text. 1109 fReversedChars = 1110 fUtf8Text && fClusters && fGlyphCount && fClusters[0] != 0; 1111 } 1112 struct Cluster { 1113 const char* fUtf8Text; 1114 uint32_t fTextByteLength; 1115 uint32_t fGlyphIndex; 1116 uint32_t fGlyphCount; 1117 explicit operator bool() const { return fGlyphCount != 0; } 1118 }; 1119 // True if this looks like right-to-left text. 1120 bool reversedChars() const { return fReversedChars; } 1121 Cluster next() { 1122 if ((!fUtf8Text || !fClusters) && fGlyphCount) { 1123 // These glyphs have no text. Treat as one "cluster". 1124 uint32_t glyphCount = fGlyphCount; 1125 fGlyphCount = 0; 1126 return Cluster{nullptr, 0, 0, glyphCount}; 1127 } 1128 if (fGlyphCount == 0 || fTextByteLength == 0) { 1129 return Cluster{nullptr, 0, 0, 0}; // empty 1130 } 1131 SkASSERT(fUtf8Text); 1132 SkASSERT(fClusters); 1133 uint32_t cluster = fClusters[0]; 1134 if (cluster >= fTextByteLength) { 1135 return Cluster{nullptr, 0, 0, 0}; // bad input. 1136 } 1137 uint32_t glyphsInCluster = 1; 1138 while (glyphsInCluster < fGlyphCount && 1139 fClusters[glyphsInCluster] == cluster) { 1140 ++glyphsInCluster; 1141 } 1142 SkASSERT(glyphsInCluster <= fGlyphCount); 1143 uint32_t textLength = 0; 1144 if (glyphsInCluster == fGlyphCount) { 1145 // consumes rest of glyphs and rest of text 1146 if (kInvalidCluster == fPreviousCluster) { // LTR text or single cluster 1147 textLength = fTextByteLength - cluster; 1148 } else { // RTL text; last cluster. 1149 SkASSERT(fPreviousCluster < fTextByteLength); 1150 if (fPreviousCluster <= cluster) { // bad input. 1151 return Cluster{nullptr, 0, 0, 0}; 1152 } 1153 textLength = fPreviousCluster - cluster; 1154 } 1155 fGlyphCount = 0; 1156 return Cluster{fUtf8Text + cluster, 1157 textLength, 1158 fGlyphIndex, 1159 glyphsInCluster}; 1160 } 1161 SkASSERT(glyphsInCluster < fGlyphCount); 1162 uint32_t nextCluster = fClusters[glyphsInCluster]; 1163 if (nextCluster >= fTextByteLength) { 1164 return Cluster{nullptr, 0, 0, 0}; // bad input. 1165 } 1166 if (nextCluster > cluster) { // LTR text 1167 if (kInvalidCluster != fPreviousCluster) { 1168 return Cluster{nullptr, 0, 0, 0}; // bad input. 1169 } 1170 textLength = nextCluster - cluster; 1171 } else { // RTL text 1172 SkASSERT(nextCluster < cluster); 1173 if (kInvalidCluster == fPreviousCluster) { // first cluster 1174 textLength = fTextByteLength - cluster; 1175 } else { // later cluster 1176 if (fPreviousCluster <= cluster) { 1177 return Cluster{nullptr, 0, 0, 0}; // bad input. 1178 } 1179 textLength = fPreviousCluster - cluster; 1180 } 1181 fPreviousCluster = cluster; 1182 } 1183 uint32_t glyphIndex = fGlyphIndex; 1184 fGlyphCount -= glyphsInCluster; 1185 fGlyphIndex += glyphsInCluster; 1186 fClusters += glyphsInCluster; 1187 return Cluster{fUtf8Text + cluster, 1188 textLength, 1189 glyphIndex, 1190 glyphsInCluster}; 1191 } 1192 1193 private: 1194 static constexpr uint32_t kInvalidCluster = 0xFFFFFFFF; 1195 const uint32_t* fClusters; 1196 const char* fUtf8Text; 1197 uint32_t fGlyphCount; 1198 uint32_t fTextByteLength; 1199 uint32_t fGlyphIndex = 0; 1200 uint32_t fPreviousCluster = kInvalidCluster; 1201 bool fReversedChars = false; 1202 }; 1203 1204 struct TextStorage { 1205 SkAutoTMalloc<char> fUtf8textStorage; 1206 SkAutoTMalloc<uint32_t> fClusterStorage; 1207 SkAutoTMalloc<SkGlyphID> fGlyphStorage; 1208 }; 1209 } // namespace 1210 1211 /** Given some unicode text (as passed to drawText(), convert to 1212 glyphs (via primitive shaping), while preserving 1213 glyph-to-character mapping information. */ 1214 static Clusterator make_clusterator( 1215 const void* sourceText, 1216 size_t sourceByteCount, 1217 const SkPaint& paint, 1218 TextStorage* storage, 1219 int glyphCount) { 1220 SkASSERT(SkPaint::kGlyphID_TextEncoding != paint.getTextEncoding()); 1221 SkASSERT(glyphCount == paint.textToGlyphs(sourceText, sourceByteCount, nullptr)); 1222 SkASSERT(glyphCount > 0); 1223 storage->fGlyphStorage.reset(SkToSizeT(glyphCount)); 1224 (void)paint.textToGlyphs(sourceText, sourceByteCount, storage->fGlyphStorage.get()); 1225 storage->fClusterStorage.reset(SkToSizeT(glyphCount)); 1226 uint32_t* clusters = storage->fClusterStorage.get(); 1227 uint32_t utf8ByteCount = 0; 1228 const char* utf8Text = nullptr; 1229 switch (paint.getTextEncoding()) { 1230 case SkPaint::kUTF8_TextEncoding: { 1231 const char* txtPtr = (const char*)sourceText; 1232 for (int i = 0; i < glyphCount; ++i) { 1233 clusters[i] = SkToU32(txtPtr - (const char*)sourceText); 1234 txtPtr += SkUTF8_LeadByteToCount(*(const unsigned char*)txtPtr); 1235 SkASSERT(txtPtr <= (const char*)sourceText + sourceByteCount); 1236 } 1237 SkASSERT(txtPtr == (const char*)sourceText + sourceByteCount); 1238 utf8ByteCount = SkToU32(sourceByteCount); 1239 utf8Text = (const char*)sourceText; 1240 break; 1241 } 1242 case SkPaint::kUTF16_TextEncoding: { 1243 const uint16_t* utf16ptr = (const uint16_t*)sourceText; 1244 int utf16count = SkToInt(sourceByteCount / sizeof(uint16_t)); 1245 utf8ByteCount = SkToU32(SkUTF16_ToUTF8(utf16ptr, utf16count)); 1246 storage->fUtf8textStorage.reset(utf8ByteCount); 1247 char* txtPtr = storage->fUtf8textStorage.get(); 1248 utf8Text = txtPtr; 1249 int clusterIndex = 0; 1250 while (utf16ptr < (const uint16_t*)sourceText + utf16count) { 1251 clusters[clusterIndex++] = SkToU32(txtPtr - utf8Text); 1252 SkUnichar uni = SkUTF16_NextUnichar(&utf16ptr); 1253 txtPtr += SkUTF8_FromUnichar(uni, txtPtr); 1254 } 1255 SkASSERT(clusterIndex == glyphCount); 1256 SkASSERT(txtPtr == storage->fUtf8textStorage.get() + utf8ByteCount); 1257 SkASSERT(utf16ptr == (const uint16_t*)sourceText + utf16count); 1258 break; 1259 } 1260 case SkPaint::kUTF32_TextEncoding: { 1261 const SkUnichar* utf32 = (const SkUnichar*)sourceText; 1262 int utf32count = SkToInt(sourceByteCount / sizeof(SkUnichar)); 1263 SkASSERT(glyphCount == utf32count); 1264 for (int i = 0; i < utf32count; ++i) { 1265 utf8ByteCount += SkToU32(SkUTF8_FromUnichar(utf32[i])); 1266 } 1267 storage->fUtf8textStorage.reset(SkToSizeT(utf8ByteCount)); 1268 char* txtPtr = storage->fUtf8textStorage.get(); 1269 utf8Text = txtPtr; 1270 for (int i = 0; i < utf32count; ++i) { 1271 clusters[i] = SkToU32(txtPtr - utf8Text); 1272 txtPtr += SkUTF8_FromUnichar(utf32[i], txtPtr); 1273 } 1274 break; 1275 } 1276 default: 1277 SkDEBUGFAIL(""); 1278 break; 1279 } 1280 return Clusterator(clusters, utf8Text, SkToU32(glyphCount), utf8ByteCount); 1281 } 1282 1283 static SkUnichar map_glyph(const SkTDArray<SkUnichar>& glyphToUnicode, SkGlyphID glyph) { 1284 return SkToInt(glyph) < glyphToUnicode.count() ? glyphToUnicode[SkToInt(glyph)] : -1; 1285 } 1286 1287 static void update_font(SkWStream* wStream, int fontIndex, SkScalar textSize) { 1288 wStream->writeText("/"); 1289 char prefix = SkPDFResourceDict::GetResourceTypePrefix(SkPDFResourceDict::kFont_ResourceType); 1290 wStream->write(&prefix, 1); 1291 wStream->writeDecAsText(fontIndex); 1292 wStream->writeText(" "); 1293 SkPDFUtils::AppendScalar(textSize, wStream); 1294 wStream->writeText(" Tf\n"); 1295 } 1296 1297 static SkPath draw_text_as_path(const void* sourceText, size_t sourceByteCount, 1298 const SkScalar pos[], SkTextBlob::GlyphPositioning positioning, 1299 SkPoint offset, const SkPaint& srcPaint) { 1300 SkPath path; 1301 int glyphCount; 1302 SkAutoTMalloc<SkPoint> tmpPoints; 1303 switch (positioning) { 1304 case SkTextBlob::kDefault_Positioning: 1305 srcPaint.getTextPath(sourceText, sourceByteCount, offset.x(), offset.y(), &path); 1306 break; 1307 case SkTextBlob::kHorizontal_Positioning: 1308 glyphCount = srcPaint.countText(sourceText, sourceByteCount); 1309 tmpPoints.realloc(glyphCount); 1310 for (int i = 0; i < glyphCount; ++i) { 1311 tmpPoints[i] = {pos[i] + offset.x(), offset.y()}; 1312 } 1313 srcPaint.getPosTextPath(sourceText, sourceByteCount, tmpPoints.get(), &path); 1314 break; 1315 case SkTextBlob::kFull_Positioning: 1316 srcPaint.getPosTextPath(sourceText, sourceByteCount, (const SkPoint*)pos, &path); 1317 path.offset(offset.x(), offset.y()); 1318 break; 1319 } 1320 return path; 1321 } 1322 1323 static bool has_outline_glyph(SkGlyphID gid, SkGlyphCache* cache) { 1324 const SkGlyph& glyph = cache->getGlyphIDMetrics(gid); 1325 const SkPath* path = cache->findPath(glyph); 1326 return (path && !path->isEmpty()) || (glyph.fWidth == 0 && glyph.fHeight == 0); 1327 } 1328 1329 static SkRect get_glyph_bounds_device_space(SkGlyphID gid, SkGlyphCache* cache, 1330 SkScalar xScale, SkScalar yScale, 1331 SkPoint xy, const SkMatrix& ctm) { 1332 const SkGlyph& glyph = cache->getGlyphIDMetrics(gid); 1333 SkRect glyphBounds = {glyph.fLeft * xScale, 1334 glyph.fTop * yScale, 1335 (glyph.fLeft + glyph.fWidth) * xScale, 1336 (glyph.fTop + glyph.fHeight) * yScale}; 1337 glyphBounds.offset(xy); 1338 ctm.mapRect(&glyphBounds); // now in dev space. 1339 return glyphBounds; 1340 } 1341 1342 static bool contains(const SkRect& r, SkPoint p) { 1343 return r.left() <= p.x() && p.x() <= r.right() && 1344 r.top() <= p.y() && p.y() <= r.bottom(); 1345 } 1346 1347 static sk_sp<SkImage> image_from_mask(const SkMask& mask) { 1348 if (!mask.fImage) { 1349 return nullptr; 1350 } 1351 SkIRect bounds = mask.fBounds; 1352 SkBitmap bm; 1353 switch (mask.fFormat) { 1354 case SkMask::kBW_Format: 1355 bm.allocPixels(SkImageInfo::MakeA8(bounds.width(), bounds.height())); 1356 for (int y = 0; y < bm.height(); ++y) { 1357 for (int x8 = 0; x8 < bm.width(); x8 += 8) { 1358 uint8_t v = *mask.getAddr1(x8 + bounds.x(), y + bounds.y()); 1359 int e = SkTMin(x8 + 8, bm.width()); 1360 for (int x = x8; x < e; ++x) { 1361 *bm.getAddr8(x, y) = (v >> (x & 0x7)) & 0x1 ? 0xFF : 0x00; 1362 } 1363 } 1364 } 1365 bm.setImmutable(); 1366 return SkImage::MakeFromBitmap(bm); 1367 case SkMask::kA8_Format: 1368 bm.installPixels(SkImageInfo::MakeA8(bounds.width(), bounds.height()), 1369 mask.fImage, mask.fRowBytes); 1370 return SkMakeImageFromRasterBitmap(bm, kAlways_SkCopyPixelsMode); 1371 case SkMask::kARGB32_Format: 1372 bm.installPixels(SkImageInfo::MakeN32Premul(bounds.width(), bounds.height()), 1373 mask.fImage, mask.fRowBytes); 1374 return SkMakeImageFromRasterBitmap(bm, kAlways_SkCopyPixelsMode); 1375 case SkMask::k3D_Format: 1376 SkASSERT(false); 1377 return nullptr; 1378 case SkMask::kLCD16_Format: 1379 SkASSERT(false); 1380 return nullptr; 1381 default: 1382 SkASSERT(false); 1383 return nullptr; 1384 } 1385 } 1386 1387 void SkPDFDevice::internalDrawText( 1388 const void* sourceText, size_t sourceByteCount, 1389 const SkScalar pos[], SkTextBlob::GlyphPositioning positioning, 1390 SkPoint offset, const SkPaint& srcPaint, const uint32_t* clusters, 1391 uint32_t textByteLength, const char* utf8Text) { 1392 if (0 == sourceByteCount || !sourceText) { 1393 return; 1394 } 1395 if (this->cs().isEmpty(size(*this))) { 1396 return; 1397 } 1398 NOT_IMPLEMENTED(srcPaint.isVerticalText(), false); 1399 if (srcPaint.isVerticalText()) { 1400 // Don't pretend we support drawing vertical text. It is not 1401 // clear to me how to switch to "vertical writing" mode in PDF. 1402 // Currently neither Chromium or Android set this flag. 1403 // https://bug.skia.org/5665 1404 } 1405 if (srcPaint.getPathEffect() 1406 || srcPaint.getMaskFilter() 1407 || SkPaint::kFill_Style != srcPaint.getStyle()) { 1408 // Stroked Text doesn't work well with Type3 fonts. 1409 SkPath path = draw_text_as_path(sourceText, sourceByteCount, pos, 1410 positioning, offset, srcPaint); 1411 this->drawPath(path, srcPaint, nullptr, true); 1412 return; 1413 } 1414 SkPaint paint = calculate_text_paint(srcPaint); 1415 remove_color_filter(&paint); 1416 replace_srcmode_on_opaque_paint(&paint); 1417 if (!paint.getTypeface()) { 1418 paint.setTypeface(SkTypeface::MakeDefault()); 1419 } 1420 SkTypeface* typeface = paint.getTypeface(); 1421 if (!typeface) { 1422 SkDebugf("SkPDF: SkTypeface::MakeDefault() returned nullptr.\n"); 1423 return; 1424 } 1425 1426 const SkAdvancedTypefaceMetrics* metrics = 1427 SkPDFFont::GetMetrics(typeface, fDocument->canon()); 1428 if (!metrics) { 1429 return; 1430 } 1431 int glyphCount = paint.textToGlyphs(sourceText, sourceByteCount, nullptr); 1432 if (glyphCount <= 0) { 1433 return; 1434 } 1435 1436 // These three heap buffers are only used in the case where no glyphs 1437 // are passed to drawText() (most clients pass glyphs or a textblob). 1438 TextStorage storage; 1439 const SkGlyphID* glyphs = nullptr; 1440 Clusterator clusterator; 1441 if (textByteLength > 0) { 1442 SkASSERT(glyphCount == SkToInt(sourceByteCount / sizeof(SkGlyphID))); 1443 glyphs = (const SkGlyphID*)sourceText; 1444 clusterator = Clusterator(clusters, utf8Text, SkToU32(glyphCount), textByteLength); 1445 SkASSERT(clusters); 1446 SkASSERT(utf8Text); 1447 SkASSERT(srcPaint.getTextEncoding() == SkPaint::kGlyphID_TextEncoding); 1448 SkASSERT(glyphCount == paint.textToGlyphs(sourceText, sourceByteCount, nullptr)); 1449 } else if (SkPaint::kGlyphID_TextEncoding == srcPaint.getTextEncoding()) { 1450 SkASSERT(glyphCount == SkToInt(sourceByteCount / sizeof(SkGlyphID))); 1451 glyphs = (const SkGlyphID*)sourceText; 1452 clusterator = Clusterator(SkToU32(glyphCount)); 1453 SkASSERT(glyphCount == paint.textToGlyphs(sourceText, sourceByteCount, nullptr)); 1454 SkASSERT(nullptr == clusters); 1455 SkASSERT(nullptr == utf8Text); 1456 } else { 1457 SkASSERT(nullptr == clusters); 1458 SkASSERT(nullptr == utf8Text); 1459 clusterator = make_clusterator(sourceText, sourceByteCount, srcPaint, 1460 &storage, glyphCount); 1461 glyphs = storage.fGlyphStorage; 1462 } 1463 bool defaultPositioning = (positioning == SkTextBlob::kDefault_Positioning); 1464 paint.setHinting(SkPaint::kNo_Hinting); 1465 1466 int emSize; 1467 SkAutoGlyphCache glyphCache = SkPDFFont::MakeVectorCache(typeface, &emSize); 1468 1469 SkScalar textSize = paint.getTextSize(); 1470 SkScalar advanceScale = textSize * paint.getTextScaleX() / emSize; 1471 1472 // textScaleX and textScaleY are used to get a conservative bounding box for glyphs. 1473 SkScalar textScaleY = textSize / emSize; 1474 SkScalar textScaleX = advanceScale + paint.getTextSkewX() * textScaleY; 1475 1476 SkPaint::Align alignment = paint.getTextAlign(); 1477 float alignmentFactor = SkPaint::kLeft_Align == alignment ? 0.0f : 1478 SkPaint::kCenter_Align == alignment ? -0.5f : 1479 /* SkPaint::kRight_Align */ -1.0f; 1480 if (defaultPositioning && alignment != SkPaint::kLeft_Align) { 1481 SkScalar advance = 0; 1482 for (int i = 0; i < glyphCount; ++i) { 1483 advance += advanceScale * glyphCache->getGlyphIDAdvance(glyphs[i]).fAdvanceX; 1484 } 1485 offset.offset(alignmentFactor * advance, 0); 1486 } 1487 SkRect clipStackBounds = this->cs().bounds(size(*this)); 1488 struct PositionedGlyph { 1489 SkPoint fPos; 1490 SkGlyphID fGlyph; 1491 }; 1492 SkTArray<PositionedGlyph> fMissingGlyphs; 1493 { 1494 ScopedContentEntry content(this, paint, true); 1495 if (!content.entry()) { 1496 return; 1497 } 1498 SkDynamicMemoryWStream* out = content.stream(); 1499 const SkTDArray<SkUnichar>& glyphToUnicode = metrics->fGlyphToUnicode; 1500 1501 out->writeText("BT\n"); 1502 SK_AT_SCOPE_EXIT(out->writeText("ET\n")); 1503 1504 const SkGlyphID maxGlyphID = SkToU16(typeface->countGlyphs() - 1); 1505 1506 bool multiByteGlyphs = SkPDFFont::IsMultiByte(SkPDFFont::FontType(*metrics)); 1507 if (clusterator.reversedChars()) { 1508 out->writeText("/ReversedChars BMC\n"); 1509 } 1510 SK_AT_SCOPE_EXIT(if (clusterator.reversedChars()) { out->writeText("EMC\n"); } ); 1511 GlyphPositioner glyphPositioner(out, 1512 paint.getTextSkewX(), 1513 multiByteGlyphs, 1514 defaultPositioning, 1515 offset); 1516 SkPDFFont* font = nullptr; 1517 1518 while (Clusterator::Cluster c = clusterator.next()) { 1519 int index = c.fGlyphIndex; 1520 int glyphLimit = index + c.fGlyphCount; 1521 1522 bool actualText = false; 1523 SK_AT_SCOPE_EXIT(if (actualText) { 1524 glyphPositioner.flush(); 1525 out->writeText("EMC\n"); 1526 }); 1527 if (c.fUtf8Text) { // real cluster 1528 // Check if `/ActualText` needed. 1529 const char* textPtr = c.fUtf8Text; 1530 const char* textEnd = c.fUtf8Text + c.fTextByteLength; 1531 SkUnichar unichar = SkUTF8_NextUnicharWithError(&textPtr, textEnd); 1532 if (unichar < 0) { 1533 return; 1534 } 1535 if (textPtr < textEnd || // more characters left 1536 glyphLimit > index + 1 || // toUnicode wouldn't work 1537 unichar != map_glyph(glyphToUnicode, glyphs[index])) // test single Unichar map 1538 { 1539 glyphPositioner.flush(); 1540 out->writeText("/Span<</ActualText <"); 1541 SkPDFUtils::WriteUTF16beHex(out, 0xFEFF); // U+FEFF = BYTE ORDER MARK 1542 // the BOM marks this text as UTF-16BE, not PDFDocEncoding. 1543 SkPDFUtils::WriteUTF16beHex(out, unichar); // first char 1544 while (textPtr < textEnd) { 1545 unichar = SkUTF8_NextUnicharWithError(&textPtr, textEnd); 1546 if (unichar < 0) { 1547 break; 1548 } 1549 SkPDFUtils::WriteUTF16beHex(out, unichar); 1550 } 1551 out->writeText("> >> BDC\n"); // begin marked-content sequence 1552 // with an associated property list. 1553 actualText = true; 1554 } 1555 } 1556 for (; index < glyphLimit; ++index) { 1557 SkGlyphID gid = glyphs[index]; 1558 if (gid > maxGlyphID) { 1559 continue; 1560 } 1561 if (!font || !font->hasGlyph(gid)) { 1562 // Not yet specified font or need to switch font. 1563 int fontIndex = this->getFontResourceIndex(typeface, gid); 1564 // All preconditions for SkPDFFont::GetFontResource are met. 1565 SkASSERT(fontIndex >= 0); 1566 if (fontIndex < 0) { 1567 return; 1568 } 1569 glyphPositioner.flush(); 1570 update_font(out, fontIndex, textSize); 1571 font = fFontResources[fontIndex]; 1572 SkASSERT(font); // All preconditions for SkPDFFont::GetFontResource are met. 1573 if (!font) { 1574 return; 1575 } 1576 SkASSERT(font->multiByteGlyphs() == multiByteGlyphs); 1577 } 1578 SkPoint xy = {0, 0}; 1579 SkScalar advance = advanceScale * glyphCache->getGlyphIDAdvance(gid).fAdvanceX; 1580 if (!defaultPositioning) { 1581 xy = SkTextBlob::kFull_Positioning == positioning 1582 ? SkPoint{pos[2 * index], pos[2 * index + 1]} 1583 : SkPoint{pos[index], 0}; 1584 if (alignment != SkPaint::kLeft_Align) { 1585 xy.offset(alignmentFactor * advance, 0); 1586 } 1587 // Do a glyph-by-glyph bounds-reject if positions are absolute. 1588 SkRect glyphBounds = get_glyph_bounds_device_space( 1589 gid, glyphCache.get(), textScaleX, textScaleY, 1590 xy + offset, this->ctm()); 1591 if (glyphBounds.isEmpty()) { 1592 if (!contains(clipStackBounds, {glyphBounds.x(), glyphBounds.y()})) { 1593 continue; 1594 } 1595 } else { 1596 if (!clipStackBounds.intersects(glyphBounds)) { 1597 continue; // reject glyphs as out of bounds 1598 } 1599 } 1600 if (!has_outline_glyph(gid, glyphCache.get())) { 1601 fMissingGlyphs.push_back({xy + offset, gid}); 1602 } 1603 } else { 1604 if (!has_outline_glyph(gid, glyphCache.get())) { 1605 fMissingGlyphs.push_back({offset, gid}); 1606 } 1607 offset += SkPoint{advance, 0}; 1608 } 1609 font->noteGlyphUsage(gid); 1610 1611 SkGlyphID encodedGlyph = multiByteGlyphs ? gid : font->glyphToPDFFontEncoding(gid); 1612 glyphPositioner.writeGlyph(xy, advance, encodedGlyph); 1613 } 1614 } 1615 } 1616 if (fMissingGlyphs.count() > 0) { 1617 // Fall back on images. 1618 SkPaint scaledGlyphCachePaint; 1619 scaledGlyphCachePaint.setTextSize(paint.getTextSize()); 1620 scaledGlyphCachePaint.setTextScaleX(paint.getTextScaleX()); 1621 scaledGlyphCachePaint.setTextSkewX(paint.getTextSkewX()); 1622 scaledGlyphCachePaint.setTypeface(sk_ref_sp(typeface)); 1623 SkAutoGlyphCache scaledGlyphCache(scaledGlyphCachePaint, nullptr, nullptr); 1624 SkTHashMap<SkPDFCanon::BitmapGlyphKey, SkPDFCanon::BitmapGlyph>* map = 1625 &this->getCanon()->fBitmapGlyphImages; 1626 for (PositionedGlyph positionedGlyph : fMissingGlyphs) { 1627 SkPDFCanon::BitmapGlyphKey key = {typeface->uniqueID(), 1628 paint.getTextSize(), 1629 paint.getTextScaleX(), 1630 paint.getTextSkewX(), 1631 positionedGlyph.fGlyph, 1632 0}; 1633 SkImage* img = nullptr; 1634 SkIPoint imgOffset = {0, 0}; 1635 if (SkPDFCanon::BitmapGlyph* ptr = map->find(key)) { 1636 img = ptr->fImage.get(); 1637 imgOffset = ptr->fOffset; 1638 } else { 1639 (void)scaledGlyphCache->findImage( 1640 scaledGlyphCache->getGlyphIDMetrics(positionedGlyph.fGlyph)); 1641 SkMask mask; 1642 scaledGlyphCache->getGlyphIDMetrics(positionedGlyph.fGlyph).toMask(&mask); 1643 imgOffset = {mask.fBounds.x(), mask.fBounds.y()}; 1644 img = map->set(key, {image_from_mask(mask), imgOffset})->fImage.get(); 1645 } 1646 if (img) { 1647 SkPoint pt = positionedGlyph.fPos + 1648 SkPoint{(SkScalar)imgOffset.x(), (SkScalar)imgOffset.y()}; 1649 this->drawImage(img, pt.x(), pt.y(), srcPaint); 1650 } 1651 } 1652 } 1653 } 1654 1655 void SkPDFDevice::drawText(const void* text, size_t len, 1656 SkScalar x, SkScalar y, const SkPaint& paint) { 1657 this->internalDrawText(text, len, nullptr, SkTextBlob::kDefault_Positioning, 1658 SkPoint{x, y}, paint, nullptr, 0, nullptr); 1659 } 1660 1661 void SkPDFDevice::drawPosText(const void* text, size_t len, 1662 const SkScalar pos[], int scalarsPerPos, 1663 const SkPoint& offset, const SkPaint& paint) { 1664 this->internalDrawText(text, len, pos, (SkTextBlob::GlyphPositioning)scalarsPerPos, 1665 offset, paint, nullptr, 0, nullptr); 1666 } 1667 1668 void SkPDFDevice::drawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y, 1669 const SkPaint &paint, SkDrawFilter* drawFilter) { 1670 for (SkTextBlobRunIterator it(blob); !it.done(); it.next()) { 1671 SkPaint runPaint(paint); 1672 it.applyFontToPaint(&runPaint); 1673 if (drawFilter && !drawFilter->filter(&runPaint, SkDrawFilter::kText_Type)) { 1674 continue; 1675 } 1676 runPaint.setFlags(this->filterTextFlags(runPaint)); 1677 SkPoint offset = it.offset() + SkPoint{x, y}; 1678 this->internalDrawText(it.glyphs(), sizeof(SkGlyphID) * it.glyphCount(), 1679 it.pos(), it.positioning(), offset, runPaint, 1680 it.clusters(), it.textSize(), it.text()); 1681 } 1682 } 1683 1684 void SkPDFDevice::drawVertices(const SkVertices*, SkBlendMode, const SkPaint&) { 1685 if (this->cs().isEmpty(size(*this))) { 1686 return; 1687 } 1688 // TODO: implement drawVertices 1689 } 1690 1691 void SkPDFDevice::drawDevice(SkBaseDevice* device, int x, int y, const SkPaint& paint) { 1692 SkASSERT(!paint.getImageFilter()); 1693 1694 // Check if the source device is really a bitmapdevice (because that's what we returned 1695 // from createDevice (likely due to an imagefilter) 1696 SkPixmap pmap; 1697 if (device->peekPixels(&pmap)) { 1698 SkBitmap bitmap; 1699 bitmap.installPixels(pmap); 1700 this->drawSprite(bitmap, x, y, paint); 1701 return; 1702 } 1703 1704 // our onCreateCompatibleDevice() always creates SkPDFDevice subclasses. 1705 SkPDFDevice* pdfDevice = static_cast<SkPDFDevice*>(device); 1706 1707 SkScalar scalarX = SkIntToScalar(x); 1708 SkScalar scalarY = SkIntToScalar(y); 1709 for (const RectWithData& l : pdfDevice->fLinkToURLs) { 1710 SkRect r = l.rect.makeOffset(scalarX, scalarY); 1711 fLinkToURLs.emplace_back(RectWithData{r, l.data}); 1712 } 1713 for (const RectWithData& l : pdfDevice->fLinkToDestinations) { 1714 SkRect r = l.rect.makeOffset(scalarX, scalarY); 1715 fLinkToDestinations.emplace_back(RectWithData{r, l.data}); 1716 } 1717 for (const NamedDestination& d : pdfDevice->fNamedDestinations) { 1718 SkPoint p = d.point + SkPoint::Make(scalarX, scalarY); 1719 fNamedDestinations.emplace_back(NamedDestination{d.nameData, p}); 1720 } 1721 1722 if (pdfDevice->isContentEmpty()) { 1723 return; 1724 } 1725 1726 SkMatrix matrix = SkMatrix::MakeTrans(SkIntToScalar(x), SkIntToScalar(y)); 1727 ScopedContentEntry content(this, this->cs(), matrix, paint); 1728 if (!content.entry()) { 1729 return; 1730 } 1731 if (content.needShape()) { 1732 SkPath shape; 1733 shape.addRect(SkRect::MakeXYWH(SkIntToScalar(x), SkIntToScalar(y), 1734 SkIntToScalar(device->width()), 1735 SkIntToScalar(device->height()))); 1736 content.setShape(shape); 1737 } 1738 if (!content.needSource()) { 1739 return; 1740 } 1741 1742 sk_sp<SkPDFObject> xObject = pdfDevice->makeFormXObjectFromDevice(); 1743 SkPDFUtils::DrawFormXObject(this->addXObjectResource(xObject.get()), content.stream()); 1744 } 1745 1746 sk_sp<SkSurface> SkPDFDevice::makeSurface(const SkImageInfo& info, const SkSurfaceProps& props) { 1747 return SkSurface::MakeRaster(info, &props); 1748 } 1749 1750 1751 sk_sp<SkPDFDict> SkPDFDevice::makeResourceDict() const { 1752 SkTDArray<SkPDFObject*> fonts; 1753 fonts.setReserve(fFontResources.count()); 1754 for (SkPDFFont* font : fFontResources) { 1755 fonts.push(font); 1756 } 1757 return SkPDFResourceDict::Make( 1758 &fGraphicStateResources, 1759 &fShaderResources, 1760 &fXObjectResources, 1761 &fonts); 1762 } 1763 1764 sk_sp<SkPDFArray> SkPDFDevice::copyMediaBox() const { 1765 auto mediaBox = sk_make_sp<SkPDFArray>(); 1766 mediaBox->reserve(4); 1767 mediaBox->appendInt(0); 1768 mediaBox->appendInt(0); 1769 mediaBox->appendInt(fPageSize.width()); 1770 mediaBox->appendInt(fPageSize.height()); 1771 return mediaBox; 1772 } 1773 1774 std::unique_ptr<SkStreamAsset> SkPDFDevice::content() const { 1775 SkDynamicMemoryWStream buffer; 1776 if (fInitialTransform.getType() != SkMatrix::kIdentity_Mask) { 1777 SkPDFUtils::AppendTransform(fInitialTransform, &buffer); 1778 } 1779 1780 GraphicStackState gsState(fExistingClipStack, &buffer); 1781 for (const auto& entry : fContentEntries) { 1782 gsState.updateClip(entry.fState.fClipStack, 1783 {0, 0}, SkRect::Make(size(*this))); 1784 gsState.updateMatrix(entry.fState.fMatrix); 1785 gsState.updateDrawingState(entry.fState); 1786 1787 entry.fContent.writeToStream(&buffer); 1788 } 1789 gsState.drainStack(); 1790 if (buffer.bytesWritten() > 0) { 1791 return std::unique_ptr<SkStreamAsset>(buffer.detachAsStream()); 1792 } else { 1793 return skstd::make_unique<SkMemoryStream>(); 1794 } 1795 } 1796 1797 /* Draws an inverse filled path by using Path Ops to compute the positive 1798 * inverse using the current clip as the inverse bounds. 1799 * Return true if this was an inverse path and was properly handled, 1800 * otherwise returns false and the normal drawing routine should continue, 1801 * either as a (incorrect) fallback or because the path was not inverse 1802 * in the first place. 1803 */ 1804 bool SkPDFDevice::handleInversePath(const SkPath& origPath, 1805 const SkPaint& paint, bool pathIsMutable, 1806 const SkMatrix* prePathMatrix) { 1807 if (!origPath.isInverseFillType()) { 1808 return false; 1809 } 1810 1811 if (this->cs().isEmpty(size(*this))) { 1812 return false; 1813 } 1814 1815 SkPath modifiedPath; 1816 SkPath* pathPtr = const_cast<SkPath*>(&origPath); 1817 SkPaint noInversePaint(paint); 1818 1819 // Merge stroking operations into final path. 1820 if (SkPaint::kStroke_Style == paint.getStyle() || 1821 SkPaint::kStrokeAndFill_Style == paint.getStyle()) { 1822 bool doFillPath = paint.getFillPath(origPath, &modifiedPath); 1823 if (doFillPath) { 1824 noInversePaint.setStyle(SkPaint::kFill_Style); 1825 noInversePaint.setStrokeWidth(0); 1826 pathPtr = &modifiedPath; 1827 } else { 1828 // To be consistent with the raster output, hairline strokes 1829 // are rendered as non-inverted. 1830 modifiedPath.toggleInverseFillType(); 1831 this->drawPath(modifiedPath, paint, nullptr, true); 1832 return true; 1833 } 1834 } 1835 1836 // Get bounds of clip in current transform space 1837 // (clip bounds are given in device space). 1838 SkMatrix transformInverse; 1839 SkMatrix totalMatrix = this->ctm(); 1840 if (prePathMatrix) { 1841 totalMatrix.preConcat(*prePathMatrix); 1842 } 1843 if (!totalMatrix.invert(&transformInverse)) { 1844 return false; 1845 } 1846 SkRect bounds = this->cs().bounds(size(*this)); 1847 transformInverse.mapRect(&bounds); 1848 1849 // Extend the bounds by the line width (plus some padding) 1850 // so the edge doesn't cause a visible stroke. 1851 bounds.outset(paint.getStrokeWidth() + SK_Scalar1, 1852 paint.getStrokeWidth() + SK_Scalar1); 1853 1854 if (!calculate_inverse_path(bounds, *pathPtr, &modifiedPath)) { 1855 return false; 1856 } 1857 1858 this->drawPath(modifiedPath, noInversePaint, prePathMatrix, true); 1859 return true; 1860 } 1861 1862 void SkPDFDevice::appendAnnotations(SkPDFArray* array) const { 1863 array->reserve(fLinkToURLs.count() + fLinkToDestinations.count()); 1864 for (const RectWithData& rectWithURL : fLinkToURLs) { 1865 SkRect r; 1866 fInitialTransform.mapRect(&r, rectWithURL.rect); 1867 array->appendObject(create_link_to_url(rectWithURL.data.get(), r)); 1868 } 1869 for (const RectWithData& linkToDestination : fLinkToDestinations) { 1870 SkRect r; 1871 fInitialTransform.mapRect(&r, linkToDestination.rect); 1872 array->appendObject( 1873 create_link_named_dest(linkToDestination.data.get(), r)); 1874 } 1875 } 1876 1877 void SkPDFDevice::appendDestinations(SkPDFDict* dict, SkPDFObject* page) const { 1878 for (const NamedDestination& dest : fNamedDestinations) { 1879 auto pdfDest = sk_make_sp<SkPDFArray>(); 1880 pdfDest->reserve(5); 1881 pdfDest->appendObjRef(sk_ref_sp(page)); 1882 pdfDest->appendName("XYZ"); 1883 SkPoint p = fInitialTransform.mapXY(dest.point.x(), dest.point.y()); 1884 pdfDest->appendScalar(p.x()); 1885 pdfDest->appendScalar(p.y()); 1886 pdfDest->appendInt(0); // Leave zoom unchanged 1887 SkString name(static_cast<const char*>(dest.nameData->data())); 1888 dict->insertObject(name, std::move(pdfDest)); 1889 } 1890 } 1891 1892 sk_sp<SkPDFObject> SkPDFDevice::makeFormXObjectFromDevice() { 1893 SkMatrix inverseTransform = SkMatrix::I(); 1894 if (!fInitialTransform.isIdentity()) { 1895 if (!fInitialTransform.invert(&inverseTransform)) { 1896 SkDEBUGFAIL("Layer initial transform should be invertible."); 1897 inverseTransform.reset(); 1898 } 1899 } 1900 sk_sp<SkPDFObject> xobject = 1901 SkPDFMakeFormXObject(this->content(), this->copyMediaBox(), 1902 this->makeResourceDict(), inverseTransform, nullptr); 1903 // We always draw the form xobjects that we create back into the device, so 1904 // we simply preserve the font usage instead of pulling it out and merging 1905 // it back in later. 1906 this->cleanUp(); // Reset this device to have no content. 1907 this->init(); 1908 return xobject; 1909 } 1910 1911 void SkPDFDevice::drawFormXObjectWithMask(int xObjectIndex, 1912 sk_sp<SkPDFObject> mask, 1913 const SkClipStack& clipStack, 1914 SkBlendMode mode, 1915 bool invertClip) { 1916 if (!invertClip && clipStack.isEmpty(size(*this))) { 1917 return; 1918 } 1919 1920 sk_sp<SkPDFDict> sMaskGS = SkPDFGraphicState::GetSMaskGraphicState( 1921 std::move(mask), invertClip, 1922 SkPDFGraphicState::kAlpha_SMaskMode, fDocument->canon()); 1923 1924 SkPaint paint; 1925 paint.setBlendMode(mode); 1926 ScopedContentEntry content(this, clipStack, SkMatrix::I(), paint); 1927 if (!content.entry()) { 1928 return; 1929 } 1930 SkPDFUtils::ApplyGraphicState(addGraphicStateResource(sMaskGS.get()), content.stream()); 1931 SkPDFUtils::DrawFormXObject(xObjectIndex, content.stream()); 1932 this->clearMaskOnGraphicState(content.stream()); 1933 } 1934 1935 SkPDFDevice::ContentEntry* SkPDFDevice::setUpContentEntry(const SkClipStack& clipStack, 1936 const SkMatrix& matrix, 1937 const SkPaint& paint, 1938 bool hasText, 1939 sk_sp<SkPDFObject>* dst) { 1940 *dst = nullptr; 1941 SkBlendMode blendMode = paint.getBlendMode(); 1942 1943 // For the following modes, we want to handle source and destination 1944 // separately, so make an object of what's already there. 1945 if (blendMode == SkBlendMode::kClear || 1946 blendMode == SkBlendMode::kSrc || 1947 blendMode == SkBlendMode::kSrcIn || 1948 blendMode == SkBlendMode::kDstIn || 1949 blendMode == SkBlendMode::kSrcOut || 1950 blendMode == SkBlendMode::kDstOut || 1951 blendMode == SkBlendMode::kSrcATop || 1952 blendMode == SkBlendMode::kDstATop || 1953 blendMode == SkBlendMode::kModulate) { 1954 if (!isContentEmpty()) { 1955 *dst = this->makeFormXObjectFromDevice(); 1956 SkASSERT(isContentEmpty()); 1957 } else if (blendMode != SkBlendMode::kSrc && 1958 blendMode != SkBlendMode::kSrcOut) { 1959 // Except for Src and SrcOut, if there isn't anything already there, 1960 // then we're done. 1961 return nullptr; 1962 } 1963 } 1964 // TODO(vandebo): Figure out how/if we can handle the following modes: 1965 // Xor, Plus. 1966 1967 // Dst xfer mode doesn't draw source at all. 1968 if (blendMode == SkBlendMode::kDst) { 1969 return nullptr; 1970 } 1971 1972 SkPDFDevice::ContentEntry* entry; 1973 if (fContentEntries.back() && fContentEntries.back()->fContent.bytesWritten() == 0) { 1974 entry = fContentEntries.back(); 1975 } else if (blendMode != SkBlendMode::kDstOver) { 1976 entry = fContentEntries.emplace_back(); 1977 } else { 1978 entry = fContentEntries.emplace_front(); 1979 } 1980 populateGraphicStateEntryFromPaint(matrix, clipStack, paint, hasText, &entry->fState); 1981 return entry; 1982 } 1983 1984 void SkPDFDevice::finishContentEntry(SkBlendMode blendMode, 1985 sk_sp<SkPDFObject> dst, 1986 SkPath* shape) { 1987 if (blendMode != SkBlendMode::kClear && 1988 blendMode != SkBlendMode::kSrc && 1989 blendMode != SkBlendMode::kDstOver && 1990 blendMode != SkBlendMode::kSrcIn && 1991 blendMode != SkBlendMode::kDstIn && 1992 blendMode != SkBlendMode::kSrcOut && 1993 blendMode != SkBlendMode::kDstOut && 1994 blendMode != SkBlendMode::kSrcATop && 1995 blendMode != SkBlendMode::kDstATop && 1996 blendMode != SkBlendMode::kModulate) { 1997 SkASSERT(!dst); 1998 return; 1999 } 2000 if (blendMode == SkBlendMode::kDstOver) { 2001 SkASSERT(!dst); 2002 if (fContentEntries.front()->fContent.bytesWritten() == 0) { 2003 // For DstOver, an empty content entry was inserted before the rest 2004 // of the content entries. If nothing was drawn, it needs to be 2005 // removed. 2006 fContentEntries.pop_front(); 2007 } 2008 return; 2009 } 2010 if (!dst) { 2011 SkASSERT(blendMode == SkBlendMode::kSrc || 2012 blendMode == SkBlendMode::kSrcOut); 2013 return; 2014 } 2015 2016 SkASSERT(dst); 2017 SkASSERT(fContentEntries.count() == 1); 2018 // Changing the current content into a form-xobject will destroy the clip 2019 // objects which is fine since the xobject will already be clipped. However 2020 // if source has shape, we need to clip it too, so a copy of the clip is 2021 // saved. 2022 2023 SkClipStack clipStack = fContentEntries.front()->fState.fClipStack; 2024 2025 SkPaint stockPaint; 2026 2027 sk_sp<SkPDFObject> srcFormXObject; 2028 if (isContentEmpty()) { 2029 // If nothing was drawn and there's no shape, then the draw was a 2030 // no-op, but dst needs to be restored for that to be true. 2031 // If there is shape, then an empty source with Src, SrcIn, SrcOut, 2032 // DstIn, DstAtop or Modulate reduces to Clear and DstOut or SrcAtop 2033 // reduces to Dst. 2034 if (shape == nullptr || blendMode == SkBlendMode::kDstOut || 2035 blendMode == SkBlendMode::kSrcATop) { 2036 ScopedContentEntry content(this, fExistingClipStack, SkMatrix::I(), stockPaint); 2037 // TODO: addXObjectResource take sk_sp 2038 SkPDFUtils::DrawFormXObject(this->addXObjectResource(dst.get()), content.stream()); 2039 return; 2040 } else { 2041 blendMode = SkBlendMode::kClear; 2042 } 2043 } else { 2044 SkASSERT(fContentEntries.count() == 1); 2045 srcFormXObject = this->makeFormXObjectFromDevice(); 2046 } 2047 2048 // TODO(vandebo) srcFormXObject may contain alpha, but here we want it 2049 // without alpha. 2050 if (blendMode == SkBlendMode::kSrcATop) { 2051 // TODO(vandebo): In order to properly support SrcATop we have to track 2052 // the shape of what's been drawn at all times. It's the intersection of 2053 // the non-transparent parts of the device and the outlines (shape) of 2054 // all images and devices drawn. 2055 drawFormXObjectWithMask(addXObjectResource(srcFormXObject.get()), dst, 2056 fExistingClipStack, SkBlendMode::kSrcOver, true); 2057 } else { 2058 if (shape != nullptr) { 2059 // Draw shape into a form-xobject. 2060 SkPaint filledPaint; 2061 filledPaint.setColor(SK_ColorBLACK); 2062 filledPaint.setStyle(SkPaint::kFill_Style); 2063 this->internalDrawPath(clipStack, SkMatrix::I(), *shape, filledPaint, nullptr, true); 2064 this->drawFormXObjectWithMask(this->addXObjectResource(dst.get()), 2065 this->makeFormXObjectFromDevice(), 2066 fExistingClipStack, 2067 SkBlendMode::kSrcOver, true); 2068 } else { 2069 this->drawFormXObjectWithMask(this->addXObjectResource(dst.get()), 2070 srcFormXObject, 2071 fExistingClipStack, 2072 SkBlendMode::kSrcOver, true); 2073 } 2074 } 2075 2076 if (blendMode == SkBlendMode::kClear) { 2077 return; 2078 } else if (blendMode == SkBlendMode::kSrc || 2079 blendMode == SkBlendMode::kDstATop) { 2080 ScopedContentEntry content(this, fExistingClipStack, SkMatrix::I(), stockPaint); 2081 if (content.entry()) { 2082 SkPDFUtils::DrawFormXObject(this->addXObjectResource(srcFormXObject.get()), 2083 content.stream()); 2084 } 2085 if (blendMode == SkBlendMode::kSrc) { 2086 return; 2087 } 2088 } else if (blendMode == SkBlendMode::kSrcATop) { 2089 ScopedContentEntry content(this, fExistingClipStack, 2090 SkMatrix::I(), stockPaint); 2091 if (content.entry()) { 2092 SkPDFUtils::DrawFormXObject(this->addXObjectResource(dst.get()), content.stream()); 2093 } 2094 } 2095 2096 SkASSERT(blendMode == SkBlendMode::kSrcIn || 2097 blendMode == SkBlendMode::kDstIn || 2098 blendMode == SkBlendMode::kSrcOut || 2099 blendMode == SkBlendMode::kDstOut || 2100 blendMode == SkBlendMode::kSrcATop || 2101 blendMode == SkBlendMode::kDstATop || 2102 blendMode == SkBlendMode::kModulate); 2103 2104 if (blendMode == SkBlendMode::kSrcIn || 2105 blendMode == SkBlendMode::kSrcOut || 2106 blendMode == SkBlendMode::kSrcATop) { 2107 drawFormXObjectWithMask(addXObjectResource(srcFormXObject.get()), 2108 std::move(dst), 2109 fExistingClipStack, 2110 SkBlendMode::kSrcOver, 2111 blendMode == SkBlendMode::kSrcOut); 2112 return; 2113 } else { 2114 SkBlendMode mode = SkBlendMode::kSrcOver; 2115 int resourceID = addXObjectResource(dst.get()); 2116 if (blendMode == SkBlendMode::kModulate) { 2117 drawFormXObjectWithMask(addXObjectResource(srcFormXObject.get()), 2118 std::move(dst), fExistingClipStack, 2119 SkBlendMode::kSrcOver, false); 2120 mode = SkBlendMode::kMultiply; 2121 } 2122 drawFormXObjectWithMask(resourceID, std::move(srcFormXObject), 2123 fExistingClipStack, mode, 2124 blendMode == SkBlendMode::kDstOut); 2125 return; 2126 } 2127 } 2128 2129 bool SkPDFDevice::isContentEmpty() { 2130 if (!fContentEntries.front() || fContentEntries.front()->fContent.bytesWritten() == 0) { 2131 SkASSERT(fContentEntries.count() <= 1); 2132 return true; 2133 } 2134 return false; 2135 } 2136 2137 void SkPDFDevice::populateGraphicStateEntryFromPaint( 2138 const SkMatrix& matrix, 2139 const SkClipStack& clipStack, 2140 const SkPaint& paint, 2141 bool hasText, 2142 SkPDFDevice::GraphicStateEntry* entry) { 2143 NOT_IMPLEMENTED(paint.getPathEffect() != nullptr, false); 2144 NOT_IMPLEMENTED(paint.getMaskFilter() != nullptr, false); 2145 NOT_IMPLEMENTED(paint.getColorFilter() != nullptr, false); 2146 2147 entry->fMatrix = matrix; 2148 entry->fClipStack = clipStack; 2149 entry->fColor = SkColorSetA(paint.getColor(), 0xFF); 2150 entry->fShaderIndex = -1; 2151 2152 // PDF treats a shader as a color, so we only set one or the other. 2153 sk_sp<SkPDFObject> pdfShader; 2154 SkShader* shader = paint.getShader(); 2155 SkColor color = paint.getColor(); 2156 if (shader) { 2157 if (SkShader::kColor_GradientType == shader->asAGradient(nullptr)) { 2158 // We don't have to set a shader just for a color. 2159 SkShader::GradientInfo gradientInfo; 2160 SkColor gradientColor = SK_ColorBLACK; 2161 gradientInfo.fColors = &gradientColor; 2162 gradientInfo.fColorOffsets = nullptr; 2163 gradientInfo.fColorCount = 1; 2164 SkAssertResult(shader->asAGradient(&gradientInfo) == SkShader::kColor_GradientType); 2165 entry->fColor = SkColorSetA(gradientColor, 0xFF); 2166 color = gradientColor; 2167 } else { 2168 // PDF positions patterns relative to the initial transform, so 2169 // we need to apply the current transform to the shader parameters. 2170 SkMatrix transform = matrix; 2171 transform.postConcat(fInitialTransform); 2172 2173 // PDF doesn't support kClamp_TileMode, so we simulate it by making 2174 // a pattern the size of the current clip. 2175 SkRect clipStackBounds = clipStack.bounds(size(*this)); 2176 2177 // We need to apply the initial transform to bounds in order to get 2178 // bounds in a consistent coordinate system. 2179 fInitialTransform.mapRect(&clipStackBounds); 2180 SkIRect bounds; 2181 clipStackBounds.roundOut(&bounds); 2182 2183 pdfShader = SkPDFMakeShader(fDocument, shader, transform, bounds); 2184 2185 if (pdfShader.get()) { 2186 // pdfShader has been canonicalized so we can directly compare 2187 // pointers. 2188 int resourceIndex = fShaderResources.find(pdfShader.get()); 2189 if (resourceIndex < 0) { 2190 resourceIndex = fShaderResources.count(); 2191 fShaderResources.push(pdfShader.get()); 2192 pdfShader.get()->ref(); 2193 } 2194 entry->fShaderIndex = resourceIndex; 2195 } 2196 } 2197 } 2198 2199 sk_sp<SkPDFDict> newGraphicState; 2200 if (color == paint.getColor()) { 2201 newGraphicState = SkPDFGraphicState::GetGraphicStateForPaint(fDocument->canon(), paint); 2202 } else { 2203 SkPaint newPaint = paint; 2204 newPaint.setColor(color); 2205 newGraphicState = SkPDFGraphicState::GetGraphicStateForPaint(fDocument->canon(), newPaint); 2206 } 2207 int resourceIndex = addGraphicStateResource(newGraphicState.get()); 2208 entry->fGraphicStateIndex = resourceIndex; 2209 2210 if (hasText) { 2211 entry->fTextScaleX = paint.getTextScaleX(); 2212 entry->fTextFill = paint.getStyle(); 2213 } else { 2214 entry->fTextScaleX = 0; 2215 } 2216 } 2217 2218 int SkPDFDevice::addGraphicStateResource(SkPDFObject* gs) { 2219 // Assumes that gs has been canonicalized (so we can directly compare 2220 // pointers). 2221 int result = fGraphicStateResources.find(gs); 2222 if (result < 0) { 2223 result = fGraphicStateResources.count(); 2224 fGraphicStateResources.push(gs); 2225 gs->ref(); 2226 } 2227 return result; 2228 } 2229 2230 int SkPDFDevice::addXObjectResource(SkPDFObject* xObject) { 2231 // TODO(halcanary): make this take a sk_sp<SkPDFObject> 2232 // Assumes that xobject has been canonicalized (so we can directly compare 2233 // pointers). 2234 int result = fXObjectResources.find(xObject); 2235 if (result < 0) { 2236 result = fXObjectResources.count(); 2237 fXObjectResources.push(SkRef(xObject)); 2238 } 2239 return result; 2240 } 2241 2242 int SkPDFDevice::getFontResourceIndex(SkTypeface* typeface, uint16_t glyphID) { 2243 sk_sp<SkPDFFont> newFont = SkPDFFont::GetFontResource(fDocument->canon(), typeface, glyphID); 2244 if (!newFont) { 2245 return -1; 2246 } 2247 int resourceIndex = fFontResources.find(newFont.get()); 2248 if (resourceIndex < 0) { 2249 fDocument->registerFont(newFont.get()); 2250 resourceIndex = fFontResources.count(); 2251 fFontResources.push(newFont.release()); 2252 } 2253 return resourceIndex; 2254 } 2255 2256 static SkSize rect_to_size(const SkRect& r) { return {r.width(), r.height()}; } 2257 2258 static sk_sp<SkImage> color_filter(const SkImage* image, 2259 SkColorFilter* colorFilter) { 2260 auto surface = 2261 SkSurface::MakeRaster(SkImageInfo::MakeN32Premul(image->dimensions())); 2262 SkASSERT(surface); 2263 SkCanvas* canvas = surface->getCanvas(); 2264 canvas->clear(SK_ColorTRANSPARENT); 2265 SkPaint paint; 2266 paint.setColorFilter(sk_ref_sp(colorFilter)); 2267 canvas->drawImage(image, 0, 0, &paint); 2268 return surface->makeImageSnapshot(); 2269 } 2270 2271 //////////////////////////////////////////////////////////////////////////////// 2272 2273 static bool is_integer(SkScalar x) { 2274 return x == SkScalarTruncToScalar(x); 2275 } 2276 2277 static bool is_integral(const SkRect& r) { 2278 return is_integer(r.left()) && 2279 is_integer(r.top()) && 2280 is_integer(r.right()) && 2281 is_integer(r.bottom()); 2282 } 2283 2284 void SkPDFDevice::internalDrawImageRect(SkKeyedImage imageSubset, 2285 const SkRect* src, 2286 const SkRect& dst, 2287 const SkPaint& srcPaint, 2288 const SkMatrix& ctm) { 2289 if (!imageSubset) { 2290 return; 2291 } 2292 2293 SkIRect bounds = imageSubset.image()->bounds(); 2294 SkPaint paint = srcPaint; 2295 if (imageSubset.image()->isOpaque()) { 2296 replace_srcmode_on_opaque_paint(&paint); 2297 } 2298 SkRect srcRect = src ? *src : SkRect::Make(bounds); 2299 SkMatrix transform; 2300 transform.setRectToRect(srcRect, dst, SkMatrix::kFill_ScaleToFit); 2301 if (src && *src != SkRect::Make(bounds)) { 2302 if (!srcRect.intersect(SkRect::Make(bounds))) { 2303 return; 2304 } 2305 srcRect.roundOut(&bounds); 2306 transform.preTranslate(SkIntToScalar(bounds.x()), 2307 SkIntToScalar(bounds.y())); 2308 if (bounds != imageSubset.image()->bounds()) { 2309 imageSubset = imageSubset.subset(bounds); 2310 } 2311 if (!imageSubset) { 2312 return; 2313 } 2314 } 2315 2316 // TODO(halcanary) support isAlphaOnly & getMaskFilter. 2317 bool imageAlphaOnly = imageSubset.image()->isAlphaOnly() && !paint.getMaskFilter(); 2318 if (imageAlphaOnly) { 2319 if (SkColorFilter* colorFilter = paint.getColorFilter()) { 2320 sk_sp<SkImage> img = color_filter(imageSubset.image().get(), colorFilter); 2321 paint.setColorFilter(nullptr); 2322 imageSubset = SkKeyedImage(std::move(img)); 2323 if (!imageSubset) { 2324 return; 2325 } 2326 imageAlphaOnly = imageSubset.image()->isAlphaOnly(); 2327 // The colorfilter can make a alphonly image no longer be alphaonly. 2328 } 2329 } 2330 if (imageAlphaOnly) { 2331 sk_sp<SkImage> mask = alpha_image_to_greyscale_image(imageSubset.image().get()); 2332 if (!mask) { 2333 return; 2334 } 2335 // PDF doesn't seem to allow masking vector graphics with an Image XObject. 2336 // Must mask with a Form XObject. 2337 sk_sp<SkPDFDevice> maskDevice = this->makeCongruentDevice(); 2338 { 2339 SkCanvas canvas(maskDevice.get()); 2340 canvas.concat(transform); 2341 canvas.concat(ctm); 2342 // TODO(halcanary): investigate sub-pixel clipping. 2343 canvas.drawImage(mask, 0, 0); 2344 } 2345 remove_color_filter(&paint); 2346 if (!ctm.isIdentity() && paint.getShader()) { 2347 transform_shader(&paint, ctm); // Since we are using identity matrix. 2348 } 2349 ScopedContentEntry content(this, this->cs(), SkMatrix::I(), paint); 2350 if (!content.entry()) { 2351 return; 2352 } 2353 this->addSMaskGraphicState(std::move(maskDevice), content.stream()); 2354 SkPDFUtils::AppendRectangle(SkRect::Make(fPageSize), content.stream()); 2355 SkPDFUtils::PaintPath(SkPaint::kFill_Style, SkPath::kWinding_FillType, content.stream()); 2356 this->clearMaskOnGraphicState(content.stream()); 2357 return; 2358 } 2359 if (paint.getMaskFilter()) { 2360 paint.setShader(imageSubset.image()->makeShader(&transform)); 2361 SkPath path; 2362 path.addRect(SkRect::Make(imageSubset.image()->bounds())); 2363 this->internalDrawPath(this->cs(), this->ctm(), path, paint, &transform, true); 2364 return; 2365 } 2366 transform.postConcat(ctm); 2367 2368 bool needToRestore = false; 2369 if (src && !is_integral(*src)) { 2370 // Need sub-pixel clipping to fix https://bug.skia.org/4374 2371 this->cs().save(); 2372 this->cs().clipRect(dst, ctm, SkClipOp::kIntersect, true); 2373 needToRestore = true; 2374 } 2375 SK_AT_SCOPE_EXIT(if (needToRestore) { this->cs().restore(); }); 2376 2377 #ifdef SK_PDF_IMAGE_STATS 2378 gDrawImageCalls.fetch_add(1); 2379 #endif 2380 SkMatrix matrix = transform; 2381 2382 // Rasterize the bitmap using perspective in a new bitmap. 2383 if (transform.hasPerspective()) { 2384 SkASSERT(fDocument->rasterDpi() > 0); 2385 // Transform the bitmap in the new space, without taking into 2386 // account the initial transform. 2387 SkPath perspectiveOutline; 2388 SkRect imageBounds = SkRect::Make(imageSubset.image()->bounds()); 2389 perspectiveOutline.addRect(imageBounds); 2390 perspectiveOutline.transform(transform); 2391 2392 // TODO(edisonn): perf - use current clip too. 2393 // Retrieve the bounds of the new shape. 2394 SkRect bounds = perspectiveOutline.getBounds(); 2395 2396 // Transform the bitmap in the new space, taking into 2397 // account the initial transform. 2398 SkMatrix total = transform; 2399 total.postConcat(fInitialTransform); 2400 SkScalar dpiScale = SkIntToScalar(fDocument->rasterDpi()) / 2401 SkIntToScalar(SkPDFUtils::kDpiForRasterScaleOne); 2402 total.postScale(dpiScale, dpiScale); 2403 2404 SkPath physicalPerspectiveOutline; 2405 physicalPerspectiveOutline.addRect(imageBounds); 2406 physicalPerspectiveOutline.transform(total); 2407 2408 SkRect physicalPerspectiveBounds = 2409 physicalPerspectiveOutline.getBounds(); 2410 SkScalar scaleX = physicalPerspectiveBounds.width() / bounds.width(); 2411 SkScalar scaleY = physicalPerspectiveBounds.height() / bounds.height(); 2412 2413 // TODO(edisonn): A better approach would be to use a bitmap shader 2414 // (in clamp mode) and draw a rect over the entire bounding box. Then 2415 // intersect perspectiveOutline to the clip. That will avoid introducing 2416 // alpha to the image while still giving good behavior at the edge of 2417 // the image. Avoiding alpha will reduce the pdf size and generation 2418 // CPU time some. 2419 2420 SkISize wh = rect_to_size(physicalPerspectiveBounds).toCeil(); 2421 2422 auto surface = SkSurface::MakeRaster(SkImageInfo::MakeN32Premul(wh)); 2423 if (!surface) { 2424 return; 2425 } 2426 SkCanvas* canvas = surface->getCanvas(); 2427 canvas->clear(SK_ColorTRANSPARENT); 2428 2429 SkScalar deltaX = bounds.left(); 2430 SkScalar deltaY = bounds.top(); 2431 2432 SkMatrix offsetMatrix = transform; 2433 offsetMatrix.postTranslate(-deltaX, -deltaY); 2434 offsetMatrix.postScale(scaleX, scaleY); 2435 2436 // Translate the draw in the new canvas, so we perfectly fit the 2437 // shape in the bitmap. 2438 canvas->setMatrix(offsetMatrix); 2439 canvas->drawImage(imageSubset.image(), 0, 0); 2440 // Make sure the final bits are in the bitmap. 2441 canvas->flush(); 2442 2443 // In the new space, we use the identity matrix translated 2444 // and scaled to reflect DPI. 2445 matrix.setScale(1 / scaleX, 1 / scaleY); 2446 matrix.postTranslate(deltaX, deltaY); 2447 2448 imageSubset = SkKeyedImage(surface->makeImageSnapshot()); 2449 if (!imageSubset) { 2450 return; 2451 } 2452 } 2453 2454 SkMatrix scaled; 2455 // Adjust for origin flip. 2456 scaled.setScale(SK_Scalar1, -SK_Scalar1); 2457 scaled.postTranslate(0, SK_Scalar1); 2458 // Scale the image up from 1x1 to WxH. 2459 SkIRect subset = imageSubset.image()->bounds(); 2460 scaled.postScale(SkIntToScalar(subset.width()), 2461 SkIntToScalar(subset.height())); 2462 scaled.postConcat(matrix); 2463 ScopedContentEntry content(this, this->cs(), scaled, paint); 2464 if (!content.entry()) { 2465 return; 2466 } 2467 if (content.needShape()) { 2468 SkPath shape; 2469 shape.addRect(SkRect::Make(subset)); 2470 shape.transform(matrix); 2471 content.setShape(shape); 2472 } 2473 if (!content.needSource()) { 2474 return; 2475 } 2476 2477 if (SkColorFilter* colorFilter = paint.getColorFilter()) { 2478 // TODO(https://bug.skia.org/4378): implement colorfilter on other 2479 // draw calls. This code here works for all 2480 // drawBitmap*()/drawImage*() calls amd ImageFilters (which 2481 // rasterize a layer on this backend). Fortuanely, this seems 2482 // to be how Chromium impements most color-filters. 2483 sk_sp<SkImage> img = color_filter(imageSubset.image().get(), colorFilter); 2484 imageSubset = SkKeyedImage(std::move(img)); 2485 if (!imageSubset) { 2486 return; 2487 } 2488 // TODO(halcanary): de-dupe this by caching filtered images. 2489 // (maybe in the resource cache?) 2490 } 2491 2492 SkBitmapKey key = imageSubset.key(); 2493 sk_sp<SkPDFObject>* pdfimagePtr = fDocument->canon()->fPDFBitmapMap.find(key); 2494 sk_sp<SkPDFObject> pdfimage = pdfimagePtr ? *pdfimagePtr : nullptr; 2495 if (!pdfimage) { 2496 SkASSERT(imageSubset); 2497 pdfimage = SkPDFCreateBitmapObject(imageSubset.release(), 2498 fDocument->canon()->fPixelSerializer.get()); 2499 if (!pdfimage) { 2500 return; 2501 } 2502 fDocument->serialize(pdfimage); // serialize images early. 2503 SkASSERT((key != SkBitmapKey{{0, 0, 0, 0}, 0})); 2504 fDocument->canon()->fPDFBitmapMap.set(key, pdfimage); 2505 } 2506 // TODO(halcanary): addXObjectResource() should take a sk_sp<SkPDFObject> 2507 SkPDFUtils::DrawFormXObject(this->addXObjectResource(pdfimage.get()), content.stream()); 2508 } 2509 2510 /////////////////////////////////////////////////////////////////////////////////////////////////// 2511 2512 #include "SkSpecialImage.h" 2513 #include "SkImageFilter.h" 2514 2515 void SkPDFDevice::drawSpecial(SkSpecialImage* srcImg, int x, int y, const SkPaint& paint, 2516 SkImage* clipImage, const SkMatrix& clipMatrix) { 2517 SkASSERT(!srcImg->isTextureBacked()); 2518 2519 //TODO: clipImage support 2520 2521 SkBitmap resultBM; 2522 2523 SkImageFilter* filter = paint.getImageFilter(); 2524 if (filter) { 2525 SkIPoint offset = SkIPoint::Make(0, 0); 2526 SkMatrix matrix = this->ctm(); 2527 matrix.postTranslate(SkIntToScalar(-x), SkIntToScalar(-y)); 2528 const SkIRect clipBounds = 2529 this->cs().bounds(this->imageInfo().bounds()).roundOut().makeOffset(-x, -y); 2530 sk_sp<SkImageFilterCache> cache(this->getImageFilterCache()); 2531 // TODO: Should PDF be operating in a specified color space? For now, run the filter 2532 // in the same color space as the source (this is different from all other backends). 2533 SkImageFilter::OutputProperties outputProperties(srcImg->getColorSpace()); 2534 SkImageFilter::Context ctx(matrix, clipBounds, cache.get(), outputProperties); 2535 2536 sk_sp<SkSpecialImage> resultImg(filter->filterImage(srcImg, ctx, &offset)); 2537 if (resultImg) { 2538 SkPaint tmpUnfiltered(paint); 2539 tmpUnfiltered.setImageFilter(nullptr); 2540 if (resultImg->getROPixels(&resultBM)) { 2541 this->drawSprite(resultBM, x + offset.x(), y + offset.y(), tmpUnfiltered); 2542 } 2543 } 2544 } else { 2545 if (srcImg->getROPixels(&resultBM)) { 2546 this->drawSprite(resultBM, x, y, paint); 2547 } 2548 } 2549 } 2550 2551 sk_sp<SkSpecialImage> SkPDFDevice::makeSpecial(const SkBitmap& bitmap) { 2552 return SkSpecialImage::MakeFromRaster(bitmap.bounds(), bitmap); 2553 } 2554 2555 sk_sp<SkSpecialImage> SkPDFDevice::makeSpecial(const SkImage* image) { 2556 // TODO: See comment above in drawSpecial. The color mode we use for decode should be driven 2557 // by the destination where we're going to draw thing thing (ie this device). But we don't have 2558 // a color space, so we always decode in legacy mode for now. 2559 SkColorSpace* legacyColorSpace = nullptr; 2560 return SkSpecialImage::MakeFromImage(SkIRect::MakeWH(image->width(), image->height()), 2561 image->makeNonTextureImage(), legacyColorSpace); 2562 } 2563 2564 sk_sp<SkSpecialImage> SkPDFDevice::snapSpecial() { 2565 return nullptr; 2566 } 2567 2568 SkImageFilterCache* SkPDFDevice::getImageFilterCache() { 2569 // We always return a transient cache, so it is freed after each 2570 // filter traversal. 2571 return SkImageFilterCache::Create(SkImageFilterCache::kDefaultTransientSize); 2572 } 2573