1/*
2 * Copyright 2018 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 "include/core/SkFont.h"
9#include "include/core/SkFontMetrics.h"
10#include "include/core/SkFontMgr.h"
11#include "include/core/SkFontStyle.h"
12#include "include/core/SkString.h"
13#include "include/core/SkTypeface.h"
14#include "include/private/SkTFitsIn.h"
15#include "modules/skshaper/include/SkShaper.h"
16#include "src/core/SkTextBlobPriv.h"
17#include "src/utils/SkUTF.h"
18
19#include <limits.h>
20#include <string.h>
21#include <locale>
22#include <string>
23#include <utility>
24
25std::unique_ptr<SkShaper> SkShaper::Make(sk_sp<SkFontMgr> fontmgr) {
26#ifdef SK_SHAPER_HARFBUZZ_AVAILABLE
27 std::unique_ptr<SkShaper> shaper = SkShaper::MakeShaperDrivenWrapper(std::move(fontmgr));
28 if (shaper) {
29 return shaper;
30 }
31#endif
32 return SkShaper::MakePrimitive();
33}
34
35std::unique_ptr<SkShaper::BiDiRunIterator>
36SkShaper::MakeBiDiRunIterator(const char* utf8, size_t utf8Bytes, uint8_t bidiLevel) {
37#ifdef SK_SHAPER_HARFBUZZ_AVAILABLE
38 std::unique_ptr<SkShaper::BiDiRunIterator> bidi =
39 SkShaper::MakeIcuBiDiRunIterator(utf8, utf8Bytes, bidiLevel);
40 if (bidi) {
41 return bidi;
42 }
43#endif
44 return std::make_unique<SkShaper::TrivialBiDiRunIterator>(bidiLevel, utf8Bytes);
45}
46
47std::unique_ptr<SkShaper::ScriptRunIterator>
48SkShaper::MakeScriptRunIterator(const char* utf8, size_t utf8Bytes, SkFourByteTag scriptTag) {
49#ifdef SK_SHAPER_HARFBUZZ_AVAILABLE
50 std::unique_ptr<SkShaper::ScriptRunIterator> script =
51 SkShaper::MakeHbIcuScriptRunIterator(utf8, utf8Bytes);
52 if (script) {
53 return script;
54 }
55#endif
56 return std::make_unique<SkShaper::TrivialScriptRunIterator>(scriptTag, utf8Bytes);
57}
58
59SkShaper::SkShaper() {}
60SkShaper::~SkShaper() {}
61
62/** Replaces invalid utf-8 sequences with REPLACEMENT CHARACTER U+FFFD. */
63static inline SkUnichar utf8_next(const char** ptr, const char* end) {
64 SkUnichar val = SkUTF::NextUTF8(ptr, end);
65 return val < 0 ? 0xFFFD : val;
66}
67
68class FontMgrRunIterator final : public SkShaper::FontRunIterator {
69public:
70 FontMgrRunIterator(const char* utf8, size_t utf8Bytes,
71 const SkFont& font, sk_sp<SkFontMgr> fallbackMgr,
72 const char* requestName, SkFontStyle requestStyle,
73 const SkShaper::LanguageRunIterator* lang)
74 : fCurrent(utf8), fBegin(utf8), fEnd(fCurrent + utf8Bytes)
75 , fFallbackMgr(std::move(fallbackMgr))
76 , fFont(font)
77 , fFallbackFont(fFont)
78 , fCurrentFont(nullptr)
79 , fRequestName(requestName)
80 , fRequestStyle(requestStyle)
81 , fLanguage(lang)
82 {
83 fFont.setTypeface(font.refTypefaceOrDefault());
84 fFallbackFont.setTypeface(nullptr);
85 }
86 FontMgrRunIterator(const char* utf8, size_t utf8Bytes,
87 const SkFont& font, sk_sp<SkFontMgr> fallbackMgr)
88 : FontMgrRunIterator(utf8, utf8Bytes, font, std::move(fallbackMgr),
89 nullptr, font.refTypefaceOrDefault()->fontStyle(), nullptr)
90 {}
91
92 void consume() override {
93 SkASSERT(fCurrent < fEnd);
94 SkASSERT(!fLanguage || this->endOfCurrentRun() <= fLanguage->endOfCurrentRun());
95 SkUnichar u = utf8_next(&fCurrent, fEnd);
96 // If the starting typeface can handle this character, use it.
97 if (fFont.unicharToGlyph(u)) {
98 fCurrentFont = &fFont;
99 // If the current fallback can handle this character, use it.
100 } else if (fFallbackFont.getTypeface() && fFallbackFont.unicharToGlyph(u)) {
101 fCurrentFont = &fFallbackFont;
102 // If not, try to find a fallback typeface
103 } else {
104 const char* language = fLanguage ? fLanguage->currentLanguage() : nullptr;
105 int languageCount = fLanguage ? 1 : 0;
106 sk_sp<SkTypeface> candidate(fFallbackMgr->matchFamilyStyleCharacter(
107 fRequestName, fRequestStyle, &language, languageCount, u));
108 if (candidate) {
109 fFallbackFont.setTypeface(std::move(candidate));
110 fCurrentFont = &fFallbackFont;
111 } else {
112 fCurrentFont = &fFont;
113 }
114 }
115
116 while (fCurrent < fEnd) {
117 const char* prev = fCurrent;
118 u = utf8_next(&fCurrent, fEnd);
119
120 // End run if not using initial typeface and initial typeface has this character.
121 if (fCurrentFont->getTypeface() != fFont.getTypeface() && fFont.unicharToGlyph(u)) {
122 fCurrent = prev;
123 return;
124 }
125
126 // End run if current typeface does not have this character and some other font does.
127 if (!fCurrentFont->unicharToGlyph(u)) {
128 const char* language = fLanguage ? fLanguage->currentLanguage() : nullptr;
129 int languageCount = fLanguage ? 1 : 0;
130 sk_sp<SkTypeface> candidate(fFallbackMgr->matchFamilyStyleCharacter(
131 fRequestName, fRequestStyle, &language, languageCount, u));
132 if (candidate) {
133 fCurrent = prev;
134 return;
135 }
136 }
137 }
138 }
139 size_t endOfCurrentRun() const override {
140 return fCurrent - fBegin;
141 }
142 bool atEnd() const override {
143 return fCurrent == fEnd;
144 }
145
146 const SkFont& currentFont() const override {
147 return *fCurrentFont;
148 }
149
150private:
151 char const * fCurrent;
152 char const * const fBegin;
153 char const * const fEnd;
154 sk_sp<SkFontMgr> const fFallbackMgr;
155 SkFont fFont;
156 SkFont fFallbackFont;
157 SkFont* fCurrentFont;
158 char const * const fRequestName;
159 SkFontStyle const fRequestStyle;
160 SkShaper::LanguageRunIterator const * const fLanguage;
161};
162
163std::unique_ptr<SkShaper::FontRunIterator>
164SkShaper::MakeFontMgrRunIterator(const char* utf8, size_t utf8Bytes,
165 const SkFont& font, sk_sp<SkFontMgr> fallback)
166{
167 return std::make_unique<FontMgrRunIterator>(utf8, utf8Bytes, font, std::move(fallback));
168}
169
170std::unique_ptr<SkShaper::FontRunIterator>
171SkShaper::MakeFontMgrRunIterator(const char* utf8, size_t utf8Bytes, const SkFont& font,
172 sk_sp<SkFontMgr> fallback,
173 const char* requestName, SkFontStyle requestStyle,
174 const SkShaper::LanguageRunIterator* language)
175{
176 return std::make_unique<FontMgrRunIterator>(utf8, utf8Bytes, font, std::move(fallback),
177 requestName, requestStyle, language);
178}
179
180std::unique_ptr<SkShaper::LanguageRunIterator>
181SkShaper::MakeStdLanguageRunIterator(const char* utf8, size_t utf8Bytes) {
182 return std::make_unique<TrivialLanguageRunIterator>(std::locale().name().c_str(), utf8Bytes);
183}
184
185void SkTextBlobBuilderRunHandler::beginLine() {
186 fCurrentPosition = fOffset;
187 fMaxRunAscent = 0;
188 fMaxRunDescent = 0;
189 fMaxRunLeading = 0;
190}
191void SkTextBlobBuilderRunHandler::runInfo(const RunInfo& info) {
192 SkFontMetrics metrics;
193 info.fFont.getMetrics(&metrics);
194 fMaxRunAscent = std::min(fMaxRunAscent, metrics.fAscent);
195 fMaxRunDescent = std::max(fMaxRunDescent, metrics.fDescent);
196 fMaxRunLeading = std::max(fMaxRunLeading, metrics.fLeading);
197}
198
199void SkTextBlobBuilderRunHandler::commitRunInfo() {
200 fCurrentPosition.fY -= fMaxRunAscent;
201}
202
203SkShaper::RunHandler::Buffer SkTextBlobBuilderRunHandler::runBuffer(const RunInfo& info) {
204 int glyphCount = SkTFitsIn<int>(info.glyphCount) ? info.glyphCount : INT_MAX;
205 int utf8RangeSize = SkTFitsIn<int>(info.utf8Range.size()) ? info.utf8Range.size() : INT_MAX;
206
207 const auto& runBuffer = SkTextBlobBuilderPriv::AllocRunTextPos(&fBuilder, info.fFont, glyphCount,
208 utf8RangeSize, SkString());
209 if (runBuffer.utf8text && fUtf8Text) {
210 memcpy(runBuffer.utf8text, fUtf8Text + info.utf8Range.begin(), utf8RangeSize);
211 }
212 fClusters = runBuffer.clusters;
213 fGlyphCount = glyphCount;
214 fClusterOffset = info.utf8Range.begin();
215
216 return { runBuffer.glyphs,
217 runBuffer.points(),
218 nullptr,
219 runBuffer.clusters,
220 fCurrentPosition };
221}
222
223void SkTextBlobBuilderRunHandler::commitRunBuffer(const RunInfo& info) {
224 SkASSERT(0 <= fClusterOffset);
225 for (int i = 0; i < fGlyphCount; ++i) {
226 SkASSERT(fClusters[i] >= (unsigned)fClusterOffset);
227 fClusters[i] -= fClusterOffset;
228 }
229 fCurrentPosition += info.fAdvance;
230}
231void SkTextBlobBuilderRunHandler::commitLine() {
232 fOffset += { 0, fMaxRunDescent + fMaxRunLeading - fMaxRunAscent };
233}
234
235sk_sp<SkTextBlob> SkTextBlobBuilderRunHandler::makeBlob() {
236 return fBuilder.make();
237}
238