1 /*
2 * Copyright (c) 2022-2023 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "core/components_ng/pattern/text/span_node.h"
17
18 #include <optional>
19
20 #include "base/utils/utils.h"
21 #include "core/components/common/layout/constants.h"
22 #include "core/components/common/properties/text_style.h"
23 #include "core/components_ng/base/frame_node.h"
24 #include "core/components_ng/pattern/text/text_pattern.h"
25 #include "core/components_ng/pattern/text/text_styles.h"
26 #include "core/components_ng/property/property.h"
27 #include "core/components_ng/render/drawing_prop_convertor.h"
28 #include "core/components_ng/render/paragraph.h"
29 #include "core/pipeline/pipeline_context.h"
30 #include "core/common/font_manager.h"
31
32 namespace OHOS::Ace::NG {
33 namespace {
GetDeclaration(const std::optional<Color> & color,const std::optional<TextDecoration> & textDecoration)34 std::string GetDeclaration(const std::optional<Color>& color, const std::optional<TextDecoration>& textDecoration)
35 {
36 auto jsonSpanDeclaration = JsonUtil::Create(true);
37 jsonSpanDeclaration->Put(
38 "type", V2::ConvertWrapTextDecorationToStirng(textDecoration.value_or(TextDecoration::NONE)).c_str());
39 jsonSpanDeclaration->Put("color", (color.value_or(Color::BLACK).ColorToString()).c_str());
40 return jsonSpanDeclaration->ToString();
41 }
42 } // namespace
43
GetFont() const44 std::string SpanItem::GetFont() const
45 {
46 auto jsonValue = JsonUtil::Create(true);
47 jsonValue->Put("style", GetFontStyleInJson(fontStyle->GetItalicFontStyle()).c_str());
48 jsonValue->Put("size", GetFontSizeInJson(fontStyle->GetFontSize()).c_str());
49 jsonValue->Put("weight", GetFontWeightInJson(fontStyle->GetFontWeight()).c_str());
50 jsonValue->Put("family", GetFontFamilyInJson(fontStyle->GetFontFamily()).c_str());
51 return jsonValue->ToString();
52 }
53
ToJsonValue(std::unique_ptr<JsonValue> & json) const54 void SpanItem::ToJsonValue(std::unique_ptr<JsonValue>& json) const
55 {
56 json->Put("content", content.c_str());
57 if (fontStyle) {
58 json->Put("font", GetFont().c_str());
59 json->Put("fontSize", GetFontSizeInJson(fontStyle->GetFontSize()).c_str());
60 json->Put(
61 "decoration", GetDeclaration(fontStyle->GetTextDecorationColor(), fontStyle->GetTextDecoration()).c_str());
62 json->Put("letterSpacing", fontStyle->GetLetterSpacing().value_or(Dimension()).ToString().c_str());
63 json->Put(
64 "textCase", V2::ConvertWrapTextCaseToStirng(fontStyle->GetTextCase().value_or(TextCase::NORMAL)).c_str());
65 json->Put("fontColor", fontStyle->GetForegroundColor()
66 .value_or(fontStyle->GetTextColor().value_or(Color::BLACK)).ColorToString().c_str());
67 json->Put("fontStyle", GetFontStyleInJson(fontStyle->GetItalicFontStyle()).c_str());
68 json->Put("fontWeight", GetFontWeightInJson(fontStyle->GetFontWeight()).c_str());
69 json->Put("fontFamily", GetFontFamilyInJson(fontStyle->GetFontFamily()).c_str());
70 }
71 if (textLineStyle) {
72 json->Put("lineHeight", textLineStyle->GetLineHeight().value_or(Dimension()).ToString().c_str());
73 }
74 }
75
GetOrCreateSpanNode(int32_t nodeId)76 RefPtr<SpanNode> SpanNode::GetOrCreateSpanNode(int32_t nodeId)
77 {
78 auto spanNode = ElementRegister::GetInstance()->GetSpecificItemById<SpanNode>(nodeId);
79 if (spanNode) {
80 return spanNode;
81 }
82 spanNode = MakeRefPtr<SpanNode>(nodeId);
83 ElementRegister::GetInstance()->AddUINode(spanNode);
84 return spanNode;
85 }
86
MountToParagraph()87 void SpanNode::MountToParagraph()
88 {
89 auto parent = GetParent();
90 while (parent) {
91 auto spanNode = DynamicCast<SpanNode>(parent);
92 if (spanNode) {
93 spanNode->AddChildSpanItem(Claim(this));
94 return;
95 }
96 auto textNode = DynamicCast<FrameNode>(parent);
97 if (textNode) {
98 auto textPattern = textNode->GetPattern<TextPattern>();
99 if (textPattern) {
100 textPattern->AddChildSpanItem(Claim(this));
101 return;
102 }
103 }
104 parent = parent->GetParent();
105 }
106 LOGE("fail to find Text or Parent Span");
107 }
108
RequestTextFlushDirty()109 void SpanNode::RequestTextFlushDirty()
110 {
111 auto parent = GetParent();
112 while (parent) {
113 auto textNode = DynamicCast<FrameNode>(parent);
114 if (textNode) {
115 textNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
116 auto textPattern = textNode->GetPattern<TextPattern>();
117 if (textPattern) {
118 textPattern->OnModifyDone();
119 return;
120 }
121 }
122 parent = parent->GetParent();
123 }
124 LOGD("fail to find Text or Parent Span");
125 }
126
UpdateParagraph(const RefPtr<FrameNode> & frameNode,const RefPtr<Paragraph> & builder,double,double,VerticalAlign)127 int32_t SpanItem::UpdateParagraph(const RefPtr<FrameNode>& frameNode,
128 const RefPtr<Paragraph>& builder, double /* width */, double /* height */, VerticalAlign /* verticalAlign */)
129 {
130 CHECK_NULL_RETURN(builder, -1);
131 std::optional<TextStyle> textStyle;
132 if (fontStyle || textLineStyle) {
133 auto pipelineContext = PipelineContext::GetCurrentContext();
134 CHECK_NULL_RETURN(pipelineContext, -1);
135 TextStyle themeTextStyle =
136 CreateTextStyleUsingTheme(fontStyle, textLineStyle, pipelineContext->GetTheme<TextTheme>());
137 if (frameNode) {
138 FontRegisterCallback(frameNode, themeTextStyle);
139 }
140 if (NearZero(themeTextStyle.GetFontSize().Value())) {
141 return -1;
142 }
143 textStyle = themeTextStyle;
144 builder->PushStyle(themeTextStyle);
145 }
146 UpdateTextStyle(builder, textStyle);
147 textStyle_ = textStyle;
148 for (const auto& child : children) {
149 if (child) {
150 child->UpdateParagraph(frameNode, builder);
151 }
152 }
153 if (fontStyle || textLineStyle) {
154 builder->PopStyle();
155 }
156 return -1;
157 }
158
FontRegisterCallback(const RefPtr<FrameNode> & frameNode,const TextStyle & textStyle)159 void SpanItem::FontRegisterCallback(const RefPtr<FrameNode>& frameNode, const TextStyle& textStyle)
160 {
161 auto callback = [weakNode = WeakPtr<FrameNode>(frameNode)] {
162 auto frameNode = weakNode.Upgrade();
163 CHECK_NULL_VOID(frameNode);
164 frameNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
165 auto pattern = frameNode->GetPattern<TextPattern>();
166 CHECK_NULL_VOID(pattern);
167 auto modifier = DynamicCast<TextContentModifier>(pattern->GetContentModifier());
168 CHECK_NULL_VOID(modifier);
169 modifier->SetFontReady(true);
170 };
171 auto pipeline = frameNode->GetContext();
172 CHECK_NULL_VOID(pipeline);
173 auto fontManager = pipeline->GetFontManager();
174 if (fontManager) {
175 bool isCustomFont = false;
176 for (const auto& familyName : textStyle.GetFontFamilies()) {
177 bool customFont = fontManager->RegisterCallbackNG(frameNode, familyName, callback);
178 if (customFont) {
179 isCustomFont = true;
180 }
181 }
182 if (isCustomFont) {
183 auto pattern = frameNode->GetPattern<TextPattern>();
184 CHECK_NULL_VOID(pattern);
185 pattern->SetIsCustomFont(true);
186 auto modifier = DynamicCast<TextContentModifier>(pattern->GetContentModifier());
187 CHECK_NULL_VOID(modifier);
188 modifier->SetIsCustomFont(true);
189 }
190 }
191 }
192
UpdateTextStyle(const RefPtr<Paragraph> & builder,const std::optional<TextStyle> & textStyle)193 void SpanItem::UpdateTextStyle(const RefPtr<Paragraph>& builder, const std::optional<TextStyle>& textStyle)
194 {
195 auto textCase = fontStyle ? fontStyle->GetTextCase().value_or(TextCase::NORMAL) : TextCase::NORMAL;
196 auto updateTextAction = [builder, textCase](const std::string& content, const std::optional<TextStyle>& textStyle) {
197 if (content.empty()) {
198 return;
199 }
200 auto displayText = content;
201 StringUtils::TransformStrCase(displayText, static_cast<int32_t>(textCase));
202 if (textStyle.has_value()) {
203 builder->PushStyle(textStyle.value());
204 }
205 builder->AddText(StringUtils::Str8ToStr16(displayText));
206 if (textStyle.has_value()) {
207 builder->PopStyle();
208 }
209 };
210 #ifdef ENABLE_DRAG_FRAMEWORK
211 if (!IsDragging()) {
212 #endif // ENABLE_DRAG_FRAMEWORK
213 updateTextAction(content, textStyle);
214 #ifdef ENABLE_DRAG_FRAMEWORK
215 } else {
216 if (content.empty()) {
217 return;
218 }
219 auto displayContent = StringUtils::Str8ToStr16(content);
220 auto contentLength = static_cast<int32_t>(displayContent.length());
221 auto beforeSelectedText = displayContent.substr(0, selectedStart);
222 updateTextAction(StringUtils::Str16ToStr8(beforeSelectedText), textStyle);
223
224 if (selectedStart < contentLength) {
225 auto pipelineContext = PipelineContext::GetCurrentContext();
226 TextStyle normalStyle =
227 !pipelineContext ? TextStyle()
228 : CreateTextStyleUsingTheme(nullptr, nullptr, pipelineContext->GetTheme<TextTheme>());
229 TextStyle selectedTextStyle = textStyle.value_or(normalStyle);
230 Color color = selectedTextStyle.GetTextColor().ChangeAlpha(DRAGGED_TEXT_OPACITY);
231 selectedTextStyle.SetTextColor(color);
232 auto selectedText = displayContent.substr(selectedStart, selectedEnd - selectedStart);
233 updateTextAction(StringUtils::Str16ToStr8(selectedText), selectedTextStyle);
234 }
235
236 if (selectedEnd < contentLength) {
237 auto afterSelectedText = displayContent.substr(selectedEnd);
238 updateTextAction(StringUtils::Str16ToStr8(afterSelectedText), textStyle);
239 }
240 }
241 #endif // ENABLE_DRAG_FRAMEWORK
242 }
243
244 #ifdef ENABLE_DRAG_FRAMEWORK
StartDrag(int32_t start,int32_t end)245 void SpanItem::StartDrag(int32_t start, int32_t end)
246 {
247 selectedStart = std::max(0, start);
248 int contentLen = content.size();
249 selectedEnd = std::min(contentLen, end);
250 }
251
EndDrag()252 void SpanItem::EndDrag()
253 {
254 selectedStart = -1;
255 selectedEnd = -1;
256 }
257
IsDragging()258 bool SpanItem::IsDragging()
259 {
260 return selectedStart >= 0 && selectedEnd >= 0;
261 }
262 #endif // ENABLE_DRAG_FRAMEWORK
263
UpdateParagraph(const RefPtr<FrameNode> &,const RefPtr<Paragraph> & builder,double width,double height,VerticalAlign verticalAlign)264 int32_t ImageSpanItem::UpdateParagraph(const RefPtr<FrameNode>& /* frameNode */,
265 const RefPtr<Paragraph>& builder, double width, double height, VerticalAlign verticalAlign)
266 {
267 LOGD("ImageSpanItem::UpdateParagraph imageWidth = %{public}f, imageHeight = %{public}f verticalAlign = "
268 "%{public}d",
269 width, height, verticalAlign);
270 CHECK_NULL_RETURN(builder, -1);
271 PlaceholderRun run;
272 run.width = width;
273 run.height = height;
274 switch (verticalAlign) {
275 case VerticalAlign::TOP:
276 run.alignment = PlaceholderAlignment::TOP;
277 break;
278 case VerticalAlign::CENTER:
279 run.alignment = PlaceholderAlignment::MIDDLE;
280 break;
281 case VerticalAlign::BOTTOM:
282 case VerticalAlign::NONE:
283 run.alignment = PlaceholderAlignment::BOTTOM;
284 break;
285 case VerticalAlign::BASELINE:
286 run.alignment = PlaceholderAlignment::ABOVEBASELINE;
287 break;
288 default:
289 run.alignment = PlaceholderAlignment::BOTTOM;
290 }
291 // ImageSpan should ignore decoration styles
292 textStyle.SetTextDecoration(TextDecoration::NONE);
293 builder->PushStyle(textStyle);
294 LOGD("ImageSpan fontsize = %{public}f", textStyle.GetFontSize().Value());
295 int32_t index = builder->AddPlaceholder(run);
296 builder->PopStyle();
297 return index;
298 }
299 } // namespace OHOS::Ace::NG
300