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 auto unicode = SkUnicode::Make();
27 if (nullptr == unicode) {
28 return nullptr;
29 }
30 return std::make_unique<ParagraphBuilderImpl>(style, fontCollection);
31 }
32
make(const ParagraphStyle & style,sk_sp<FontCollection> fontCollection,std::unique_ptr<SkUnicode> unicode)33 std::unique_ptr<ParagraphBuilder> ParagraphBuilderImpl::make(
34 const ParagraphStyle& style, sk_sp<FontCollection> fontCollection, std::unique_ptr<SkUnicode> unicode) {
35 if (nullptr == unicode) {
36 return nullptr;
37 }
38 return std::make_unique<ParagraphBuilderImpl>(style, fontCollection, std::move(unicode));
39 }
40
ParagraphBuilderImpl(const ParagraphStyle & style,sk_sp<FontCollection> fontCollection,std::unique_ptr<SkUnicode> unicode)41 ParagraphBuilderImpl::ParagraphBuilderImpl(
42 const ParagraphStyle& style, sk_sp<FontCollection> fontCollection, std::unique_ptr<SkUnicode> unicode)
43 : ParagraphBuilder(style, fontCollection)
44 , fUtf8()
45 , fFontCollection(std::move(fontCollection))
46 , fUnicode(std::move(unicode)) {
47 SkASSERT(fUnicode);
48 this->setParagraphStyle(style);
49 }
50
ParagraphBuilderImpl(const ParagraphStyle & style,sk_sp<FontCollection> fontCollection)51 ParagraphBuilderImpl::ParagraphBuilderImpl(
52 const ParagraphStyle& style, sk_sp<FontCollection> fontCollection)
53 : ParagraphBuilderImpl(style, fontCollection, SkUnicode::Make())
54 { }
55
56 ParagraphBuilderImpl::~ParagraphBuilderImpl() = default;
57
setParagraphStyle(const ParagraphStyle & style)58 void ParagraphBuilderImpl::setParagraphStyle(const ParagraphStyle& style) {
59 fParagraphStyle = style;
60 fTextStyles.push(fParagraphStyle.getTextStyle());
61 fStyledBlocks.emplace_back(fUtf8.size(), fUtf8.size(), fParagraphStyle.getTextStyle());
62 }
63
pushStyle(const TextStyle & style)64 void ParagraphBuilderImpl::pushStyle(const TextStyle& style) {
65 this->endRunIfNeeded();
66
67 fTextStyles.push(style);
68 if (!fStyledBlocks.empty() && fStyledBlocks.back().fRange.end == fUtf8.size() &&
69 fStyledBlocks.back().fStyle == style) {
70 // Just continue with the same style
71 } else {
72 // Go with the new style
73 fStyledBlocks.emplace_back(fUtf8.size(), fUtf8.size(), fTextStyles.top());
74 }
75 }
76
pop()77 void ParagraphBuilderImpl::pop() {
78 this->endRunIfNeeded();
79
80 if (fTextStyles.size() > 1) {
81 fTextStyles.pop();
82 } else {
83 // In this case we use paragraph style and skip Pop operation
84 SkDEBUGF("SkParagraphBuilder.Pop() called too many times.\n");
85 }
86
87 auto top = fTextStyles.top();
88 fStyledBlocks.emplace_back(fUtf8.size(), fUtf8.size(), top);
89 }
90
peekStyle()91 TextStyle ParagraphBuilderImpl::peekStyle() {
92 this->endRunIfNeeded();
93
94 if (!fTextStyles.empty()) {
95 return fTextStyles.top();
96 } else {
97 SkDebugf("SkParagraphBuilder._styles is empty.\n");
98 return fParagraphStyle.getTextStyle();
99 }
100 }
101
addText(const std::u16string & text)102 void ParagraphBuilderImpl::addText(const std::u16string& text) {
103 auto utf8 = fUnicode->convertUtf16ToUtf8(text);
104 fUtf8.append(utf8);
105 }
106
addText(const char * text)107 void ParagraphBuilderImpl::addText(const char* text) {
108 fUtf8.append(text);
109 }
110
addText(const char * text,size_t len)111 void ParagraphBuilderImpl::addText(const char* text, size_t len) {
112 fUtf8.append(text, len);
113 }
114
addPlaceholder(const PlaceholderStyle & placeholderStyle)115 void ParagraphBuilderImpl::addPlaceholder(const PlaceholderStyle& placeholderStyle) {
116 addPlaceholder(placeholderStyle, false);
117 }
118
addPlaceholder(const PlaceholderStyle & placeholderStyle,bool lastOne)119 void ParagraphBuilderImpl::addPlaceholder(const PlaceholderStyle& placeholderStyle, bool lastOne) {
120 if (!fUtf8.isEmpty() && !lastOne) {
121 // We keep the very last text style
122 this->endRunIfNeeded();
123 }
124
125 BlockRange stylesBefore(fPlaceholders.empty() ? 0 : fPlaceholders.back().fBlocksBefore.end + 1,
126 fStyledBlocks.size());
127 TextRange textBefore(fPlaceholders.empty() ? 0 : fPlaceholders.back().fRange.end,
128 fUtf8.size());
129 auto start = fUtf8.size();
130 auto topStyle = fTextStyles.top();
131 if (!lastOne) {
132 pushStyle(TextStyle(topStyle, true));
133 addText(std::u16string(1ull, 0xFFFC));
134 pop();
135 }
136 auto end = fUtf8.size();
137 fPlaceholders.emplace_back(start, end, placeholderStyle, topStyle, stylesBefore, textBefore);
138 }
139
endRunIfNeeded()140 void ParagraphBuilderImpl::endRunIfNeeded() {
141 if (fStyledBlocks.empty()) {
142 return;
143 }
144
145 auto& last = fStyledBlocks.back();
146 if (last.fRange.start == fUtf8.size()) {
147 fStyledBlocks.pop_back();
148 } else {
149 last.fRange.end = fUtf8.size();
150 }
151 }
152
Build()153 std::unique_ptr<Paragraph> ParagraphBuilderImpl::Build() {
154 if (!fUtf8.isEmpty()) {
155 this->endRunIfNeeded();
156 }
157
158 // Add one fake placeholder with the rest of the text
159 addPlaceholder(PlaceholderStyle(), true);
160 return std::make_unique<ParagraphImpl>(
161 fUtf8, fParagraphStyle, fStyledBlocks, fPlaceholders, fFontCollection, std::move(fUnicode));
162 }
163
164 } // namespace textlayout
165 } // namespace skia
166