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