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