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