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