/* * 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 "SkFontMetrics.h" #include "SkMakeUnique.h" #include "SkShaper.h" #include "SkStream.h" #include "SkTo.h" #include "SkTypeface.h" #include "SkUTF.h" class SkShaperPrimitive : public SkShaper { public: SkShaperPrimitive() {} private: SkPoint shape(RunHandler* handler, const SkFont& srcFont, const char* utf8text, size_t textBytes, bool leftToRight, SkPoint point, SkScalar width) const override; }; std::unique_ptr<SkShaper> SkShaper::MakePrimitive() { return skstd::make_unique<SkShaperPrimitive>(); } 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[], const SkFont& font, SkScalar width, SkScalar* advance, size_t* trailing) { SkScalar accumulatedWidth = 0; int glyphIndex = 0; const char* start = text; const char* word_start = 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) { word_start = prevText; } prevWS = currWS; if (width < accumulatedWidth) { if (currWS) { // eat the rest of the whitespace const char* next = text; while (next < stop && is_breaking_whitespace(SkUTF::NextUTF8(&next, stop))) { text = next; } if (trailing) { *trailing = text - prevText; } } else { // backup until a whitespace (or 1 char) if (word_start == start) { if (prevText > start) { text = prevText; } } else { text = word_start; } } break; } if ('\n' == uni) { size_t ret = text - start; size_t lineBreakSize = 1; if (text < stop) { uni = SkUTF::NextUTF8(&text, stop); if ('\r' == uni) { ret = text - start; ++lineBreakSize; } } if (trailing) { *trailing = lineBreakSize; } return ret; } if ('\r' == uni) { size_t ret = text - start; size_t lineBreakSize = 1; if (text < stop) { uni = SkUTF::NextUTF8(&text, stop); if ('\n' == uni) { ret = text - start; ++lineBreakSize; } } if (trailing) { *trailing = lineBreakSize; } return ret; } } return text - start; } SkPoint SkShaperPrimitive::shape(RunHandler* handler, const SkFont& font, const char* utf8text, size_t textBytes, bool leftToRight, SkPoint point, SkScalar width) const { sk_ignore_unused_variable(leftToRight); int glyphCount = font.countText(utf8text, textBytes, SkTextEncoding::kUTF8); if (glyphCount <= 0) { return point; } std::unique_ptr<SkGlyphID[]> glyphs(new SkGlyphID[glyphCount]); font.textToGlyphs(utf8text, textBytes, SkTextEncoding::kUTF8, glyphs.get(), glyphCount); std::unique_ptr<SkScalar[]> advances(new SkScalar[glyphCount]); font.getWidthsBounds(glyphs.get(), glyphCount, advances.get(), nullptr, nullptr); SkFontMetrics metrics; font.getMetrics(&metrics); size_t glyphOffset = 0; size_t utf8Offset = 0; while (0 < textBytes) { point.fY -= metrics.fAscent; size_t bytesCollapsed; size_t bytesConsumed = linebreak(utf8text, utf8text + textBytes, font, width, advances.get() + glyphOffset, &bytesCollapsed); size_t bytesVisible = bytesConsumed - bytesCollapsed; int numGlyphs = SkUTF::CountUTF8(utf8text, bytesVisible); const RunHandler::RunInfo info = { { font.measureText(utf8text, bytesVisible, SkTextEncoding::kUTF8), 0 }, metrics.fAscent, metrics.fDescent, metrics.fLeading, }; const auto buffer = handler->newRunBuffer(info, font, numGlyphs, SkSpan<const char>(utf8text, bytesVisible)); memcpy(buffer.glyphs, glyphs.get() + glyphOffset, numGlyphs * sizeof(SkGlyphID)); SkScalar position = point.fX; for (int i = 0; i < numGlyphs; ++i) { buffer.positions[i] = { position, point.fY }; position += advances[i + glyphOffset]; } if (buffer.clusters) { const char* txtPtr = utf8text; for (int i = 0; i < numGlyphs; ++i) { // Each character maps to exactly one glyph. buffer.clusters[i] = SkToU32(txtPtr - utf8text + utf8Offset); SkUTF::NextUTF8(&txtPtr, utf8text + textBytes); } } handler->commitRun(); handler->commitLine(); glyphOffset += SkUTF::CountUTF8(utf8text, bytesConsumed); utf8Offset += bytesConsumed; utf8text += bytesConsumed; textBytes -= bytesConsumed; point.fY += metrics.fDescent + metrics.fLeading; } return point; }