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_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_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_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_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_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_CLIENT_IMPLEMENTATION)
124 // The very last placeholder is added automatically
125 // and only AFTER finalize() is called
126 SkASSERT(!fTextIsFinalized && !lastOne);
127 #endif
128 if (!fUtf8.isEmpty() && !lastOne) {
129 // We keep the very last text style
130 this->endRunIfNeeded();
131 }
132
133 BlockRange stylesBefore(fPlaceholders.empty() ? 0 : fPlaceholders.back().fBlocksBefore.end + 1,
134 fStyledBlocks.size());
135 TextRange textBefore(fPlaceholders.empty() ? 0 : fPlaceholders.back().fRange.end,
136 fUtf8.size());
137 auto start = fUtf8.size();
138 auto topStyle = internalPeekStyle();
139 if (!lastOne) {
140 pushStyle(topStyle.cloneForPlaceholder());
141 addText(std::u16string(1ull, 0xFFFC));
142 pop();
143 }
144 auto end = fUtf8.size();
145 fPlaceholders.emplace_back(start, end, placeholderStyle, topStyle, stylesBefore, textBefore);
146 }
147
endRunIfNeeded()148 void ParagraphBuilderImpl::endRunIfNeeded() {
149 if (fStyledBlocks.empty()) {
150 return;
151 }
152
153 auto& last = fStyledBlocks.back();
154 if (last.fRange.start == fUtf8.size()) {
155 fStyledBlocks.pop_back();
156 } else {
157 last.fRange.end = fUtf8.size();
158 }
159 }
160
startStyledBlock()161 void ParagraphBuilderImpl::startStyledBlock() {
162 endRunIfNeeded();
163 fStyledBlocks.emplace_back(fUtf8.size(), fUtf8.size(), internalPeekStyle());
164 }
165
finalize()166 void ParagraphBuilderImpl::finalize() {
167 #if defined(SK_UNICODE_CLIENT_IMPLEMENTATION)
168 if (fTextIsFinalized) {
169 return;
170 }
171 #endif
172 if (!fUtf8.isEmpty()) {
173 this->endRunIfNeeded();
174 }
175
176 #if defined(SK_UNICODE_CLIENT_IMPLEMENTATION)
177 fTextIsFinalized = true;
178 #endif
179 }
180
Build()181 std::unique_ptr<Paragraph> ParagraphBuilderImpl::Build() {
182 this->finalize();
183 // Add one fake placeholder with the rest of the text
184 this->addPlaceholder(PlaceholderStyle(), true);
185
186 #if defined(SK_UNICODE_CLIENT_IMPLEMENTATION)
187 SkASSERT(fUsingClientInfo);
188 fUTF8IndexForUTF16Index.clear();
189 fUTF16IndexForUTF8Index.clear();
190 // This is the place where SkUnicode is paired with SkParagraph
191 fUnicode = SkUnicode::MakeClientBasedUnicode(this->getText(),
192 std::move(fWordsUtf16),
193 std::move(fGraphemeBreaksUtf8),
194 std::move(fLineBreaksUtf8));
195 #endif
196
197 SkASSERT(fUnicode);
198 return std::make_unique<ParagraphImpl>(
199 fUtf8, fParagraphStyle, fStyledBlocks, fPlaceholders, fFontCollection, fUnicode);
200 }
201
getText()202 SkSpan<char> ParagraphBuilderImpl::getText() {
203 this->finalize();
204 return SkSpan<char>(fUtf8.isEmpty() ? nullptr : const_cast<char*>(fUtf8.c_str()), fUtf8.size());
205 }
206
getParagraphStyle() const207 const ParagraphStyle& ParagraphBuilderImpl::getParagraphStyle() const {
208 return fParagraphStyle;
209 }
210
ensureUTF16Mapping()211 void ParagraphBuilderImpl::ensureUTF16Mapping() {
212 fillUTF16MappingOnce([&] {
213 SkUnicode::extractUtfConversionMapping(
214 this->getText(),
215 [&](size_t index) { fUTF8IndexForUTF16Index.emplace_back(index); },
216 [&](size_t index) { fUTF16IndexForUTF8Index.emplace_back(index); });
217 });
218 }
219
220 #if defined(SK_UNICODE_CLIENT_IMPLEMENTATION)
setWordsUtf8(std::vector<SkUnicode::Position> wordsUtf8)221 void ParagraphBuilderImpl::setWordsUtf8(std::vector<SkUnicode::Position> wordsUtf8) {
222 ensureUTF16Mapping();
223 std::vector<SkUnicode::Position> wordsUtf16;
224 for (SkUnicode::Position indexUtf8: wordsUtf8) {
225 wordsUtf16.emplace_back(fUTF16IndexForUTF8Index[indexUtf8]);
226 }
227 setWordsUtf16(wordsUtf16);
228 }
229
setWordsUtf16(std::vector<SkUnicode::Position> wordsUtf16)230 void ParagraphBuilderImpl::setWordsUtf16(std::vector<SkUnicode::Position> wordsUtf16) {
231 fUsingClientInfo = true;
232 fWordsUtf16 = std::move(wordsUtf16);
233 }
234
setGraphemeBreaksUtf8(std::vector<SkUnicode::Position> graphemeBreaksUtf8)235 void ParagraphBuilderImpl::setGraphemeBreaksUtf8(std::vector<SkUnicode::Position> graphemeBreaksUtf8) {
236 fUsingClientInfo = true;
237 fGraphemeBreaksUtf8 = std::move(graphemeBreaksUtf8);
238 }
239
setGraphemeBreaksUtf16(std::vector<SkUnicode::Position> graphemeBreaksUtf16)240 void ParagraphBuilderImpl::setGraphemeBreaksUtf16(std::vector<SkUnicode::Position> graphemeBreaksUtf16) {
241 ensureUTF16Mapping();
242 std::vector<SkUnicode::Position> graphemeBreaksUtf8;
243 for (SkUnicode::Position indexUtf16: graphemeBreaksUtf16) {
244 graphemeBreaksUtf8.emplace_back(fUTF8IndexForUTF16Index[indexUtf16]);
245 }
246 setGraphemeBreaksUtf8(graphemeBreaksUtf8);
247 }
248
setLineBreaksUtf8(std::vector<SkUnicode::LineBreakBefore> lineBreaksUtf8)249 void ParagraphBuilderImpl::setLineBreaksUtf8(std::vector<SkUnicode::LineBreakBefore> lineBreaksUtf8) {
250 fUsingClientInfo = true;
251 fLineBreaksUtf8 = std::move(lineBreaksUtf8);
252 }
253
setLineBreaksUtf16(std::vector<SkUnicode::LineBreakBefore> lineBreaksUtf16)254 void ParagraphBuilderImpl::setLineBreaksUtf16(std::vector<SkUnicode::LineBreakBefore> lineBreaksUtf16) {
255 ensureUTF16Mapping();
256 std::vector<SkUnicode::LineBreakBefore> lineBreaksUtf8;
257 for (SkUnicode::LineBreakBefore lineBreakUtf16: lineBreaksUtf16) {
258 lineBreaksUtf8.emplace_back(SkUnicode::LineBreakBefore(
259 fUTF8IndexForUTF16Index[lineBreakUtf16.pos], lineBreakUtf16.breakType));
260 }
261 setLineBreaksUtf8(lineBreaksUtf8);
262 }
263 #else
setWordsUtf8(std::vector<SkUnicode::Position> wordsUtf8)264 void ParagraphBuilderImpl::setWordsUtf8(std::vector<SkUnicode::Position> wordsUtf8) {
265 SkASSERT(false);
266 }
267
setWordsUtf16(std::vector<SkUnicode::Position> wordsUtf16)268 void ParagraphBuilderImpl::setWordsUtf16(std::vector<SkUnicode::Position> wordsUtf16) {
269 SkASSERT(false);
270 }
271
setGraphemeBreaksUtf8(std::vector<SkUnicode::Position> graphemesUtf8)272 void ParagraphBuilderImpl::setGraphemeBreaksUtf8(std::vector<SkUnicode::Position> graphemesUtf8) {
273 SkASSERT(false);
274 }
275
setGraphemeBreaksUtf16(std::vector<SkUnicode::Position> graphemesUtf16)276 void ParagraphBuilderImpl::setGraphemeBreaksUtf16(std::vector<SkUnicode::Position> graphemesUtf16) {
277 SkASSERT(false);
278 }
279
setLineBreaksUtf8(std::vector<SkUnicode::LineBreakBefore> lineBreaksUtf8)280 void ParagraphBuilderImpl::setLineBreaksUtf8(std::vector<SkUnicode::LineBreakBefore> lineBreaksUtf8) {
281 SkASSERT(false);
282 }
283
setLineBreaksUtf16(std::vector<SkUnicode::LineBreakBefore> lineBreaksUtf16)284 void ParagraphBuilderImpl::setLineBreaksUtf16(std::vector<SkUnicode::LineBreakBefore> lineBreaksUtf16) {
285 SkASSERT(false);
286 }
287 #endif
288
Reset()289 void ParagraphBuilderImpl::Reset() {
290
291 fTextStyles.reset();
292 fUtf8.reset();
293 fStyledBlocks.reset();
294 fPlaceholders.reset();
295 #if defined(SK_UNICODE_CLIENT_IMPLEMENTATION)
296 fUTF8IndexForUTF16Index.clear();
297 fUTF16IndexForUTF8Index.clear();
298 fWordsUtf16.clear();
299 fGraphemeBreaksUtf8.clear();
300 fLineBreaksUtf8.clear();
301 fTextIsFinalized = false;
302 #endif
303 startStyledBlock();
304 }
305
RequiresClientICU()306 bool ParagraphBuilderImpl::RequiresClientICU() {
307 #if defined(SK_UNICODE_CLIENT_IMPLEMENTATION)
308 return true;
309 #else
310 return false;
311 #endif
312 }
313
314 } // namespace textlayout
315 } // namespace skia
316