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