SkPDF: clean up
- TextFill is always "Fill", so eliminate dead code
- Stop getting textScale, textSize, etc from paint, get from font instead.
- Stop calling SkFont::LEGACY_applyToPaint.
- replace SkPDFDevice::populateGraphicStateEntryFromPaint
all tests return the same PDF files.
Change-Id: I46a46a501f5ebc881b1458f2f9cb9397b3df1e06
Reviewed-on: https://skia-review.googlesource.com/c/180320
Commit-Queue: Herb Derby <herb@google.com>
Auto-Submit: Hal Canary <halcanary@google.com>
Reviewed-by: Herb Derby <herb@google.com>
diff --git a/src/pdf/SkPDFDevice.cpp b/src/pdf/SkPDFDevice.cpp
index a6780b0..b7479a8 100644
--- a/src/pdf/SkPDFDevice.cpp
+++ b/src/pdf/SkPDFDevice.cpp
@@ -17,6 +17,7 @@
#include "SkColor.h"
#include "SkColorFilter.h"
#include "SkDraw.h"
+#include "SkFontPriv.h"
#include "SkGlyphCache.h"
#include "SkGlyphRun.h"
#include "SkImageFilterCache.h"
@@ -372,14 +373,6 @@
fContentStream->writeText(" Tz\n");
currentEntry()->fTextScaleX = state.fTextScaleX;
}
- if (state.fTextFill != currentEntry()->fTextFill) {
- static_assert(SkPaint::kFill_Style == 0, "enum_must_match_value");
- static_assert(SkPaint::kStroke_Style == 1, "enum_must_match_value");
- static_assert(SkPaint::kStrokeAndFill_Style == 2, "enum_must_match_value");
- fContentStream->writeDecAsText(state.fTextFill);
- fContentStream->writeText(" Tr\n");
- currentEntry()->fTextFill = state.fTextFill;
- }
}
}
@@ -412,7 +405,7 @@
const SkClipStack* clipStack,
const SkMatrix& matrix,
const SkPaint& paint,
- bool hasText = false)
+ SkScalar textScale = 0)
: fDevice(device)
, fBlendMode(SkBlendMode::kSrcOver)
, fClipStack(clipStack)
@@ -423,10 +416,10 @@
}
fBlendMode = paint.getBlendMode();
fContentStream =
- fDevice->setUpContentEntry(clipStack, matrix, paint, hasText, &fDstFormXObject);
+ fDevice->setUpContentEntry(clipStack, matrix, paint, textScale, &fDstFormXObject);
}
- ScopedContentEntry(SkPDFDevice* dev, const SkPaint& paint, bool hasText = false)
- : ScopedContentEntry(dev, &dev->cs(), dev->ctm(), paint, hasText) {}
+ ScopedContentEntry(SkPDFDevice* dev, const SkPaint& paint, SkScalar textScale = 0)
+ : ScopedContentEntry(dev, &dev->cs(), dev->ctm(), paint, textScale) {}
~ScopedContentEntry() {
if (fContentStream) {
@@ -1023,9 +1016,7 @@
void SkPDFDevice::drawGlyphRunAsPath(
const SkGlyphRun& glyphRun, SkPoint offset, const SkPaint& runPaint) {
- SkPaint paint{runPaint};
- glyphRun.font().LEGACY_applyToPaint(&paint);
- SkFont font = glyphRun.font();
+ const SkFont& font = glyphRun.font();
SkPath path;
struct Rec {
@@ -1045,7 +1036,7 @@
}
rec->fPos += 1; // move to the next glyph's position
}, &rec);
- this->drawPath(path, paint, true);
+ this->drawPath(path, runPaint, true);
SkFont transparentFont = glyphRun.font();
transparentFont.setEmbolden(false); // Stop Recursion
@@ -1087,34 +1078,26 @@
const SkGlyphID* glyphs = glyphRun.glyphsIDs().data();
uint32_t glyphCount = SkToU32(glyphRun.glyphsIDs().size());
- SkPaint srcPaint{runPaint};
- glyphRun.font().LEGACY_applyToPaint(&srcPaint);
- srcPaint.setTextEncoding(kGlyphID_SkTextEncoding);
+ const SkFont& glyphRunFont = glyphRun.font();
- if (!glyphCount || !glyphs || srcPaint.getTextSize() <= 0 || this->hasEmptyClip()) {
+ if (!glyphCount || !glyphs || glyphRunFont.getSize() <= 0 || this->hasEmptyClip()) {
return;
}
- if (srcPaint.getPathEffect()
- || srcPaint.getMaskFilter()
- || srcPaint.isFakeBoldText()
+ if (runPaint.getPathEffect()
+ || runPaint.getMaskFilter()
+ || glyphRunFont.isEmbolden()
|| this->ctm().hasPerspective()
- || SkPaint::kFill_Style != srcPaint.getStyle()) {
+ || SkPaint::kFill_Style != runPaint.getStyle()) {
// Stroked Text doesn't work well with Type3 fonts.
this->drawGlyphRunAsPath(glyphRun, offset, runPaint);
return;
}
- SkPaint paint(srcPaint);
- remove_color_filter(&paint);
- replace_srcmode_on_opaque_paint(&paint);
- paint.setHinting(kNo_SkFontHinting);
- if (!paint.getTypeface()) {
- paint.setTypeface(SkTypeface::MakeDefault());
- }
- SkTypeface* typeface = paint.getTypeface();
+ SkTypeface* typeface = SkFontPriv::GetTypefaceOrDefault(glyphRunFont);
if (!typeface) {
SkDebugf("SkPDF: SkTypeface::MakeDefault() returned nullptr.\n");
return;
}
+
const SkAdvancedTypefaceMetrics* metrics = SkPDFFont::GetMetrics(typeface, fDocument->canon());
if (!metrics) {
return;
@@ -1129,122 +1112,124 @@
int emSize;
auto glyphCache = SkPDFFont::MakeVectorCache(typeface, &emSize);
- SkScalar textSize = paint.getTextSize();
- SkScalar advanceScale = textSize * paint.getTextScaleX() / emSize;
+ SkScalar textSize = glyphRunFont.getSize();
+ SkScalar advanceScale = textSize * glyphRunFont.getScaleX() / emSize;
// textScaleX and textScaleY are used to get a conservative bounding box for glyphs.
SkScalar textScaleY = textSize / emSize;
- SkScalar textScaleX = advanceScale + paint.getTextSkewX() * textScaleY;
+ SkScalar textScaleX = advanceScale + glyphRunFont.getSkewX() * textScaleY;
SkRect clipStackBounds = this->cs().bounds(this->bounds());
- {
- ScopedContentEntry content(this, paint, true);
- if (!content) {
- return;
- }
- SkDynamicMemoryWStream* out = content.stream();
- out->writeText("BT\n");
+ SkPaint paint(runPaint);
+ remove_color_filter(&paint);
+ replace_srcmode_on_opaque_paint(&paint);
+ ScopedContentEntry content(this, paint, glyphRunFont.getScaleX());
+ if (!content) {
+ return;
+ }
+ SkDynamicMemoryWStream* out = content.stream();
- int markId = -1;
- if (fNodeId) {
- markId = fDocument->getMarkIdForNodeId(fNodeId);
- }
+ out->writeText("BT\n");
- if (markId != -1) {
- out->writeText("/P <</MCID ");
- out->writeDecAsText(markId);
- out->writeText(" >>BDC\n");
- }
- SK_AT_SCOPE_EXIT(if (markId != -1) out->writeText("EMC\n"));
+ int markId = -1;
+ if (fNodeId) {
+ markId = fDocument->getMarkIdForNodeId(fNodeId);
+ }
- SK_AT_SCOPE_EXIT(out->writeText("ET\n"));
+ if (markId != -1) {
+ out->writeText("/P <</MCID ");
+ out->writeDecAsText(markId);
+ out->writeText(" >>BDC\n");
+ }
+ SK_AT_SCOPE_EXIT(if (markId != -1) out->writeText("EMC\n"));
- const SkGlyphID maxGlyphID = SkToU16(typeface->countGlyphs() - 1);
+ SK_AT_SCOPE_EXIT(out->writeText("ET\n"));
- if (clusterator.reversedChars()) {
- out->writeText("/ReversedChars BMC\n");
- }
- SK_AT_SCOPE_EXIT(if (clusterator.reversedChars()) { out->writeText("EMC\n"); } );
- GlyphPositioner glyphPositioner(out, paint.getTextSkewX(), offset);
- SkPDFFont* font = nullptr;
+ const SkGlyphID maxGlyphID = SkToU16(typeface->countGlyphs() - 1);
- while (SkClusterator::Cluster c = clusterator.next()) {
- int index = c.fGlyphIndex;
- int glyphLimit = index + c.fGlyphCount;
+ if (clusterator.reversedChars()) {
+ out->writeText("/ReversedChars BMC\n");
+ }
+ SK_AT_SCOPE_EXIT(if (clusterator.reversedChars()) { out->writeText("EMC\n"); } );
+ GlyphPositioner glyphPositioner(out, glyphRunFont.getSkewX(), offset);
+ SkPDFFont* font = nullptr;
- bool actualText = false;
- SK_AT_SCOPE_EXIT(if (actualText) {
- glyphPositioner.flush();
- out->writeText("EMC\n");
- });
- if (c.fUtf8Text) { // real cluster
- // Check if `/ActualText` needed.
- const char* textPtr = c.fUtf8Text;
- const char* textEnd = c.fUtf8Text + c.fTextByteLength;
- SkUnichar unichar = SkUTF::NextUTF8(&textPtr, textEnd);
- if (unichar < 0) {
- return;
- }
- if (textPtr < textEnd || // more characters left
- glyphLimit > index + 1 || // toUnicode wouldn't work
- unichar != map_glyph(glyphToUnicode, glyphs[index])) // test single Unichar map
- {
- glyphPositioner.flush();
- out->writeText("/Span<</ActualText <");
- SkPDFUtils::WriteUTF16beHex(out, 0xFEFF); // U+FEFF = BYTE ORDER MARK
- // the BOM marks this text as UTF-16BE, not PDFDocEncoding.
- SkPDFUtils::WriteUTF16beHex(out, unichar); // first char
- while (textPtr < textEnd) {
- unichar = SkUTF::NextUTF8(&textPtr, textEnd);
- if (unichar < 0) {
- break;
- }
- SkPDFUtils::WriteUTF16beHex(out, unichar);
- }
- out->writeText("> >> BDC\n"); // begin marked-content sequence
- // with an associated property list.
- actualText = true;
- }
+ while (SkClusterator::Cluster c = clusterator.next()) {
+ int index = c.fGlyphIndex;
+ int glyphLimit = index + c.fGlyphCount;
+
+ bool actualText = false;
+ SK_AT_SCOPE_EXIT(if (actualText) {
+ glyphPositioner.flush();
+ out->writeText("EMC\n");
+ });
+ if (c.fUtf8Text) { // real cluster
+ // Check if `/ActualText` needed.
+ const char* textPtr = c.fUtf8Text;
+ const char* textEnd = c.fUtf8Text + c.fTextByteLength;
+ SkUnichar unichar = SkUTF::NextUTF8(&textPtr, textEnd);
+ if (unichar < 0) {
+ return;
}
- for (; index < glyphLimit; ++index) {
- SkGlyphID gid = glyphs[index];
- if (gid > maxGlyphID) {
+ if (textPtr < textEnd || // more characters left
+ glyphLimit > index + 1 || // toUnicode wouldn't work
+ unichar != map_glyph(glyphToUnicode, glyphs[index])) // test single Unichar map
+ {
+ glyphPositioner.flush();
+ out->writeText("/Span<</ActualText <");
+ SkPDFUtils::WriteUTF16beHex(out, 0xFEFF); // U+FEFF = BYTE ORDER MARK
+ // the BOM marks this text as UTF-16BE, not PDFDocEncoding.
+ SkPDFUtils::WriteUTF16beHex(out, unichar); // first char
+ while (textPtr < textEnd) {
+ unichar = SkUTF::NextUTF8(&textPtr, textEnd);
+ if (unichar < 0) {
+ break;
+ }
+ SkPDFUtils::WriteUTF16beHex(out, unichar);
+ }
+ out->writeText("> >> BDC\n"); // begin marked-content sequence
+ // with an associated property list.
+ actualText = true;
+ }
+ }
+ for (; index < glyphLimit; ++index) {
+ SkGlyphID gid = glyphs[index];
+ if (gid > maxGlyphID) {
+ continue;
+ }
+ SkPoint xy = glyphRun.positions()[index];
+ // Do a glyph-by-glyph bounds-reject if positions are absolute.
+ SkRect glyphBounds = get_glyph_bounds_device_space(
+ gid, glyphCache.get(), textScaleX, textScaleY,
+ xy + offset, this->ctm());
+ if (glyphBounds.isEmpty()) {
+ if (!contains(clipStackBounds, {glyphBounds.x(), glyphBounds.y()})) {
continue;
}
- SkPoint xy = glyphRun.positions()[index];
- // Do a glyph-by-glyph bounds-reject if positions are absolute.
- SkRect glyphBounds = get_glyph_bounds_device_space(
- gid, glyphCache.get(), textScaleX, textScaleY,
- xy + offset, this->ctm());
- if (glyphBounds.isEmpty()) {
- if (!contains(clipStackBounds, {glyphBounds.x(), glyphBounds.y()})) {
- continue;
- }
- } else {
- if (!clipStackBounds.intersects(glyphBounds)) {
- continue; // reject glyphs as out of bounds
- }
+ } else {
+ if (!clipStackBounds.intersects(glyphBounds)) {
+ continue; // reject glyphs as out of bounds
}
- if (needs_new_font(font, gid, glyphCache.get(), fontType)) {
- // Not yet specified font or need to switch font.
- font = SkPDFFont::GetFontResource(fDocument, glyphCache.get(), typeface, gid);
- SkASSERT(font); // All preconditions for SkPDFFont::GetFontResource are met.
- glyphPositioner.flush();
- glyphPositioner.setWideChars(font->multiByteGlyphs());
- SkPDFWriteResourceName(out, SkPDFResourceType::kFont,
- add_resource(fFontResources, font->indirectReference()));
- out->writeText(" ");
- SkPDFUtils::AppendScalar(textSize, out);
- out->writeText(" Tf\n");
-
- }
- font->noteGlyphUsage(gid);
- SkGlyphID encodedGlyph = font->multiByteGlyphs()
- ? gid : font->glyphToPDFFontEncoding(gid);
- SkScalar advance = advanceScale * glyphCache->getGlyphIDAdvance(gid).fAdvanceX;
- glyphPositioner.writeGlyph(xy, advance, encodedGlyph);
}
+ if (needs_new_font(font, gid, glyphCache.get(), fontType)) {
+ // Not yet specified font or need to switch font.
+ font = SkPDFFont::GetFontResource(fDocument, glyphCache.get(), typeface, gid);
+ SkASSERT(font); // All preconditions for SkPDFFont::GetFontResource are met.
+ glyphPositioner.flush();
+ glyphPositioner.setWideChars(font->multiByteGlyphs());
+ SkPDFWriteResourceName(out, SkPDFResourceType::kFont,
+ add_resource(fFontResources, font->indirectReference()));
+ out->writeText(" ");
+ SkPDFUtils::AppendScalar(textSize, out);
+ out->writeText(" Tf\n");
+
+ }
+ font->noteGlyphUsage(gid);
+ SkGlyphID encodedGlyph = font->multiByteGlyphs()
+ ? gid : font->glyphToPDFFontEncoding(gid);
+ SkScalar advance = advanceScale * glyphCache->getGlyphIDAdvance(gid).fAdvanceX;
+ glyphPositioner.writeGlyph(xy, advance, encodedGlyph);
}
}
}
@@ -1507,10 +1492,85 @@
return nullptr != SkPDFUtils::BlendModeName(blendMode);
}
+static void populate_graphic_state_entry_from_paint(
+ SkPDFDocument* doc,
+ const SkMatrix& matrix,
+ const SkClipStack* clipStack,
+ SkIRect deviceBounds,
+ const SkPaint& paint,
+ const SkMatrix& initialTransform,
+ SkScalar textScale,
+ SkPDFDevice::GraphicStateEntry* entry,
+ SkTHashSet<SkPDFIndirectReference>* shaderResources,
+ SkTHashSet<SkPDFIndirectReference>* graphicStateResources) {
+ NOT_IMPLEMENTED(paint.getPathEffect() != nullptr, false);
+ NOT_IMPLEMENTED(paint.getMaskFilter() != nullptr, false);
+ NOT_IMPLEMENTED(paint.getColorFilter() != nullptr, false);
+
+ entry->fMatrix = matrix;
+ entry->fClipStackGenID = clipStack ? clipStack->getTopmostGenID()
+ : SkClipStack::kWideOpenGenID;
+ SkColor4f color = paint.getColor4f();
+ entry->fColor = {color.fR, color.fG, color.fB, 1};
+ entry->fShaderIndex = -1;
+
+ // PDF treats a shader as a color, so we only set one or the other.
+ SkShader* shader = paint.getShader();
+ if (shader) {
+ if (SkShader::kColor_GradientType == shader->asAGradient(nullptr)) {
+ // We don't have to set a shader just for a color.
+ SkShader::GradientInfo gradientInfo;
+ SkColor gradientColor = SK_ColorBLACK;
+ gradientInfo.fColors = &gradientColor;
+ gradientInfo.fColorOffsets = nullptr;
+ gradientInfo.fColorCount = 1;
+ SkAssertResult(shader->asAGradient(&gradientInfo) == SkShader::kColor_GradientType);
+ color = SkColor4f::FromColor(gradientColor);
+ entry->fColor ={color.fR, color.fG, color.fB, 1};
+
+ } else {
+ // PDF positions patterns relative to the initial transform, so
+ // we need to apply the current transform to the shader parameters.
+ SkMatrix transform = matrix;
+ transform.postConcat(initialTransform);
+
+ // PDF doesn't support kClamp_TileMode, so we simulate it by making
+ // a pattern the size of the current clip.
+ SkRect clipStackBounds = clipStack ? clipStack->bounds(deviceBounds)
+ : SkRect::Make(deviceBounds);
+
+ // We need to apply the initial transform to bounds in order to get
+ // bounds in a consistent coordinate system.
+ initialTransform.mapRect(&clipStackBounds);
+ SkIRect bounds;
+ clipStackBounds.roundOut(&bounds);
+
+ SkPDFIndirectReference pdfShader
+ = SkPDFMakeShader(doc, shader, transform, bounds, paint.getColor());
+
+ if (pdfShader) {
+ // pdfShader has been canonicalized so we can directly compare pointers.
+ entry->fShaderIndex = add_resource(*shaderResources, pdfShader);
+ }
+ }
+ }
+
+ SkPDFIndirectReference newGraphicState;
+ if (color == paint.getColor4f()) {
+ newGraphicState = SkPDFGraphicState::GetGraphicStateForPaint(doc, paint);
+ } else {
+ SkPaint newPaint = paint;
+ newPaint.setColor4f(color, nullptr);
+ newGraphicState = SkPDFGraphicState::GetGraphicStateForPaint(doc, newPaint);
+ }
+ entry->fGraphicStateIndex = add_resource(*graphicStateResources, newGraphicState);
+ entry->fTextScaleX = textScale;
+}
+
SkDynamicMemoryWStream* SkPDFDevice::setUpContentEntry(const SkClipStack* clipStack,
const SkMatrix& matrix,
const SkPaint& paint,
- bool hasText,
+ SkScalar textScale,
SkPDFIndirectReference* dst) {
SkASSERT(!*dst);
SkBlendMode blendMode = paint.getBlendMode();
@@ -1552,7 +1612,17 @@
}
SkASSERT(fActiveStackState.fContentStream);
GraphicStateEntry entry;
- this->populateGraphicStateEntryFromPaint(matrix, clipStack, paint, hasText, &entry);
+ populate_graphic_state_entry_from_paint(
+ fDocument,
+ matrix,
+ clipStack,
+ this->bounds(),
+ paint,
+ fInitialTransform,
+ textScale,
+ &entry,
+ &fShaderResources,
+ &fGraphicStateResources);
fActiveStackState.updateClip(clipStack, this->bounds());
fActiveStackState.updateMatrix(entry.fMatrix);
fActiveStackState.updateDrawingState(entry);
@@ -1701,82 +1771,6 @@
return fContent.bytesWritten() == 0 && fContentBuffer.bytesWritten() == 0;
}
-void SkPDFDevice::populateGraphicStateEntryFromPaint(
- const SkMatrix& matrix,
- const SkClipStack* clipStack,
- const SkPaint& paint,
- bool hasText,
- SkPDFDevice::GraphicStateEntry* entry) {
- NOT_IMPLEMENTED(paint.getPathEffect() != nullptr, false);
- NOT_IMPLEMENTED(paint.getMaskFilter() != nullptr, false);
- NOT_IMPLEMENTED(paint.getColorFilter() != nullptr, false);
-
- entry->fMatrix = matrix;
- entry->fClipStackGenID = clipStack ? clipStack->getTopmostGenID()
- : SkClipStack::kWideOpenGenID;
- SkColor4f color = paint.getColor4f();
- entry->fColor = {color.fR, color.fG, color.fB, 1};
- entry->fShaderIndex = -1;
-
- // PDF treats a shader as a color, so we only set one or the other.
- SkShader* shader = paint.getShader();
- if (shader) {
- if (SkShader::kColor_GradientType == shader->asAGradient(nullptr)) {
- // We don't have to set a shader just for a color.
- SkShader::GradientInfo gradientInfo;
- SkColor gradientColor = SK_ColorBLACK;
- gradientInfo.fColors = &gradientColor;
- gradientInfo.fColorOffsets = nullptr;
- gradientInfo.fColorCount = 1;
- SkAssertResult(shader->asAGradient(&gradientInfo) == SkShader::kColor_GradientType);
- color = SkColor4f::FromColor(gradientColor);
- entry->fColor ={color.fR, color.fG, color.fB, 1};
-
- } else {
- // PDF positions patterns relative to the initial transform, so
- // we need to apply the current transform to the shader parameters.
- SkMatrix transform = matrix;
- transform.postConcat(fInitialTransform);
-
- // PDF doesn't support kClamp_TileMode, so we simulate it by making
- // a pattern the size of the current clip.
- SkRect clipStackBounds = clipStack ? clipStack->bounds(this->bounds())
- : SkRect::Make(this->bounds());
-
- // We need to apply the initial transform to bounds in order to get
- // bounds in a consistent coordinate system.
- fInitialTransform.mapRect(&clipStackBounds);
- SkIRect bounds;
- clipStackBounds.roundOut(&bounds);
-
- SkPDFIndirectReference pdfShader
- = SkPDFMakeShader(fDocument, shader, transform, bounds, paint.getColor());
-
- if (pdfShader) {
- // pdfShader has been canonicalized so we can directly compare pointers.
- entry->fShaderIndex = add_resource(fShaderResources, pdfShader);
- }
- }
- }
-
- SkPDFIndirectReference newGraphicState;
- if (color == paint.getColor4f()) {
- newGraphicState = SkPDFGraphicState::GetGraphicStateForPaint(fDocument, paint);
- } else {
- SkPaint newPaint = paint;
- newPaint.setColor4f(color, nullptr);
- newGraphicState = SkPDFGraphicState::GetGraphicStateForPaint(fDocument, newPaint);
- }
- entry->fGraphicStateIndex = add_resource(fGraphicStateResources, std::move(newGraphicState));
-
- if (hasText) {
- entry->fTextScaleX = paint.getTextScaleX();
- entry->fTextFill = paint.getStyle();
- } else {
- entry->fTextScaleX = 0;
- }
-}
-
static SkSize rect_to_size(const SkRect& r) { return {r.width(), r.height()}; }
static sk_sp<SkImage> color_filter(const SkImage* image,
diff --git a/src/pdf/SkPDFDevice.h b/src/pdf/SkPDFDevice.h
index 069e1cb..55897ed 100644
--- a/src/pdf/SkPDFDevice.h
+++ b/src/pdf/SkPDFDevice.h
@@ -213,17 +213,11 @@
SkDynamicMemoryWStream* setUpContentEntry(const SkClipStack* clipStack,
const SkMatrix& matrix,
const SkPaint& paint,
- bool hasText,
+ SkScalar,
SkPDFIndirectReference* dst);
void finishContentEntry(const SkClipStack*, SkBlendMode, SkPDFIndirectReference, SkPath*);
bool isContentEmpty();
- void populateGraphicStateEntryFromPaint(const SkMatrix& matrix,
- const SkClipStack* clipStack,
- const SkPaint& paint,
- bool hasText,
- GraphicStateEntry* entry);
-
void internalDrawGlyphRun(const SkGlyphRun& glyphRun, SkPoint offset, const SkPaint& runPaint);
void drawGlyphRunAsPath(const SkGlyphRun& glyphRun, SkPoint offset, const SkPaint& runPaint);