* Copyright (c) 2022 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "common/spannable_string.h"
#include "font/ui_font.h"
#include "gfx_utils/graphic_log.h"
#include "securec.h"
namespace OHOS {
namespace {
constexpr uint16_t DEFAULT_IS_SPANNABLE_LEN = 10;
constexpr uint16_t DEFAULT_EXPAND_EDGE = 1024;
constexpr uint16_t DEFAULT_EXPAND_TIMES = 2;
constexpr uint16_t DEFAULT_EXPAND_OFFSET = 1;
}
SpannableString::SpannableString() : isSpannableLen_(0), isSpannable_(nullptr) {}
SpannableString::~SpannableString()
{
Reset();
}
void SpannableString::SetTextStyle(TextStyle inputTextStyle, uint16_t startIndex, uint16_t endIndex)
{
StyleSpan* style = new StyleSpan(inputTextStyle, startIndex, endIndex);
styleList_.PushBack(style);
SetSpannable(true, startIndex, endIndex);
}
bool SpannableString::GetTextStyle(uint16_t index, TextStyle& textStyle)
{
bool hasFind = false;
ListNode<StyleSpan*>* tempSpan = styleList_.Begin();
for (; ((tempSpan != nullptr) && (tempSpan != styleList_.End())); tempSpan = tempSpan->next_) {
uint16_t tempStart = tempSpan->data_->start_;
uint16_t tempEnd = tempSpan->data_->end_;
if ((tempStart <= index) && (index < tempEnd)) {
textStyle = tempSpan->data_->textStyle_;
hasFind = true;
break;
}
}
return hasFind;
}
void SpannableString::Reset()
{
if (isSpannable_ != nullptr) {
UIFree(isSpannable_);
}
isSpannable_ = nullptr;
isSpannableLen_ = 0;
if (styleList_.Size() > 0) {
for (auto iter = styleList_.Begin(); iter != styleList_.End(); iter = iter->next_) {
delete iter->data_;
iter->data_ = nullptr;
}
styleList_.Clear();
}
if (sizeList_.Size() > 0) {
sizeList_.Clear();
}
if (fontIdList_.Size() > 0) {
fontIdList_.Clear();
}
if (heightList_.Size() > 0) {
heightList_.Clear();
}
if (backgroundColorList_.Size() > 0) {
backgroundColorList_.Clear();
}
if (foregroundColorList_.Size() > 0) {
foregroundColorList_.Clear();
}
if (lineBackgroundColorList_.Size() > 0) {
lineBackgroundColorList_.Clear();
}
}
void SpannableString::SetSpannableString(const SpannableString* input)
{
Reset();
SetSpannable(true, 0, DEFAULT_IS_SPANNABLE_LEN);
ListNode<FontSizeSpan>* node = input->sizeList_.Begin();
while (node != input->sizeList_.End()) {
SetFontSize(node->data_.fontSize, node->data_.start, node->data_.end);
node = node->next_;
}
ListNode<FontIdSpan>* node_id = input->fontIdList_.Begin();
while (node_id != input->fontIdList_.End()) {
SetFontId(node_id->data_.fontId, node_id->data_.start, node_id->data_.end);
node_id = node_id->next_;
}
ListNode<LetterHeightSpan>* node_height = input->heightList_.Begin();
while (node_height != input->heightList_.End()) {
SetFontHeight(node_height->data_.height, node_height->data_.start, node_height->data_.end);
node_height = node_height->next_;
}
ListNode<StyleSpan*>* node_span = input->styleList_.Begin();
while (node_span != input->styleList_.End()) {
SetTextStyle(node_span->data_->textStyle_, node_span->data_->start_, node_span->data_->end_);
node_span = node_span->next_;
}
ListNode<BackgroundColorSpan>* node_backColor = input->backgroundColorList_.Begin();
while (node_backColor != input->backgroundColorList_.End()) {
SetBackgroundColor(node_backColor->data_.backgroundColor,
node_backColor->data_.start,
node_backColor->data_.end);
node_backColor = node_backColor->next_;
}
ListNode<ForegroundColorSpan>* node_foreColor = input->foregroundColorList_.Begin();
while (node_foreColor != input->foregroundColorList_.End()) {
SetForegroundColor(node_foreColor->data_.fontColor, node_foreColor->data_.start, node_foreColor->data_.end);
node_foreColor = node_foreColor->next_;
}
ListNode<LineBackgroundColorSpan>* node_lineBackColor = input->lineBackgroundColorList_.Begin();
while (node_lineBackColor != input->lineBackgroundColorList_.End()) {
SetLineBackgroundColor(node_lineBackColor->data_.linebackgroundColor,
node_lineBackColor->data_.start,
node_lineBackColor->data_.end);
node_lineBackColor = node_lineBackColor->next_;
}
}
bool SpannableString::ExpandSpannableLen(uint16_t index)
{
if (isSpannableLen_ < index) {
uint16_t preLens = isSpannableLen_;
while (isSpannableLen_ < index && isSpannableLen_ != 0 && isSpannableLen_ < DEFAULT_EXPAND_EDGE) {
isSpannableLen_ = isSpannableLen_ * DEFAULT_EXPAND_TIMES + DEFAULT_EXPAND_OFFSET;
}
bool* tempIsSpannable_ = static_cast<bool*>(UIMalloc(isSpannableLen_ * sizeof(bool)));
if (tempIsSpannable_ == nullptr) {
GRAPHIC_LOGE("SpannableString::InitSpannable() isSpannable_ == nullptr");
return false;
}
if (isSpannable_ != nullptr) {
if (memcpy_s(tempIsSpannable_, isSpannableLen_, isSpannable_, isSpannableLen_) != EOK) {
UIFree(tempIsSpannable_);
tempIsSpannable_ = nullptr;
return false;
}
UIFree(isSpannable_);
isSpannable_ = nullptr;
}
for (uint16_t i = preLens; i < isSpannableLen_; i++) {
tempIsSpannable_[i] = false;
}
isSpannable_ = tempIsSpannable_;
}
return true;
}
bool SpannableString::SetSpannable(bool value, uint16_t startIndex, uint16_t endIndex)
{
if (isSpannable_ == nullptr) {
isSpannableLen_ = DEFAULT_IS_SPANNABLE_LEN;
isSpannable_ = static_cast<bool*>(UIMalloc(isSpannableLen_ * sizeof(bool)));
if (isSpannable_ == nullptr) {
GRAPHIC_LOGE("SpannableString::SetSpannable() isSpannable_ == nullptr");
return false;
}
for (uint16_t i = 0; i < isSpannableLen_; i++) {
isSpannable_[i] = false;
}
}
bool isSuccess = ExpandSpannableLen(endIndex);
if (isSuccess && (isSpannable_ != nullptr)) {
for (uint16_t i = startIndex; ((i < endIndex) && (i < isSpannableLen_)); i++) {
isSpannable_[i] = value;
}
isSuccess = true;
}
return isSuccess;
}
bool SpannableString::GetSpannable(uint16_t index)
{
bool result = false;
if ((isSpannable_ != nullptr) && (index < isSpannableLen_)) {
result = isSpannable_[index];
}
return result;
}
* this function merge samge value when add node
*/
void SpannableString::SetFontSize(uint8_t inputFontSize, uint16_t startIndex, uint16_t endIndex)
{
if (sizeList_.IsEmpty()) {
FontSizeSpan inputSpan;
inputSpan.start = startIndex;
inputSpan.end = endIndex;
inputSpan.fontSize = inputFontSize;
sizeList_.PushFront(inputSpan);
SetSpannable(true, startIndex, endIndex);
return;
} else {
ListNode<FontSizeSpan>* tempSpan = sizeList_.Begin();
for (; ((tempSpan != nullptr) && (tempSpan != sizeList_.End())); tempSpan = tempSpan->next_) {
bool needAddNode = true;
uint16_t tempStart = tempSpan->data_.start;
uint16_t tempEnd = tempSpan->data_.end;
uint8_t tempSize = tempSpan->data_.fontSize;
if (inputFontSize == tempSize) {
needAddNode = EqualInsert<FontSizeSpan>(startIndex, endIndex, tempStart, tempEnd, &tempSpan, sizeList_);
} else {
FontSizeSpan tempLeft;
tempLeft.start = tempStart;
tempLeft.end = startIndex;
tempLeft.fontSize = tempSize;
FontSizeSpan tempRight;
tempRight.start = endIndex;
tempRight.end = tempEnd;
tempRight.fontSize = tempSize;
needAddNode = UnequalInsert<FontSizeSpan>(startIndex, endIndex, tempStart, tempEnd, &tempSpan,
sizeList_, tempLeft, tempRight);
}
if (needAddNode) {
FontSizeSpan inputSpan;
inputSpan.start = startIndex;
inputSpan.end = endIndex;
inputSpan.fontSize = inputFontSize;
sizeList_.PushBack(inputSpan);
SetSpannable(true, startIndex, endIndex);
}
}
}
}
bool SpannableString::GetFontSize(uint16_t index, uint8_t& outputSize)
{
bool hasFind = false;
ListNode<FontSizeSpan>* tempSpan = sizeList_.Begin();
for (; ((tempSpan != nullptr) && (tempSpan != sizeList_.End())); tempSpan = tempSpan->next_) {
uint16_t tempStart = tempSpan->data_.start;
uint16_t tempEnd = tempSpan->data_.end;
if ((tempStart <= index) && (index < tempEnd)) {
outputSize = tempSpan->data_.fontSize;
#if defined(CONFIG_SCALE_FONT_SIZE) && (CONFIG_SCALE_FONT_SIZE == 1)
float tempSize = ceilf(outputSize * scaleRatio_);
outputSize = tempSize > 0xff ? 0xff : static_cast<uint8_t>(tempSize);
#endif
hasFind = true;
break;
}
}
return hasFind;
}
void SpannableString::SetFontId(uint16_t inputFontId, uint16_t startIndex, uint16_t endIndex)
{
if (fontIdList_.IsEmpty()) {
FontIdSpan inputSpan;
inputSpan.start = startIndex;
inputSpan.end = endIndex;
inputSpan.fontId = inputFontId;
fontIdList_.PushFront(inputSpan);
SetSpannable(true, startIndex, endIndex);
return;
}
ListNode<FontIdSpan>* tempSpan = fontIdList_.Begin();
for (; ((tempSpan != nullptr) && (tempSpan != fontIdList_.End())); tempSpan = tempSpan->next_) {
bool needAddNode = true;
uint16_t tempStart = tempSpan->data_.start;
uint16_t tempEnd = tempSpan->data_.end;
uint16_t tempId = tempSpan->data_.fontId;
if (inputFontId == tempId) {
needAddNode = EqualInsert<FontIdSpan>(startIndex, endIndex, tempStart, tempEnd, &tempSpan, fontIdList_);
} else {
FontIdSpan tempLeft;
tempLeft.start = tempStart;
tempLeft.end = startIndex;
tempLeft.fontId = tempId;
FontIdSpan tempRight;
tempRight.start = endIndex;
tempRight.end = tempEnd;
tempRight.fontId = tempId;
needAddNode = UnequalInsert<FontIdSpan>(startIndex, endIndex, tempStart, tempEnd, &tempSpan, fontIdList_,
tempLeft, tempRight);
}
if (needAddNode) {
FontIdSpan inputSpan;
inputSpan.start = startIndex;
inputSpan.end = endIndex;
inputSpan.fontId = inputFontId;
fontIdList_.PushBack(inputSpan);
SetSpannable(true, startIndex, endIndex);
}
}
}
bool SpannableString::GetFontId(uint16_t index, uint16_t& outputFontId)
{
bool hasFind = false;
ListNode<FontIdSpan>* tempSpan = fontIdList_.Begin();
for (; ((tempSpan != nullptr) && (tempSpan != fontIdList_.End())); tempSpan = tempSpan->next_) {
uint16_t tempStart = tempSpan->data_.start;
uint16_t tempEnd = tempSpan->data_.end;
if ((tempStart <= index) && (index < tempEnd)) {
outputFontId = tempSpan->data_.fontId;
hasFind = true;
break;
}
}
return hasFind;
}
void SpannableString::SetFontHeight(int16_t inputHeight, uint16_t startIndex, uint16_t endIndex)
{
if (heightList_.IsEmpty()) {
LetterHeightSpan inputSpan;
inputSpan.start = startIndex;
inputSpan.end = endIndex;
inputSpan.height = inputHeight;
heightList_.PushFront(inputSpan);
SetSpannable(true, startIndex, endIndex);
return;
}
ListNode<LetterHeightSpan>* tempSpan = heightList_.Begin();
for (; ((tempSpan != nullptr) && (tempSpan != heightList_.End())); tempSpan = tempSpan->next_) {
bool needAddNode = true;
uint16_t tempStart = tempSpan->data_.start;
uint16_t tempEnd = tempSpan->data_.end;
int16_t tempHeight = tempSpan->data_.height;
if (inputHeight == tempHeight) {
needAddNode =
EqualInsert<LetterHeightSpan>(startIndex, endIndex, tempStart, tempEnd, &tempSpan, heightList_);
} else {
LetterHeightSpan tempLeft;
tempLeft.start = tempStart;
tempLeft.end = startIndex;
tempLeft.height = tempHeight;
LetterHeightSpan tempRight;
tempRight.start = endIndex;
tempRight.end = tempEnd;
tempRight.height = tempHeight;
needAddNode = UnequalInsert<LetterHeightSpan>(startIndex, endIndex, tempStart, tempEnd, &tempSpan,
heightList_, tempLeft, tempRight);
}
if (needAddNode) {
LetterHeightSpan inputSpan;
inputSpan.start = startIndex;
inputSpan.end = endIndex;
inputSpan.height = inputHeight;
heightList_.PushBack(inputSpan);
SetSpannable(true, startIndex, endIndex);
}
}
}
bool SpannableString::GetFontHeight(uint16_t index,
int16_t& outputHeight,
uint16_t& defaultFontId,
uint8_t defaultFontSize)
{
bool hasFind = false;
ListNode<LetterHeightSpan>* tempSpan = heightList_.Begin();
for (; ((tempSpan != nullptr) && (tempSpan != heightList_.End())); tempSpan = tempSpan->next_) {
uint16_t tempStart = tempSpan->data_.start;
uint16_t tempEnd = tempSpan->data_.end;
if ((tempStart <= index) && (index < tempEnd)) {
hasFind = true;
outputHeight = tempSpan->data_.height;
break;
}
}
if (!hasFind) {
GetFontId(index, defaultFontId);
GetFontSize(index, defaultFontSize);
UIFont* uifont = UIFont::GetInstance();
outputHeight = uifont->GetHeight(defaultFontId, defaultFontSize);
SetFontHeight(outputHeight, index, index + 1);
}
return hasFind;
}
void SpannableString::SetBackgroundColor(ColorType inputBackgroundColor, uint16_t startIndex, uint16_t endIndex)
{
if (backgroundColorList_.IsEmpty()) {
BackgroundColorSpan inputSpan;
inputSpan.start = startIndex;
inputSpan.end = endIndex;
inputSpan.backgroundColor.full = inputBackgroundColor.full;
backgroundColorList_.PushFront(inputSpan);
SetSpannable(true, startIndex, endIndex);
return;
} else {
ListNode<BackgroundColorSpan>* tempSpan = backgroundColorList_.Begin();
for (; ((tempSpan != nullptr) && (tempSpan != backgroundColorList_.End())); tempSpan = tempSpan->next_) {
bool needAddNode = true;
uint16_t tempStart = tempSpan->data_.start;
uint16_t tempEnd = tempSpan->data_.end;
ColorType tempSize;
tempSize.full = tempSpan->data_.backgroundColor.full;
if (inputBackgroundColor.full == tempSize.full) {
needAddNode = EqualInsert<BackgroundColorSpan>(
startIndex, endIndex, tempStart, tempEnd, &tempSpan,
backgroundColorList_);
} else {
BackgroundColorSpan tempLeft;
tempLeft.start = tempStart;
tempLeft.end = startIndex;
tempLeft.backgroundColor.full = tempSize.full;
BackgroundColorSpan tempRight;
tempRight.start = endIndex;
tempRight.end = tempEnd;
tempRight.backgroundColor.full = tempSize.full;
needAddNode = UnequalInsert<BackgroundColorSpan>(
startIndex, endIndex, tempStart, tempEnd, &tempSpan,
backgroundColorList_, tempLeft, tempRight);
}
if (needAddNode) {
BackgroundColorSpan inputSpan;
inputSpan.start = startIndex;
inputSpan.end = endIndex;
inputSpan.backgroundColor.full = inputBackgroundColor.full;
backgroundColorList_.PushBack(inputSpan);
SetSpannable(true, startIndex, endIndex);
}
}
}
}
bool SpannableString::GetBackgroundColor(uint16_t index, ColorType& outputBackgroundColor)
{
bool hasFind = false;
ListNode<BackgroundColorSpan>* tempSpan = backgroundColorList_.Begin();
for (; ((tempSpan != nullptr) && (tempSpan != backgroundColorList_.End())); tempSpan = tempSpan->next_) {
uint16_t tempStart = tempSpan->data_.start;
uint16_t tempEnd = tempSpan->data_.end;
if ((tempStart <= index) && (index < tempEnd)) {
outputBackgroundColor.full = tempSpan->data_.backgroundColor.full;
hasFind = true;
break;
}
}
return hasFind;
}
void SpannableString::SetForegroundColor(ColorType inputForegroundColor, uint16_t startIndex, uint16_t endIndex)
{
if (foregroundColorList_.IsEmpty()) {
ForegroundColorSpan inputSpan;
inputSpan.start = startIndex;
inputSpan.end = endIndex;
inputSpan.fontColor.full = inputForegroundColor.full;
foregroundColorList_.PushFront(inputSpan);
SetSpannable(true, startIndex, endIndex);
return;
} else {
ListNode<ForegroundColorSpan>* tempSpan = foregroundColorList_.Begin();
for (; ((tempSpan != nullptr) && (tempSpan != foregroundColorList_.End())); tempSpan = tempSpan->next_) {
bool needAddNode = true;
uint16_t tempStart = tempSpan->data_.start;
uint16_t tempEnd = tempSpan->data_.end;
ColorType tempSize;
tempSize.full= tempSpan->data_.fontColor.full;
if (inputForegroundColor.full == tempSize.full) {
needAddNode = EqualInsert<ForegroundColorSpan>(
startIndex, endIndex, tempStart, tempEnd, &tempSpan,
foregroundColorList_);
} else {
ForegroundColorSpan tempLeft;
tempLeft.start = tempStart;
tempLeft.end = startIndex;
tempLeft.fontColor.full = tempSize.full;
ForegroundColorSpan tempRight;
tempRight.start = endIndex;
tempRight.end = tempEnd;
tempRight.fontColor.full = tempSize.full;
needAddNode = UnequalInsert<ForegroundColorSpan>(startIndex, endIndex, tempStart, tempEnd, &tempSpan,
foregroundColorList_, tempLeft, tempRight);
}
if (needAddNode) {
ForegroundColorSpan inputSpan;
inputSpan.start = startIndex;
inputSpan.end = endIndex;
inputSpan.fontColor.full = inputForegroundColor.full;
foregroundColorList_.PushBack(inputSpan);
SetSpannable(true, startIndex, endIndex);
}
}
}
}
bool SpannableString::GetForegroundColor(uint16_t index, ColorType& outputForegroundColor)
{
bool hasFind = false;
ListNode<ForegroundColorSpan>* tempSpan = foregroundColorList_.Begin();
for (; ((tempSpan != nullptr) && (tempSpan != foregroundColorList_.End())); tempSpan = tempSpan->next_) {
uint16_t tempStart = tempSpan->data_.start;
uint16_t tempEnd = tempSpan->data_.end;
if ((tempStart <= index) && (index < tempEnd)) {
outputForegroundColor.full = tempSpan->data_.fontColor.full;
hasFind = true;
break;
}
}
return hasFind;
}
void SpannableString::SetLineBackgroundColor(ColorType inputLineBackgroundColor, uint16_t startIndex, uint16_t endIndex)
{
if (lineBackgroundColorList_.IsEmpty()) {
LineBackgroundColorSpan inputSpan;
inputSpan.start = startIndex;
inputSpan.end = endIndex;
inputSpan.linebackgroundColor.full = inputLineBackgroundColor.full;
lineBackgroundColorList_.PushFront(inputSpan);
SetSpannable(true, startIndex, endIndex);
return;
} else {
ListNode<LineBackgroundColorSpan>* tempSpan = lineBackgroundColorList_.Begin();
for (; ((tempSpan != nullptr) && (tempSpan != lineBackgroundColorList_.End())); tempSpan = tempSpan->next_) {
bool needAddNode = true;
uint16_t tempStart = tempSpan->data_.start;
uint16_t tempEnd = tempSpan->data_.end;
ColorType tempSize;
tempSize.full = tempSpan->data_.linebackgroundColor.full;
if (inputLineBackgroundColor.full == tempSize.full) {
needAddNode = EqualInsert<LineBackgroundColorSpan>(
startIndex, endIndex, tempStart, tempEnd, &tempSpan,
lineBackgroundColorList_);
} else {
LineBackgroundColorSpan tempLeft;
tempLeft.start = tempStart;
tempLeft.end = startIndex;
tempLeft.linebackgroundColor.full = tempSize.full;
LineBackgroundColorSpan tempRight;
tempRight.start = endIndex;
tempRight.end = tempEnd;
tempRight.linebackgroundColor.full = tempSize.full;
needAddNode = UnequalInsert<LineBackgroundColorSpan>(
startIndex, endIndex, tempStart, tempEnd, &tempSpan,
lineBackgroundColorList_, tempLeft, tempRight);
}
if (needAddNode) {
LineBackgroundColorSpan inputSpan;
inputSpan.start = startIndex;
inputSpan.end = endIndex;
inputSpan.linebackgroundColor.full = inputLineBackgroundColor.full;
lineBackgroundColorList_.PushBack(inputSpan);
SetSpannable(true, startIndex, endIndex);
}
}
}
}
bool SpannableString::GetLineBackgroundColor(uint16_t index, ColorType& outputLineBackgroundColor)
{
bool hasFind = false;
ListNode<LineBackgroundColorSpan>* tempSpan = lineBackgroundColorList_.Begin();
for (; ((tempSpan != nullptr) && (tempSpan != lineBackgroundColorList_.End())); tempSpan = tempSpan->next_) {
uint16_t tempStart = tempSpan->data_.start;
uint16_t tempEnd = tempSpan->data_.end;
if ((tempStart <= index) && (index < tempEnd)) {
outputLineBackgroundColor.full = tempSpan->data_.linebackgroundColor.full;
hasFind = true;
break;
}
}
return hasFind;
}
#if defined(CONFIG_SCALE_FONT_SIZE) && (CONFIG_SCALE_FONT_SIZE == 1)
void SpannableString::SetFontSizeScale(float ratio)
{
if (fabs(ratio - scaleRatio_) < 1e-6f) {
return;
}
scaleRatio_ = ratio;
}
#endif
}