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