1 // Copyright 2019 Google LLC.
2 #include "modules/skparagraph/src/ParagraphBuilderImpl.h"
3
4 #include "include/core/SkRefCnt.h"
5 #include "include/core/SkTypes.h"
6 #include "modules/skparagraph/include/FontCollection.h"
7 #include "modules/skparagraph/include/Paragraph.h"
8 #include "modules/skparagraph/include/ParagraphBuilder.h"
9 #include "modules/skparagraph/include/ParagraphStyle.h"
10 #include "modules/skparagraph/include/TextStyle.h"
11 #include "modules/skparagraph/src/ParagraphImpl.h"
12 #include "modules/skunicode/include/SkUnicode.h"
13 #include "src/core/SkStringUtils.h"
14
15 #if !defined(SK_DISABLE_LEGACY_PARAGRAPH_UNICODE)
16 #if defined(SK_UNICODE_ICU_IMPLEMENTATION)
17 #include "modules/skunicode/include/SkUnicode_icu.h"
18 #endif
19
20 #if defined(SK_UNICODE_LIBGRAPHEME_IMPLEMENTATION)
21 #include "modules/skunicode/include/SkUnicode_libgrapheme.h"
22 #endif
23
24 #if defined(SK_UNICODE_ICU4X_IMPLEMENTATION)
25 #include "modules/skunicode/include/SkUnicode_icu4x.h"
26 #endif
27
28 #if defined(SK_UNICODE_CLIENT_IMPLEMENTATION)
29 #include "modules/skunicode/include/SkUnicode_client.h"
30 #endif
31
32 #endif // !defined(SK_DISABLE_LEGACY_PARAGRAPH_UNICODE)
33
34 #include <memory>
35 #include <utility>
36 #ifdef ENABLE_TEXT_ENHANCE
37 #include <algorithm>
38 #include "modules/skparagraph/src/ParagraphLineFetcherImpl.h"
39 #endif
40
41 namespace skia {
42 namespace textlayout {
43
44 #if !defined(SK_DISABLE_LEGACY_PARAGRAPH_UNICODE)
45
46 namespace {
47 // TODO(kjlubick,jlavrova) Remove these defines by having clients register something or somehow
48 // plumbing this all into the animation builder factories.
get_unicode()49 sk_sp<SkUnicode> get_unicode() {
50 #ifdef SK_UNICODE_ICU_IMPLEMENTATION
51 if (auto unicode = SkUnicodes::ICU::Make()) {
52 return unicode;
53 }
54 #endif
55 #ifdef SK_UNICODE_ICU4X_IMPLEMENTATION
56 if (auto unicode = SkUnicodes::ICU4X::Make()) {
57 return unicode;
58 }
59 #endif
60 #ifdef SK_UNICODE_LIBGRAPHEME_IMPLEMENTATION
61 if (auto unicode = SkUnicodes::Libgrapheme::Make()) {
62 return unicode;
63 }
64 #endif
65 return nullptr;
66 }
67 }
68
make(const ParagraphStyle & style,sk_sp<FontCollection> fontCollection)69 std::unique_ptr<ParagraphBuilder> ParagraphBuilder::make(const ParagraphStyle& style,
70 sk_sp<FontCollection> fontCollection) {
71 return ParagraphBuilderImpl::make(style, std::move(fontCollection), get_unicode());
72 }
73
make(const ParagraphStyle & style,sk_sp<FontCollection> fontCollection)74 std::unique_ptr<ParagraphBuilder> ParagraphBuilderImpl::make(const ParagraphStyle& style,
75 sk_sp<FontCollection> fontCollection) {
76 return std::make_unique<ParagraphBuilderImpl>(style, std::move(fontCollection), get_unicode());
77 }
78
ParagraphBuilderImpl(const ParagraphStyle & style,sk_sp<FontCollection> fontCollection)79 ParagraphBuilderImpl::ParagraphBuilderImpl(
80 const ParagraphStyle& style, sk_sp<FontCollection> fontCollection)
81 : ParagraphBuilderImpl(style, std::move(fontCollection), get_unicode())
82 { }
83
84 #endif // !defined(SK_DISABLE_LEGACY_PARAGRAPH_UNICODE)
85
make(const ParagraphStyle & style,sk_sp<FontCollection> fontCollection,sk_sp<SkUnicode> unicode)86 std::unique_ptr<ParagraphBuilder> ParagraphBuilder::make(const ParagraphStyle& style,
87 sk_sp<FontCollection> fontCollection,
88 sk_sp<SkUnicode> unicode) {
89 return ParagraphBuilderImpl::make(style, std::move(fontCollection), std::move(unicode));
90 }
91
make(const ParagraphStyle & style,sk_sp<FontCollection> fontCollection,sk_sp<SkUnicode> unicode)92 std::unique_ptr<ParagraphBuilder> ParagraphBuilderImpl::make(const ParagraphStyle& style,
93 sk_sp<FontCollection> fontCollection,
94 sk_sp<SkUnicode> unicode) {
95 return std::make_unique<ParagraphBuilderImpl>(style, std::move(fontCollection),
96 std::move(unicode));
97 }
98
ParagraphBuilderImpl(const ParagraphStyle & style,sk_sp<FontCollection> fontCollection,sk_sp<SkUnicode> unicode)99 ParagraphBuilderImpl::ParagraphBuilderImpl(
100 const ParagraphStyle& style, sk_sp<FontCollection> fontCollection, sk_sp<SkUnicode> unicode)
101 : ParagraphBuilder()
102 , fUtf8()
103 , fFontCollection(std::move(fontCollection))
104 , fParagraphStyle(style)
105 , fUnicode(std::move(unicode))
106 #if defined(SK_UNICODE_CLIENT_IMPLEMENTATION)
107 , fTextIsFinalized(false)
108 , fUsingClientInfo(false)
109 #endif
110 {
111 SkASSERT(fFontCollection);
112 startStyledBlock();
113 }
114
115
116 ParagraphBuilderImpl::~ParagraphBuilderImpl() = default;
117
pushStyle(const TextStyle & style)118 void ParagraphBuilderImpl::pushStyle(const TextStyle& style) {
119 fTextStyles.push_back(style);
120 if (!fStyledBlocks.empty() && fStyledBlocks.back().fRange.end == fUtf8.size() &&
121 fStyledBlocks.back().fStyle == style) {
122 // Just continue with the same style
123 } else {
124 // Go with the new style
125 startStyledBlock();
126 }
127 }
128
pop()129 void ParagraphBuilderImpl::pop() {
130 if (!fTextStyles.empty()) {
131 fTextStyles.pop_back();
132 } else {
133 // In this case we use paragraph style and skip Pop operation
134 SkDEBUGF("SkParagraphBuilder.Pop() called too many times.\n");
135 }
136
137 this->startStyledBlock();
138 }
139
internalPeekStyle()140 const TextStyle& ParagraphBuilderImpl::internalPeekStyle() {
141 if (fTextStyles.empty()) {
142 return fParagraphStyle.getTextStyle();
143 } else {
144 return fTextStyles.back();
145 }
146 }
147
peekStyle()148 TextStyle ParagraphBuilderImpl::peekStyle() {
149 return this->internalPeekStyle();
150 }
151
addText(const std::u16string & text)152 void ParagraphBuilderImpl::addText(const std::u16string& text) {
153 #if defined(SK_UNICODE_CLIENT_IMPLEMENTATION)
154 SkASSERT(!fTextIsFinalized);
155 #endif
156 auto utf8 = SkUnicode::convertUtf16ToUtf8(text);
157 fUtf8.append(utf8);
158 }
159
addText(const char * text)160 void ParagraphBuilderImpl::addText(const char* text) {
161 #if defined(SK_UNICODE_CLIENT_IMPLEMENTATION)
162 SkASSERT(!fTextIsFinalized);
163 #endif
164 fUtf8.append(text);
165 }
166
addText(const char * text,size_t len)167 void ParagraphBuilderImpl::addText(const char* text, size_t len) {
168 #if defined(SK_UNICODE_CLIENT_IMPLEMENTATION)
169 SkASSERT(!fTextIsFinalized);
170 #endif
171 fUtf8.append(text, len);
172 }
173
addPlaceholder(const PlaceholderStyle & placeholderStyle)174 void ParagraphBuilderImpl::addPlaceholder(const PlaceholderStyle& placeholderStyle) {
175 #if defined(SK_UNICODE_CLIENT_IMPLEMENTATION)
176 SkASSERT(!fTextIsFinalized);
177 #endif
178 addPlaceholder(placeholderStyle, false);
179 }
180
addPlaceholder(const PlaceholderStyle & placeholderStyle,bool lastOne)181 void ParagraphBuilderImpl::addPlaceholder(const PlaceholderStyle& placeholderStyle, bool lastOne) {
182 #if defined(SK_UNICODE_CLIENT_IMPLEMENTATION)
183 // The very last placeholder is added automatically
184 // and only AFTER finalize() is called
185 SkASSERT(!fTextIsFinalized || lastOne);
186 #endif
187 if (!fUtf8.isEmpty() && !lastOne) {
188 // We keep the very last text style
189 this->endRunIfNeeded();
190 }
191
192 BlockRange stylesBefore(fPlaceholders.empty() ? 0 : fPlaceholders.back().fBlocksBefore.end + 1,
193 fStyledBlocks.size());
194 TextRange textBefore(fPlaceholders.empty() ? 0 : fPlaceholders.back().fRange.end,
195 fUtf8.size());
196 auto start = fUtf8.size();
197 auto topStyle = internalPeekStyle();
198 if (!lastOne) {
199 pushStyle(topStyle.cloneForPlaceholder());
200 addText(std::u16string(1ull, 0xFFFC));
201 pop();
202 }
203 auto end = fUtf8.size();
204 fPlaceholders.emplace_back(start, end, placeholderStyle, topStyle, stylesBefore, textBefore);
205 }
206
endRunIfNeeded()207 void ParagraphBuilderImpl::endRunIfNeeded() {
208 if (fStyledBlocks.empty()) {
209 return;
210 }
211
212 auto& last = fStyledBlocks.back();
213 if (last.fRange.start == fUtf8.size()) {
214 fStyledBlocks.pop_back();
215 } else {
216 last.fRange.end = fUtf8.size();
217 }
218 }
219
startStyledBlock()220 void ParagraphBuilderImpl::startStyledBlock() {
221 endRunIfNeeded();
222 fStyledBlocks.emplace_back(fUtf8.size(), fUtf8.size(), internalPeekStyle());
223 }
224
finalize()225 void ParagraphBuilderImpl::finalize() {
226 #if defined(SK_UNICODE_CLIENT_IMPLEMENTATION)
227 if (fTextIsFinalized) {
228 return;
229 }
230 #endif
231 if (!fUtf8.isEmpty()) {
232 this->endRunIfNeeded();
233 }
234
235 #if defined(SK_UNICODE_CLIENT_IMPLEMENTATION)
236 fTextIsFinalized = true;
237 #endif
238 }
239
Build()240 std::unique_ptr<Paragraph> ParagraphBuilderImpl::Build() {
241 this->finalize();
242 // Add one fake placeholder with the rest of the text
243 this->addPlaceholder(PlaceholderStyle(), true);
244
245 fUTF8IndexForUTF16Index.clear();
246 fUTF16IndexForUTF8Index.clear();
247 #if !defined(SK_DISABLE_LEGACY_PARAGRAPH_UNICODE) && defined(SK_UNICODE_CLIENT_IMPLEMENTATION)
248 if (fUsingClientInfo && !fUnicode) {
249 // This is the place where SkUnicode is paired with SkParagraph
250 fUnicode = SkUnicodes::Client::Make(this->getText(),
251 std::move(fWordsUtf16),
252 std::move(fGraphemeBreaksUtf8),
253 std::move(fLineBreaksUtf8));
254 }
255 #endif
256
257 #if defined(SK_UNICODE_ICU_IMPLEMENTATION)
258 if (!fUnicode) {
259 fUnicode = get_unicode();
260 }
261 #endif
262 SkASSERT_RELEASE(fUnicode);
263 return std::make_unique<ParagraphImpl>(
264 fUtf8, fParagraphStyle, fStyledBlocks, fPlaceholders, fFontCollection, fUnicode);
265 }
266
267 #ifdef ENABLE_TEXT_ENHANCE
buildLineFetcher()268 std::unique_ptr<ParagraphLineFetcher> ParagraphBuilderImpl::buildLineFetcher() {
269 if (fUtf8.isEmpty()) {
270 return nullptr;
271 }
272 fParagraphStyle.setMaxLines(1);
273 fParagraphStyle.setTextAlign(TextAlign::kLeft);
274 return std::make_unique<ParagraphLineFetcherImpl>(Build());
275 }
276 #endif
277
getText()278 SkSpan<char> ParagraphBuilderImpl::getText() {
279 this->finalize();
280 return SkSpan<char>(fUtf8.isEmpty() ? nullptr : fUtf8.data(), fUtf8.size());
281 }
282
getParagraphStyle() const283 const ParagraphStyle& ParagraphBuilderImpl::getParagraphStyle() const {
284 return fParagraphStyle;
285 }
286
ensureUTF16Mapping()287 void ParagraphBuilderImpl::ensureUTF16Mapping() {
288 fillUTF16MappingOnce([&] {
289 SkUnicode::extractUtfConversionMapping(
290 this->getText(),
291 [&](size_t index) { fUTF8IndexForUTF16Index.emplace_back(index); },
292 [&](size_t index) { fUTF16IndexForUTF8Index.emplace_back(index); });
293 });
294 }
295
296 #if !defined(SK_DISABLE_LEGACY_CLIENT_UNICODE) && defined(SK_UNICODE_CLIENT_IMPLEMENTATION)
setWordsUtf8(std::vector<SkUnicode::Position> wordsUtf8)297 void ParagraphBuilderImpl::setWordsUtf8(std::vector<SkUnicode::Position> wordsUtf8) {
298 ensureUTF16Mapping();
299 std::vector<SkUnicode::Position> wordsUtf16;
300 for (SkUnicode::Position indexUtf8: wordsUtf8) {
301 wordsUtf16.emplace_back(fUTF16IndexForUTF8Index[indexUtf8]);
302 }
303 setWordsUtf16(wordsUtf16);
304 }
305
setWordsUtf16(std::vector<SkUnicode::Position> wordsUtf16)306 void ParagraphBuilderImpl::setWordsUtf16(std::vector<SkUnicode::Position> wordsUtf16) {
307 fUsingClientInfo = true;
308 fWordsUtf16 = std::move(wordsUtf16);
309 }
310
setGraphemeBreaksUtf8(std::vector<SkUnicode::Position> graphemeBreaksUtf8)311 void ParagraphBuilderImpl::setGraphemeBreaksUtf8(std::vector<SkUnicode::Position> graphemeBreaksUtf8) {
312 fUsingClientInfo = true;
313 fGraphemeBreaksUtf8 = std::move(graphemeBreaksUtf8);
314 }
315
setGraphemeBreaksUtf16(std::vector<SkUnicode::Position> graphemeBreaksUtf16)316 void ParagraphBuilderImpl::setGraphemeBreaksUtf16(std::vector<SkUnicode::Position> graphemeBreaksUtf16) {
317 ensureUTF16Mapping();
318 std::vector<SkUnicode::Position> graphemeBreaksUtf8;
319 for (SkUnicode::Position indexUtf16: graphemeBreaksUtf16) {
320 graphemeBreaksUtf8.emplace_back(fUTF8IndexForUTF16Index[indexUtf16]);
321 }
322 setGraphemeBreaksUtf8(graphemeBreaksUtf8);
323 }
324
setLineBreaksUtf8(std::vector<SkUnicode::LineBreakBefore> lineBreaksUtf8)325 void ParagraphBuilderImpl::setLineBreaksUtf8(std::vector<SkUnicode::LineBreakBefore> lineBreaksUtf8) {
326 fUsingClientInfo = true;
327 fLineBreaksUtf8 = std::move(lineBreaksUtf8);
328 }
329
setLineBreaksUtf16(std::vector<SkUnicode::LineBreakBefore> lineBreaksUtf16)330 void ParagraphBuilderImpl::setLineBreaksUtf16(std::vector<SkUnicode::LineBreakBefore> lineBreaksUtf16) {
331 ensureUTF16Mapping();
332 std::vector<SkUnicode::LineBreakBefore> lineBreaksUtf8;
333 for (SkUnicode::LineBreakBefore lineBreakUtf16: lineBreaksUtf16) {
334 lineBreaksUtf8.emplace_back(SkUnicode::LineBreakBefore(
335 fUTF8IndexForUTF16Index[lineBreakUtf16.pos], lineBreakUtf16.breakType));
336 }
337 setLineBreaksUtf8(lineBreaksUtf8);
338 }
339 #endif
340
Reset()341 void ParagraphBuilderImpl::Reset() {
342
343 fTextStyles.clear();
344 fUtf8.reset();
345 fStyledBlocks.clear();
346 fPlaceholders.clear();
347 fUTF8IndexForUTF16Index.clear();
348 fUTF16IndexForUTF8Index.clear();
349 #if defined(SK_UNICODE_CLIENT_IMPLEMENTATION)
350 fWordsUtf16.clear();
351 fGraphemeBreaksUtf8.clear();
352 fLineBreaksUtf8.clear();
353 fTextIsFinalized = false;
354 #endif
355 startStyledBlock();
356 }
357
RequiresClientICU()358 bool ParagraphBuilderImpl::RequiresClientICU() {
359 #if defined(SK_UNICODE_CLIENT_IMPLEMENTATION)
360 return true;
361 #else
362 return false;
363 #endif
364 }
365
366 } // namespace textlayout
367 } // namespace skia
368