• 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 
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