From 98caedd213775b61d93a320909e947735c626968 Mon Sep 17 00:00:00 2001 From: Hal Canary Date: Mon, 23 Jul 2018 10:50:49 -0400 Subject: SkPDF: only draw text with SkglyphRuns Change-Id: I24e79c73a9c65a5d6a974bf52b0d0aee21be07db Reviewed-on: https://skia-review.googlesource.com/142695 Commit-Queue: Hal Canary Reviewed-by: Herb Derby --- src/pdf/SkClusterator.cpp | 98 ++++--------------------- src/pdf/SkClusterator.h | 17 +---- src/pdf/SkPDFDevice.cpp | 182 ++++++++++++++-------------------------------- src/pdf/SkPDFDevice.h | 6 +- 4 files changed, 71 insertions(+), 232 deletions(-) (limited to 'src') diff --git a/src/pdf/SkClusterator.cpp b/src/pdf/SkClusterator.cpp index 6c56eb6b45..1b74d38f11 100644 --- a/src/pdf/SkClusterator.cpp +++ b/src/pdf/SkClusterator.cpp @@ -7,6 +7,7 @@ #include "SkClusterator.h" +#include "SkGlyphRun.h" #include "SkTo.h" #include "SkUtils.h" @@ -24,90 +25,18 @@ static bool is_reversed(const uint32_t* clusters, uint32_t count) { return true; } -SkClusterator::SkClusterator(const void* sourceText, - size_t sourceByteCount, - const SkPaint& paint, - const uint32_t* clusters, - uint32_t utf8TextByteLength, - const char* utf8Text) { - if (SkPaint::kGlyphID_TextEncoding == paint.getTextEncoding()) { - fGlyphs = reinterpret_cast(sourceText); - fClusters = clusters; - fUtf8Text = utf8Text; - fGlyphCount = sourceByteCount / sizeof(SkGlyphID); - fTextByteLength = utf8TextByteLength; - if (fClusters) { - SkASSERT(fUtf8Text && fTextByteLength > 0 && fGlyphCount > 0); - fReversedChars = is_reversed(fClusters, fGlyphCount); - } else { - SkASSERT(!fUtf8Text && fTextByteLength == 0); - } - return; - } - - // If Skia is given text (not glyphs), then our fallback primitive shaping will - // produce a simple 1-1 cluster mapping. - fGlyphCount = SkToU32(paint.textToGlyphs(sourceText, sourceByteCount, nullptr)); - fGlyphStorage.resize(fGlyphCount); - (void)paint.textToGlyphs(sourceText, sourceByteCount, fGlyphStorage.data()); - fGlyphs = fGlyphStorage.data(); - fClusterStorage.resize(fGlyphCount); - fClusters = fClusterStorage.data(); - - switch (paint.getTextEncoding()) { - case SkPaint::kUTF8_TextEncoding: - { - fUtf8Text = reinterpret_cast(sourceText); - fTextByteLength = SkToU32(sourceByteCount); - const char* txtPtr = fUtf8Text; - for (uint32_t i = 0; i < fGlyphCount; ++i) { - fClusterStorage[i] = SkToU32(txtPtr - fUtf8Text); - txtPtr += SkUTF8_LeadByteToCount(*(const unsigned char*)txtPtr); - SkASSERT(txtPtr <= fUtf8Text + sourceByteCount); - } - SkASSERT(txtPtr == fUtf8Text + sourceByteCount); - return; - } - case SkPaint::kUTF16_TextEncoding: - { - const uint16_t* utf16ptr = reinterpret_cast(sourceText); - int utf16count = SkToInt(sourceByteCount / sizeof(uint16_t)); - fTextByteLength = SkToU32(SkUTF16_ToUTF8(utf16ptr, utf16count)); - fUtf8textStorage.resize(fTextByteLength); - fUtf8Text = fUtf8textStorage.data(); - char* txtPtr = fUtf8textStorage.data(); - uint32_t clusterIndex = 0; - while (utf16ptr < (const uint16_t*)sourceText + utf16count) { - fClusterStorage[clusterIndex++] = SkToU32(txtPtr - fUtf8Text); - SkUnichar uni = SkUTF16_NextUnichar(&utf16ptr); - txtPtr += SkUTF8_FromUnichar(uni, txtPtr); - } - SkASSERT(clusterIndex == fGlyphCount); - SkASSERT(txtPtr == fUtf8textStorage.data() + fTextByteLength); - SkASSERT(utf16ptr == (const uint16_t*)sourceText + utf16count); - return; - } - case SkPaint::kUTF32_TextEncoding: - { - const SkUnichar* utf32 = reinterpret_cast(sourceText); - uint32_t utf32count = SkToU32(sourceByteCount / sizeof(SkUnichar)); - SkASSERT(fGlyphCount == utf32count); - fTextByteLength = 0; - for (uint32_t i = 0; i < utf32count; ++i) { - fTextByteLength += SkToU32(SkUTF8_FromUnichar(utf32[i])); - } - fUtf8textStorage.resize(SkToSizeT(fTextByteLength)); - fUtf8Text = fUtf8textStorage.data(); - char* txtPtr = fUtf8textStorage.data(); - for (uint32_t i = 0; i < utf32count; ++i) { - fClusterStorage[i] = SkToU32(txtPtr - fUtf8Text); - txtPtr += SkUTF8_FromUnichar(utf32[i], txtPtr); - } - return; - } - default: - SkDEBUGFAIL(""); - break; +SkClusterator::SkClusterator(const SkGlyphRun& run) + : fClusters(run.clusters().data()) + , fUtf8Text(run.text().data()) + , fGlyphCount(SkToU32(run.shuntGlyphsIDs().size())) + , fTextByteLength(SkToU32(run.text().size())) +{ + SkASSERT(SkPaint::kGlyphID_TextEncoding == run.paint().getTextEncoding()); + if (fClusters) { + SkASSERT(fUtf8Text && fTextByteLength > 0 && fGlyphCount > 0); + fReversedChars = is_reversed(fClusters, fGlyphCount); + } else { + SkASSERT(!fUtf8Text && fTextByteLength == 0); } } @@ -134,4 +63,3 @@ SkClusterator::Cluster SkClusterator::next() { uint32_t clusterLen = clusterEnd - cluster; return Cluster{fUtf8Text + cluster, clusterLen, clusterGlyphIndex, clusterGlyphCount}; } - diff --git a/src/pdf/SkClusterator.h b/src/pdf/SkClusterator.h index 97aded2197..40d9994ba3 100644 --- a/src/pdf/SkClusterator.h +++ b/src/pdf/SkClusterator.h @@ -8,21 +8,15 @@ #define SkClusterator_DEFINED #include +#include -#include "SkTypes.h" -#include "SkPaint.h" +class SkGlyphRun; /** Given the m-to-n glyph-to-character mapping data (as returned by harfbuzz), iterate over the clusters. */ class SkClusterator { public: - SkClusterator(const void* sourceText, - size_t sourceByteCount, - const SkPaint& paint, - const uint32_t* clusters, - uint32_t utf8TextByteLength, - const char* utf8Text); - const SkGlyphID* glyphs() const { return fGlyphs; } + SkClusterator(const SkGlyphRun& run); uint32_t glyphCount() const { return fGlyphCount; } bool reversedChars() const { return fReversedChars; } struct Cluster { @@ -37,15 +31,10 @@ public: && fGlyphIndex == o.fGlyphIndex && fGlyphCount == o.fGlyphCount; } - }; Cluster next(); private: - std::vector fGlyphStorage; - std::vector fUtf8textStorage; - std::vector fClusterStorage; - const SkGlyphID* fGlyphs; const uint32_t* fClusters; const char* fUtf8Text; uint32_t fGlyphCount; diff --git a/src/pdf/SkPDFDevice.cpp b/src/pdf/SkPDFDevice.cpp index d7a0f501c1..88955a1b9c 100644 --- a/src/pdf/SkPDFDevice.cpp +++ b/src/pdf/SkPDFDevice.cpp @@ -1033,13 +1033,11 @@ public: GlyphPositioner(SkDynamicMemoryWStream* content, SkScalar textSkewX, bool wideChars, - bool defaultPositioning, SkPoint origin) : fContent(content) , fCurrentMatrixOrigin(origin) , fTextSkewX(textSkewX) - , fWideChars(wideChars) - , fDefaultPositioning(defaultPositioning) { + , fWideChars(wideChars) { } ~GlyphPositioner() { this->flush(); } void flush() { @@ -1064,19 +1062,17 @@ public: fCurrentMatrixOrigin.set(0.0f, 0.0f); fInitialized = true; } - if (!fDefaultPositioning) { - SkPoint position = xy - fCurrentMatrixOrigin; - if (position != SkPoint{fXAdvance, 0}) { - this->flush(); - SkPDFUtils::AppendScalar(position.x() - position.y() * fTextSkewX, fContent); - fContent->writeText(" "); - SkPDFUtils::AppendScalar(-position.y(), fContent); - fContent->writeText(" Td "); - fCurrentMatrixOrigin = xy; - fXAdvance = 0; - } - fXAdvance += advanceWidth; + SkPoint position = xy - fCurrentMatrixOrigin; + if (position != SkPoint{fXAdvance, 0}) { + this->flush(); + SkPDFUtils::AppendScalar(position.x() - position.y() * fTextSkewX, fContent); + fContent->writeText(" "); + SkPDFUtils::AppendScalar(-position.y(), fContent); + fContent->writeText(" Td "); + fCurrentMatrixOrigin = xy; + fXAdvance = 0; } + fXAdvance += advanceWidth; if (!fInText) { fContent->writeText("<"); fInText = true; @@ -1097,7 +1093,6 @@ private: bool fWideChars; bool fInText = false; bool fInitialized = false; - const bool fDefaultPositioning; }; } // namespace @@ -1115,30 +1110,15 @@ static void update_font(SkWStream* wStream, int fontIndex, SkScalar textSize) { wStream->writeText(" Tf\n"); } -static SkPath draw_text_as_path(const void* sourceText, size_t sourceByteCount, - const SkScalar pos[], SkTextBlob::GlyphPositioning positioning, - SkPoint offset, const SkPaint& srcPaint) { +static void draw_glyph_run_as_path(SkPDFDevice* dev, const SkGlyphRun& glyphRun, SkPoint offset) { SkPath path; - int glyphCount; - SkAutoTMalloc tmpPoints; - switch (positioning) { - case SkTextBlob::kDefault_Positioning: - srcPaint.getTextPath(sourceText, sourceByteCount, offset.x(), offset.y(), &path); - break; - case SkTextBlob::kHorizontal_Positioning: - glyphCount = srcPaint.countText(sourceText, sourceByteCount); - tmpPoints.realloc(glyphCount); - for (int i = 0; i < glyphCount; ++i) { - tmpPoints[i] = {pos[i] + offset.x(), offset.y()}; - } - srcPaint.getPosTextPath(sourceText, sourceByteCount, tmpPoints.get(), &path); - break; - case SkTextBlob::kFull_Positioning: - srcPaint.getPosTextPath(sourceText, sourceByteCount, (const SkPoint*)pos, &path); - path.offset(offset.x(), offset.y()); - break; - } - return path; + SkASSERT(glyphRun.paint().getTextEncoding() == SkPaint::kGlyphID_TextEncoding); + glyphRun.paint().getPosTextPath(glyphRun.shuntGlyphsIDs().data(), + glyphRun.shuntGlyphsIDs().size() * sizeof(SkGlyphID), + glyphRun.positions().data(), + &path); + path.offset(offset.x(), offset.y()); + dev->drawPath(path, glyphRun.paint(), nullptr, true); } static bool has_outline_glyph(SkGlyphID gid, SkGlyphCache* cache) { @@ -1205,36 +1185,25 @@ static sk_sp image_from_mask(const SkMask& mask) { } } -void SkPDFDevice::internalDrawText( - const void* sourceText, size_t sourceByteCount, - const SkScalar pos[], SkTextBlob::GlyphPositioning positioning, - SkPoint offset, const SkPaint& srcPaint, const uint32_t* clusters, - uint32_t textByteLength, const char* utf8Text) { - if (0 == sourceByteCount || !sourceText || srcPaint.getTextSize() <= 0) { - return; - } - if (this->hasEmptyClip()) { +void SkPDFDevice::internalDrawGlyphRun(const SkGlyphRun& glyphRun, SkPoint offset) { + + const SkGlyphID* glyphs = glyphRun.shuntGlyphsIDs().data(); + uint32_t glyphCount = SkToU32(glyphRun.shuntGlyphsIDs().size()); + const SkPaint& srcPaint = glyphRun.paint(); + if (!glyphCount || !glyphs || srcPaint.getTextSize() <= 0 || this->hasEmptyClip()) { return; } - NOT_IMPLEMENTED(srcPaint.isVerticalText(), false); - if (srcPaint.isVerticalText()) { - // Don't pretend we support drawing vertical text. It is not - // clear to me how to switch to "vertical writing" mode in PDF. - // Currently neither Chromium or Android set this flag. - // https://bug.skia.org/5665 - } if (srcPaint.getPathEffect() - || srcPaint.getMaskFilter() - || SkPaint::kFill_Style != srcPaint.getStyle()) { + || srcPaint.getMaskFilter() + || srcPaint.isVerticalText() + || SkPaint::kFill_Style != srcPaint.getStyle()) { // Stroked Text doesn't work well with Type3 fonts. - SkPath path = draw_text_as_path(sourceText, sourceByteCount, pos, - positioning, offset, srcPaint); - this->drawPath(path, srcPaint, nullptr, true); - return; + return draw_glyph_run_as_path(this, glyphRun, offset); } SkPaint paint = calculate_text_paint(srcPaint); remove_color_filter(&paint); replace_srcmode_on_opaque_paint(&paint); + paint.setHinting(SkPaint::kNo_Hinting); if (!paint.getTypeface()) { paint.setTypeface(SkTypeface::MakeDefault()); } @@ -1243,25 +1212,14 @@ void SkPDFDevice::internalDrawText( SkDebugf("SkPDF: SkTypeface::MakeDefault() returned nullptr.\n"); return; } - - const SkAdvancedTypefaceMetrics* metrics = - SkPDFFont::GetMetrics(typeface, fDocument->canon()); + const SkAdvancedTypefaceMetrics* metrics = SkPDFFont::GetMetrics(typeface, fDocument->canon()); if (!metrics) { return; } const std::vector& glyphToUnicode = SkPDFFont::GetUnicodeMap( typeface, fDocument->canon()); - SkClusterator clusterator(sourceText, sourceByteCount, paint, - clusters, textByteLength, utf8Text); - const SkGlyphID* glyphs = clusterator.glyphs(); - uint32_t glyphCount = clusterator.glyphCount(); - if (glyphCount == 0) { - return; - } - - bool defaultPositioning = (positioning == SkTextBlob::kDefault_Positioning); - paint.setHinting(SkPaint::kNo_Hinting); + SkClusterator clusterator(glyphRun); int emSize; auto glyphCache = SkPDFFont::MakeVectorCache(typeface, &emSize); @@ -1273,23 +1231,13 @@ void SkPDFDevice::internalDrawText( SkScalar textScaleY = textSize / emSize; SkScalar textScaleX = advanceScale + paint.getTextSkewX() * textScaleY; - SkPaint::Align alignment = paint.getTextAlign(); - float alignmentFactor = SkPaint::kLeft_Align == alignment ? 0.0f : - SkPaint::kCenter_Align == alignment ? -0.5f : - /* SkPaint::kRight_Align */ -1.0f; - if (defaultPositioning && alignment != SkPaint::kLeft_Align) { - SkScalar advance = 0; - for (uint32_t i = 0; i < glyphCount; ++i) { - advance += advanceScale * glyphCache->getGlyphIDAdvance(glyphs[i]).fAdvanceX; - } - offset.offset(alignmentFactor * advance, 0); - } + SkASSERT(paint.getTextAlign() == SkPaint::kLeft_Align); SkRect clipStackBounds = this->cs().bounds(this->bounds()); struct PositionedGlyph { SkPoint fPos; SkGlyphID fGlyph; }; - SkTArray fMissingGlyphs; + SkTArray missingGlyphs; { ScopedContentEntry content(this, paint, true); if (!content.entry()) { @@ -1310,7 +1258,6 @@ void SkPDFDevice::internalDrawText( GlyphPositioner glyphPositioner(out, paint.getTextSkewX(), multiByteGlyphs, - defaultPositioning, offset); SkPDFFont* font = nullptr; @@ -1374,45 +1321,33 @@ void SkPDFDevice::internalDrawText( } SkASSERT(font->multiByteGlyphs() == multiByteGlyphs); } - SkPoint xy = {0, 0}; - SkScalar advance = advanceScale * glyphCache->getGlyphIDAdvance(gid).fAdvanceX; - if (!defaultPositioning) { - xy = SkTextBlob::kFull_Positioning == positioning - ? SkPoint{pos[2 * index], pos[2 * index + 1]} - : SkPoint{pos[index], 0}; - if (alignment != SkPaint::kLeft_Align) { - xy.offset(alignmentFactor * advance, 0); - } - // 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 - } - } - if (!has_outline_glyph(gid, glyphCache.get())) { - fMissingGlyphs.push_back({xy + offset, gid}); + 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 (!has_outline_glyph(gid, glyphCache.get())) { - fMissingGlyphs.push_back({offset, gid}); + if (!clipStackBounds.intersects(glyphBounds)) { + continue; // reject glyphs as out of bounds } - offset += SkPoint{advance, 0}; } + if (!has_outline_glyph(gid, glyphCache.get())) { + missingGlyphs.push_back({xy + offset, gid}); + } + font->noteGlyphUsage(gid); SkGlyphID encodedGlyph = multiByteGlyphs ? gid : font->glyphToPDFFontEncoding(gid); + SkScalar advance = advanceScale * glyphCache->getGlyphIDAdvance(gid).fAdvanceX; glyphPositioner.writeGlyph(xy, advance, encodedGlyph); } } } - if (fMissingGlyphs.count() > 0) { + if (missingGlyphs.count() > 0) { // Fall back on images. SkPaint scaledGlyphCachePaint; scaledGlyphCachePaint.setTextSize(paint.getTextSize()); @@ -1422,7 +1357,7 @@ void SkPDFDevice::internalDrawText( auto scaledGlyphCache = SkStrikeCache::FindOrCreateStrikeExclusive(scaledGlyphCachePaint); SkTHashMap* map = &this->getCanon()->fBitmapGlyphImages; - for (PositionedGlyph positionedGlyph : fMissingGlyphs) { + for (PositionedGlyph positionedGlyph : missingGlyphs) { SkPDFCanon::BitmapGlyphKey key = {typeface->uniqueID(), paint.getTextSize(), paint.getTextScaleX(), @@ -1451,20 +1386,9 @@ void SkPDFDevice::internalDrawText( } } -void SkPDFDevice::drawPosText(const void* text, size_t len, - const SkScalar pos[], int scalarsPerPos, - const SkPoint& offset, const SkPaint& paint) { - this->internalDrawText(text, len, pos, (SkTextBlob::GlyphPositioning)scalarsPerPos, - offset, paint, nullptr, 0, nullptr); -} - void SkPDFDevice::drawGlyphRunList(SkGlyphRunList* glyphRunList) { - for (SkGlyphRunListIterator it(glyphRunList); !it.done(); it.next()) { - SkPaint runPaint; - it.applyFontToPaint(&runPaint); - this->internalDrawText(it.glyphs(), sizeof(SkGlyphID) * it.glyphCount(), - it.pos(), it.positioning(), glyphRunList->origin(), runPaint, - it.clusters(), it.textSize(), it.text()); + for (const SkGlyphRun& glyphRun : *glyphRunList) { + this->internalDrawGlyphRun(glyphRun, glyphRunList->origin()); } } diff --git a/src/pdf/SkPDFDevice.h b/src/pdf/SkPDFDevice.h index eb293fc8a4..1ef4a20ed6 100644 --- a/src/pdf/SkPDFDevice.h +++ b/src/pdf/SkPDFDevice.h @@ -97,7 +97,7 @@ public: SkCanvas::SrcRectConstraint) override; void drawPosText(const void* text, size_t len, const SkScalar pos[], int scalarsPerPos, - const SkPoint& offset, const SkPaint&) override; + const SkPoint& offset, const SkPaint&) override { SkASSERT(false); } void drawGlyphRunList(SkGlyphRunList* glyphRunList) override; void drawVertices(const SkVertices*, const SkMatrix* bones, int boneCount, SkBlendMode, const SkPaint&) override; @@ -242,9 +242,7 @@ private: int getFontResourceIndex(SkTypeface* typeface, uint16_t glyphID); - void internalDrawText( const void*, size_t, const SkScalar pos[], - SkTextBlob::GlyphPositioning, SkPoint, const SkPaint&, - const uint32_t*, uint32_t, const char*); + void internalDrawGlyphRun(const SkGlyphRun& glyphRun, SkPoint offset); void internalDrawPaint(const SkPaint& paint, ContentEntry* contentEntry); -- cgit v1.2.3