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
16namespace skia {
17namespace textlayout {
18
19std::unique_ptr<ParagraphBuilder> ParagraphBuilder::make(
20 const ParagraphStyle& style, sk_sp<FontCollection> fontCollection) {
21 return std::make_unique<ParagraphBuilderImpl>(style, fontCollection);
22}
23
24ParagraphBuilderImpl::ParagraphBuilderImpl(
25 const ParagraphStyle& style, sk_sp<FontCollection> fontCollection)
26 : ParagraphBuilder(style, fontCollection), fUtf8(), fFontCollection(std::move(fontCollection)) {
27 this->setParagraphStyle(style);
28}
29
30ParagraphBuilderImpl::~ParagraphBuilderImpl() = default;
31
32void 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
38void 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
51void 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
65TextStyle 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
76void ParagraphBuilderImpl::addText(const std::u16string& text) {
77 fUtf8.append(SkStringFromU16String(text));
78}
79
80void ParagraphBuilderImpl::addText(const char* text) {
81 fUtf8.append(text);
82}
83
84void ParagraphBuilderImpl::addText(const char* text, size_t len) {
85 fUtf8.append(text, len);
86}
87
88void ParagraphBuilderImpl::addPlaceholder(const PlaceholderStyle& placeholderStyle) {
89 addPlaceholder(placeholderStyle, false);
90}
91
92void 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
110void 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
123std::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