1 /*
2 * Copyright (c) 2020-2022 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 #if ENABLE_ICU
17 #include "common/typed_text.h"
18 #include "draw/draw_utils.h"
19 #include "font/ui_font.h"
20 #include "font/ui_line_break.h"
21 #include "font/icu_umutex_stub.h"
22 #include "rbbidata.h"
23 #include "ucmndata.h"
24 #include "unicode/ucptrie.h"
25
26 using namespace U_ICU_NAMESPACE;
27 namespace OHOS {
MemAlloc(const void* context, size_t size)28 static void* MemAlloc(const void* context, size_t size)
29 {
30 return UIMalloc(size);
31 }
32
MemFree(const void* context, void* mem)33 static void MemFree(const void* context, void* mem)
34 {
35 if (mem == nullptr) {
36 return;
37 }
38 UIFree(mem);
39 }
40
MemRealloc(const void* context, void* mem, size_t size)41 static void* MemRealloc(const void* context, void* mem, size_t size)
42 {
43 return UIRealloc(mem, size);
44 }
45
GetInstance()46 UILineBreakEngine& UILineBreakEngine::GetInstance()
47 {
48 static UILineBreakEngine instance;
49 return instance;
50 }
51
GetNextBreakPos(UILineBreakProxy& record)52 uint16_t UILineBreakEngine::GetNextBreakPos(UILineBreakProxy& record)
53 {
54 const uint32_t* str = record.GetStr();
55 if ((str == nullptr) || !initSuccess_ || (lineBreakTrie_ == nullptr)) {
56 return 0;
57 }
58 int32_t state = LINE_BREAK_STATE_START;
59 const RBBIStateTable* rbbStateTable = reinterpret_cast<const RBBIStateTable*>(stateTbl_);
60 const RBBIStateTableRow8* row =
61 reinterpret_cast<const RBBIStateTableRow8*>(rbbStateTable->fTableData + rbbStateTable->fRowLen * state);
62 UCPTrie* trie = reinterpret_cast<UCPTrie*>(lineBreakTrie_);
63 for (uint16_t index = 0; index < record.GetStrLen(); ++index) {
64 uint16_t category = UCPTRIE_FAST_GET(trie, UCPTRIE_8, static_cast<int32_t>(str[index]));
65 // 0x4000: remove the dictionary flag bit
66 if ((category & 0x4000) != 0) {
67 // 0x4000: remove the dictionary flag bit
68 category &= ~0x4000;
69 }
70 state = row->fNextState[category];
71 row = reinterpret_cast<const RBBIStateTableRow8*>(rbbStateTable->fTableData + rbbStateTable->fRowLen * state);
72 int16_t completedRule = row->fAccepting;
73 if ((completedRule > 1) || (state == LINE_BREAK_STATE_STOP)) {
74 return index;
75 }
76 }
77 return record.GetStrLen();
78 }
79
LoadRule()80 void UILineBreakEngine::LoadRule()
81 {
82 if ((fp_ < 0) || (addr_ == nullptr)) {
83 return;
84 }
85 UErrorCode status = U_ZERO_ERROR;
86 u_setMemoryFunctions(nullptr, MemAlloc, MemRealloc, MemFree, &status);
87 if (status != U_ZERO_ERROR) {
88 return;
89 }
90 int32_t ret = lseek(fp_, offset_, SEEK_SET);
91 if (ret != offset_) {
92 return;
93 }
94 char* buf = addr_;
95 ret = read(fp_, buf, size_);
96 if (ret != size_) {
97 return;
98 }
99 const char* dataInBytes = reinterpret_cast<const char*>(buf);
100 const DataHeader* dh = reinterpret_cast<const DataHeader*>(buf);
101 const RBBIDataHeader* rbbidh = reinterpret_cast<const RBBIDataHeader*>(dataInBytes + dh->dataHeader.headerSize);
102 stateTbl_ = reinterpret_cast<const RBBIStateTable*>(reinterpret_cast<const char*>(rbbidh) + rbbidh->fFTable);
103 status = U_ZERO_ERROR;
104 lineBreakTrie_ = reinterpret_cast<UCPTrie*>(ucptrie_openFromBinary(UCPTRIE_TYPE_FAST, UCPTRIE_VALUE_BITS_8,
105 reinterpret_cast<const uint8_t*>(rbbidh)
106 + rbbidh->fTrie,
107 rbbidh->fTrieLen, nullptr, &status));
108 if (status != U_ZERO_ERROR) {
109 return;
110 }
111 initSuccess_ = true;
112 }
113
GetNextLineAndWidth(const char* text, uint16_t fontId, uint8_t fontSize, int16_t space, bool allBreak, int16_t& maxWidth, int16_t& maxHeight, uint16_t& letterIndex, SpannableString* spannableString, uint16_t len, bool eliminateTrailingSpaces)114 uint32_t UILineBreakEngine::GetNextLineAndWidth(const char* text,
115 uint16_t fontId,
116 uint8_t fontSize,
117 int16_t space,
118 bool allBreak,
119 int16_t& maxWidth,
120 int16_t& maxHeight,
121 uint16_t& letterIndex,
122 SpannableString* spannableString,
123 uint16_t len,
124 bool eliminateTrailingSpaces)
125 {
126 if (text == nullptr) {
127 return 0;
128 }
129 bool isAllCanBreak = allBreak;
130 uint32_t byteIdx = 0;
131 uint32_t preIndex = 0;
132 int16_t lastWidth = 0;
133 int16_t lastIndex = 0;
134 int16_t curWidth = 0;
135 int32_t state = LINE_BREAK_STATE_START;
136 int16_t width = 0;
137 int16_t height = 0;
138
139 int16_t preWidth = 0;
140 bool isEliminateSpace = false;
141 while ((byteIdx < len) && (text[byteIdx] != '\0')) {
142 uint32_t unicode = TypedText::GetUTF8Next(text, preIndex, byteIdx);
143 if (unicode == 0) {
144 preIndex = byteIdx;
145 continue;
146 }
147 isEliminateSpace = eliminateTrailingSpaces && unicode == ' ';
148
149 if (isAllCanBreak || IsBreakPos(unicode, fontId, fontSize, state) || isEliminateSpace) {
150 state = LINE_BREAK_STATE_START;
151 // Accumulates the status value from the current character.
152 IsBreakPos(unicode, fontId, fontSize, state);
153 lastIndex = preIndex;
154 lastWidth = eliminateTrailingSpaces ? preWidth : curWidth;
155 }
156 width = GetLetterWidth(unicode, letterIndex, height, fontId, fontSize, spannableString);
157 letterIndex++;
158 if (height > maxHeight) {
159 maxHeight = height;
160 }
161 int16_t nextWidth = (curWidth > 0 && width > 0) ? (curWidth + space + width) : (curWidth + width);
162 if (isEliminateSpace) {
163 if (nextWidth > maxWidth) {
164 curWidth = nextWidth;
165 preIndex = byteIdx;
166 continue;
167 }
168 } else {
169 if (nextWidth > maxWidth) {
170 letterIndex--;
171 if (lastIndex == 0) {
172 break;
173 }
174 maxWidth = lastWidth;
175 return lastIndex;
176 }
177 }
178
179 if (unicode != ' ' && eliminateTrailingSpaces) {
180 preWidth = nextWidth;
181 }
182 curWidth = nextWidth;
183 preIndex = byteIdx;
184 if (byteIdx > 0 && ((text[byteIdx - 1] == '\r') || (text[byteIdx - 1] == '\n'))) {
185 break;
186 }
187 }
188
189 maxWidth = eliminateTrailingSpaces ? preWidth : curWidth;
190 return preIndex;
191 }
192
GetLetterWidth(uint32_t unicode, uint16_t& letterIndex, int16_t& height, uint16_t fontId, uint8_t fontSize, SpannableString* spannableString)193 int16_t UILineBreakEngine::GetLetterWidth(uint32_t unicode,
194 uint16_t& letterIndex,
195 int16_t& height,
196 uint16_t fontId,
197 uint8_t fontSize,
198 SpannableString* spannableString)
199 {
200 UIFont* fontEngine = UIFont::GetInstance();
201 if (spannableString != nullptr && spannableString->GetSpannable(letterIndex)) {
202 spannableString->GetFontSize(letterIndex, fontSize);
203 spannableString->GetFontHeight(letterIndex, height, fontId, fontSize);
204 int16_t width = fontEngine->GetWidth(unicode, fontId, fontSize, 0);
205 return width;
206 } else {
207 uint16_t tempHeight = fontEngine->GetHeight(fontId, fontSize);
208 height = static_cast<int16_t>(tempHeight);
209 return fontEngine->GetWidth(unicode, fontId, fontSize, 0);
210 }
211 }
212
IsBreakPos(uint32_t unicode, uint16_t fontId, uint8_t fontSize, int32_t& state)213 bool UILineBreakEngine::IsBreakPos(uint32_t unicode, uint16_t fontId, uint8_t fontSize, int32_t& state)
214 {
215 if (TypedText::IsEmoji(unicode)) {
216 return true;
217 }
218 if ((unicode > TypedText::MAX_UINT16_HIGH_SCOPE) || (stateTbl_ == nullptr) || (lineBreakTrie_ == nullptr)) {
219 return true;
220 }
221 const RBBIStateTable* rbbStateTable = reinterpret_cast<const RBBIStateTable*>(stateTbl_);
222 const RBBIStateTableRow8* row =
223 reinterpret_cast<const RBBIStateTableRow8*>(rbbStateTable->fTableData + rbbStateTable->fRowLen * state);
224 uint16_t utf16 = 0;
225 if (unicode <= TypedText::MAX_UINT16_LOW_SCOPE) {
226 utf16 = (unicode & TypedText::MAX_UINT16_LOW_SCOPE);
227 } else if (unicode <= TypedText::MAX_UINT16_HIGH_SCOPE) {
228 utf16 = static_cast<uint16_t>(TypedText::UTF16_LOW_PARAM + (unicode & TypedText::UTF16_LOW_MASK)); // low
229 uint16_t category = UCPTRIE_FAST_GET(reinterpret_cast<UCPTrie*>(lineBreakTrie_), UCPTRIE_8, utf16);
230 // 0x4000: remove the dictionary flag bit
231 if ((category & 0x4000) != 0) {
232 // 0x4000: remove the dictionary flag bit
233 category &= ~0x4000;
234 }
235 state = row->fNextState[category];
236 row = reinterpret_cast<const RBBIStateTableRow8*>(rbbStateTable->fTableData + rbbStateTable->fRowLen * state);
237 utf16 = static_cast<uint16_t>(TypedText::UTF16_HIGH_PARAM1 + (unicode >> TypedText::UTF16_HIGH_SHIFT) -
238 TypedText::UTF16_HIGH_PARAM2); // high
239 }
240 uint16_t category = UCPTRIE_FAST_GET(reinterpret_cast<UCPTrie*>(lineBreakTrie_), UCPTRIE_8, utf16);
241 // 0x4000: remove the dictionary flag bit
242 if ((category & 0x4000) != 0) {
243 // 0x4000: remove the dictionary flag bit
244 category &= ~0x4000;
245 }
246 state = row->fNextState[category];
247 row = reinterpret_cast<const RBBIStateTableRow8*>(rbbStateTable->fTableData + rbbStateTable->fRowLen * state);
248 return (row->fAccepting > 1 || state == LINE_BREAK_STATE_STOP);
249 }
250 } // namespace OHOS
251 #endif // ENABLE_ICU
252