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