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 | #include "modules/skparagraph/src/ParagraphUtil.h" |
12 | |
13 | #include <algorithm> |
14 | #include <utility> |
15 | |
16 | namespace skia { |
17 | namespace textlayout { |
18 | |
19 | std::unique_ptr<ParagraphBuilder> ParagraphBuilder::make( |
20 | const ParagraphStyle& style, sk_sp<FontCollection> fontCollection) { |
21 | return std::make_unique<ParagraphBuilderImpl>(style, fontCollection); |
22 | } |
23 | |
24 | ParagraphBuilderImpl::ParagraphBuilderImpl( |
25 | const ParagraphStyle& style, sk_sp<FontCollection> fontCollection) |
26 | : ParagraphBuilder(style, fontCollection), fUtf8(), fFontCollection(std::move(fontCollection)) { |
27 | this->setParagraphStyle(style); |
28 | } |
29 | |
30 | ParagraphBuilderImpl::~ParagraphBuilderImpl() = default; |
31 | |
32 | void ParagraphBuilderImpl::setParagraphStyle(const ParagraphStyle& style) { |
33 | fParagraphStyle = style; |
34 | fTextStyles.push(fParagraphStyle.getTextStyle()); |
35 | fStyledBlocks.emplace_back(fUtf8.size(), fUtf8.size(), fParagraphStyle.getTextStyle()); |
36 | } |
37 | |
38 | void ParagraphBuilderImpl::pushStyle(const TextStyle& style) { |
39 | this->endRunIfNeeded(); |
40 | |
41 | fTextStyles.push(style); |
42 | if (!fStyledBlocks.empty() && fStyledBlocks.back().fRange.end == fUtf8.size() && |
43 | fStyledBlocks.back().fStyle == style) { |
44 | // Just continue with the same style |
45 | } else { |
46 | // Go with the new style |
47 | fStyledBlocks.emplace_back(fUtf8.size(), fUtf8.size(), fTextStyles.top()); |
48 | } |
49 | } |
50 | |
51 | void ParagraphBuilderImpl::pop() { |
52 | this->endRunIfNeeded(); |
53 | |
54 | if (fTextStyles.size() > 1) { |
55 | fTextStyles.pop(); |
56 | } else { |
57 | // In this case we use paragraph style and skip Pop operation |
58 | SkDEBUGF("SkParagraphBuilder.Pop() called too many times.\n" ); |
59 | } |
60 | |
61 | auto top = fTextStyles.top(); |
62 | fStyledBlocks.emplace_back(fUtf8.size(), fUtf8.size(), top); |
63 | } |
64 | |
65 | TextStyle ParagraphBuilderImpl::peekStyle() { |
66 | this->endRunIfNeeded(); |
67 | |
68 | if (!fTextStyles.empty()) { |
69 | return fTextStyles.top(); |
70 | } else { |
71 | SkDebugf("SkParagraphBuilder._styles is empty.\n" ); |
72 | return fParagraphStyle.getTextStyle(); |
73 | } |
74 | } |
75 | |
76 | void ParagraphBuilderImpl::addText(const std::u16string& text) { |
77 | fUtf8.append(SkStringFromU16String(text)); |
78 | } |
79 | |
80 | void ParagraphBuilderImpl::addText(const char* text) { |
81 | fUtf8.append(text); |
82 | } |
83 | |
84 | void ParagraphBuilderImpl::addText(const char* text, size_t len) { |
85 | fUtf8.append(text, len); |
86 | } |
87 | |
88 | void ParagraphBuilderImpl::addPlaceholder(const PlaceholderStyle& placeholderStyle) { |
89 | addPlaceholder(placeholderStyle, false); |
90 | } |
91 | |
92 | void ParagraphBuilderImpl::addPlaceholder(const PlaceholderStyle& placeholderStyle, bool lastOne) { |
93 | this->endRunIfNeeded(); |
94 | |
95 | BlockRange stylesBefore(fPlaceholders.empty() ? 0 : fPlaceholders.back().fBlocksBefore.end + 1, |
96 | fStyledBlocks.size()); |
97 | TextRange textBefore(fPlaceholders.empty() ? 0 : fPlaceholders.back().fRange.end, |
98 | fUtf8.size()); |
99 | auto start = fUtf8.size(); |
100 | auto topStyle = fTextStyles.top(); |
101 | if (!lastOne) { |
102 | pushStyle(TextStyle(topStyle, true)); |
103 | addText(std::u16string(1ull, 0xFFFC)); |
104 | pop(); |
105 | } |
106 | auto end = fUtf8.size(); |
107 | fPlaceholders.emplace_back(start, end, placeholderStyle, topStyle, stylesBefore, textBefore); |
108 | } |
109 | |
110 | void ParagraphBuilderImpl::endRunIfNeeded() { |
111 | if (fStyledBlocks.empty()) { |
112 | return; |
113 | } |
114 | |
115 | auto& last = fStyledBlocks.back(); |
116 | if (last.fRange.start == fUtf8.size()) { |
117 | fStyledBlocks.pop_back(); |
118 | } else { |
119 | last.fRange.end = fUtf8.size(); |
120 | } |
121 | } |
122 | |
123 | std::unique_ptr<Paragraph> ParagraphBuilderImpl::Build() { |
124 | if (!fUtf8.isEmpty()) { |
125 | this->endRunIfNeeded(); |
126 | } |
127 | |
128 | // Add one fake placeholder with the rest of the text |
129 | addPlaceholder(PlaceholderStyle(), true); |
130 | return std::make_unique<ParagraphImpl>( |
131 | fUtf8, fParagraphStyle, fStyledBlocks, fPlaceholders, fFontCollection); |
132 | } |
133 | |
134 | } // namespace textlayout |
135 | } // namespace skia |
136 | |