• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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