/* * Copyright 2016 Google Inc. * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #include "include/core/SkFontMetrics.h" #include "include/core/SkStream.h" #include "include/core/SkTypeface.h" #include "include/private/SkTo.h" #include "modules/skshaper/include/SkShaper.h" #include "src/utils/SkUTF.h" #ifdef USE_SKIA_TXT namespace SkiaRsText{ #endif class SkShaperPrimitive : public SkShaper { public: SkShaperPrimitive() {} private: void shape(const char* utf8, size_t utf8Bytes, #ifdef USE_SKIA_TXT const RSFont& srcFont, #else const SkFont& srcFont, #endif bool leftToRight, SkScalar width, RunHandler*) const override; void shape(const char* utf8, size_t utf8Bytes, FontRunIterator&, BiDiRunIterator&, ScriptRunIterator&, LanguageRunIterator&, SkScalar width, RunHandler*) const override; void shape(const char* utf8, size_t utf8Bytes, FontRunIterator&, BiDiRunIterator&, ScriptRunIterator&, LanguageRunIterator&, const Feature*, size_t featureSize, SkScalar width, RunHandler*) const override; }; std::unique_ptr SkShaper::MakePrimitive() { return std::make_unique(); } static inline bool is_breaking_whitespace(SkUnichar c) { switch (c) { case 0x0020: // SPACE //case 0x00A0: // NO-BREAK SPACE case 0x1680: // OGHAM SPACE MARK case 0x180E: // MONGOLIAN VOWEL SEPARATOR case 0x2000: // EN QUAD case 0x2001: // EM QUAD case 0x2002: // EN SPACE (nut) case 0x2003: // EM SPACE (mutton) case 0x2004: // THREE-PER-EM SPACE (thick space) case 0x2005: // FOUR-PER-EM SPACE (mid space) case 0x2006: // SIX-PER-EM SPACE case 0x2007: // FIGURE SPACE case 0x2008: // PUNCTUATION SPACE case 0x2009: // THIN SPACE case 0x200A: // HAIR SPACE case 0x200B: // ZERO WIDTH SPACE case 0x202F: // NARROW NO-BREAK SPACE case 0x205F: // MEDIUM MATHEMATICAL SPACE case 0x3000: // IDEOGRAPHIC SPACE //case 0xFEFF: // ZERO WIDTH NO-BREAK SPACE return true; default: return false; } } static size_t linebreak(const char text[], const char stop[], #ifdef USE_SKIA_TXT const RSFont& font, SkScalar width, #else const SkFont& font, SkScalar width, #endif SkScalar* advance, size_t* trailing) { SkScalar accumulatedWidth = 0; int glyphIndex = 0; const char* start = text; const char* wordStart = text; bool prevWS = true; *trailing = 0; while (text < stop) { const char* prevText = text; SkUnichar uni = SkUTF::NextUTF8(&text, stop); accumulatedWidth += advance[glyphIndex++]; bool currWS = is_breaking_whitespace(uni); if (!currWS && prevWS) { wordStart = prevText; } prevWS = currWS; if (width < accumulatedWidth) { bool consumeWhitespace = false; if (currWS) { // previous fit, put this and following whitespace in trailing if (prevText == start) { // don't put this in trailing if it's the first thing prevText = text; } consumeWhitespace = true; } else if (wordStart != start) { // backup to the last whitespace that fit text = wordStart; } else if (prevText > start) { // backup to just before the glyph that didn't fit text = prevText; } else { // let it overflow, put any following whitespace in trailing prevText = text; consumeWhitespace = true; } if (consumeWhitespace) { const char* next = text; while (next < stop && is_breaking_whitespace(SkUTF::NextUTF8(&next, stop))) { text = next; } if (trailing) { *trailing = text - prevText; } } break; } } return text - start; } void SkShaperPrimitive::shape(const char* utf8, size_t utf8Bytes, FontRunIterator& font, BiDiRunIterator& bidi, ScriptRunIterator&, LanguageRunIterator&, SkScalar width, RunHandler* handler) const { #ifdef USE_SKIA_TXT RSFont skfont; #else SkFont skfont; #endif if (!font.atEnd()) { font.consume(); skfont = font.currentFont(); } else { #ifdef USE_SKIA_TXT skfont.SetTypeface(RSTypeface::MakeDefault()); #else skfont.setTypeface(sk_ref_sp(skfont.getTypefaceOrDefault())); #endif } SkASSERT(skfont.getTypeface()); bool skbidi = 0; if (!bidi.atEnd()) { bidi.consume(); skbidi = (bidi.currentLevel() % 2) == 0; } return this->shape(utf8, utf8Bytes, skfont, skbidi, width, handler); } void SkShaperPrimitive::shape(const char* utf8, size_t utf8Bytes, FontRunIterator& font, BiDiRunIterator& bidi, ScriptRunIterator&, LanguageRunIterator&, const Feature*, size_t, SkScalar width, RunHandler* handler) const { font.consume(); SkASSERT(font.currentFont().getTypeface()); bidi.consume(); return this->shape(utf8, utf8Bytes, font.currentFont(), (bidi.currentLevel() % 2) == 0, width, handler); } void SkShaperPrimitive::shape(const char* utf8, size_t utf8Bytes, #ifdef USE_SKIA_TXT const RSFont& font, #else const SkFont& font, #endif bool leftToRight, SkScalar width, RunHandler* handler) const { sk_ignore_unused_variable(leftToRight); #ifdef USE_SKIA_TXT int glyphCount = font.CountText(utf8, utf8Bytes, RSDrawing::TextEncoding::UTF8); #else int glyphCount = font.countText(utf8, utf8Bytes, SkTextEncoding::kUTF8); #endif if (glyphCount < 0) { return; } std::unique_ptr glyphs(new SkGlyphID[glyphCount]); #ifdef USE_SKIA_TXT font.TextToGlyphs(utf8, utf8Bytes, RSDrawing::TextEncoding::UTF8, glyphs.get(), glyphCount); #else font.textToGlyphs(utf8, utf8Bytes, SkTextEncoding::kUTF8, glyphs.get(), glyphCount); #endif std::unique_ptr advances(new SkScalar[glyphCount]); #ifdef USE_SKIA_TXT font.GetWidths(glyphs.get(), glyphCount, advances.get(), nullptr); #else font.getWidthsBounds(glyphs.get(), glyphCount, advances.get(), nullptr, nullptr); #endif size_t glyphOffset = 0; size_t utf8Offset = 0; do { size_t bytesCollapsed; size_t bytesConsumed = linebreak(utf8, utf8 + utf8Bytes, font, width, advances.get() + glyphOffset, &bytesCollapsed); size_t bytesVisible = bytesConsumed - bytesCollapsed; size_t numGlyphs = SkUTF::CountUTF8(utf8, bytesVisible); const RunHandler::RunInfo info = { font, 0, #ifdef USE_SKIA_TXT { font.MeasureText(utf8, bytesVisible, RSDrawing::TextEncoding::UTF8), 0 }, #else { font.measureText(utf8, bytesVisible, SkTextEncoding::kUTF8), 0 }, #endif numGlyphs, RunHandler::Range(utf8Offset, bytesVisible) }; handler->beginLine(); if (info.glyphCount) { handler->runInfo(info); } handler->commitRunInfo(); if (info.glyphCount) { const auto buffer = handler->runBuffer(info); memcpy(buffer.glyphs, glyphs.get() + glyphOffset, info.glyphCount * sizeof(SkGlyphID)); SkPoint position = buffer.point; for (size_t i = 0; i < info.glyphCount; ++i) { buffer.positions[i] = position; position.fX += advances[i + glyphOffset]; } if (buffer.clusters) { const char* txtPtr = utf8; for (size_t i = 0; i < info.glyphCount; ++i) { // Each character maps to exactly one glyph. buffer.clusters[i] = SkToU32(txtPtr - utf8 + utf8Offset); SkUTF::NextUTF8(&txtPtr, utf8 + utf8Bytes); } } handler->commitRunBuffer(info); } handler->commitLine(); glyphOffset += SkUTF::CountUTF8(utf8, bytesConsumed); utf8Offset += bytesConsumed; utf8 += bytesConsumed; utf8Bytes -= bytesConsumed; } while (0 < utf8Bytes); return; } #ifdef USE_SKIA_TXT } #endif