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     if (dh->dataHeader.headerSize >= static_cast<uint32_t>(size_)) {
102         return;
103     }
104     const RBBIDataHeader* rbbidh = reinterpret_cast<const RBBIDataHeader*>(dataInBytes + dh->dataHeader.headerSize);
105     if (dh->dataHeader.headerSize + rbbidh->fFTable >= static_cast<uint32_t>(size_)) {
106         return;
107     }
108     stateTbl_ = reinterpret_cast<const RBBIStateTable*>(reinterpret_cast<const char*>(rbbidh) + rbbidh->fFTable);
109     status = U_ZERO_ERROR;
110     if (dh->dataHeader.headerSize + rbbidh->fTrie + rbbidh->fTrieLen > static_cast<uint32_t>(size_)) {
111         return;
112     }
113     lineBreakTrie_ = reinterpret_cast<UCPTrie*>(ucptrie_openFromBinary(UCPTRIE_TYPE_FAST, UCPTRIE_VALUE_BITS_8,
114                                                                        reinterpret_cast<const uint8_t*>(rbbidh)
115                                                                        + rbbidh->fTrie,
116                                                                        rbbidh->fTrieLen, nullptr, &status));
117     if (status != U_ZERO_ERROR) {
118         return;
119     }
120     initSuccess_ = true;
121 }
122 
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)123 uint32_t UILineBreakEngine::GetNextLineAndWidth(const char* text,
124                                                 uint16_t fontId,
125                                                 uint8_t fontSize,
126                                                 int16_t space,
127                                                 bool allBreak,
128                                                 int16_t& maxWidth,
129                                                 int16_t& maxHeight,
130                                                 uint16_t& letterIndex,
131                                                 SpannableString* spannableString,
132                                                 uint16_t len,
133                                                 bool eliminateTrailingSpaces)
134 {
135     if (text == nullptr) {
136         return 0;
137     }
138     bool isAllCanBreak = allBreak;
139     uint32_t byteIdx = 0;
140     uint32_t preIndex = 0;
141     int16_t lastWidth = 0;
142     int16_t lastIndex = 0;
143     int16_t curWidth = 0;
144     int32_t state = LINE_BREAK_STATE_START;
145     int16_t width = 0;
146     int16_t height = 0;
147 
148     int16_t preWidth = 0;
149     bool isEliminateSpace = false;
150     while ((byteIdx < len) && (text[byteIdx] != '\0')) {
151         uint32_t unicode = TypedText::GetUTF8Next(text, preIndex, byteIdx);
152         if (unicode == 0) {
153             preIndex = byteIdx;
154             continue;
155         }
156         isEliminateSpace = eliminateTrailingSpaces && unicode == ' ';
157 
158         if (isAllCanBreak || IsBreakPos(unicode, fontId, fontSize, state) || isEliminateSpace) {
159             state = LINE_BREAK_STATE_START;
160             // Accumulates the status value from the current character.
161             IsBreakPos(unicode, fontId, fontSize, state);
162             lastIndex = preIndex;
163             lastWidth = eliminateTrailingSpaces ? preWidth : curWidth;
164         }
165         width = GetLetterWidth(unicode, letterIndex, height, fontId, fontSize, spannableString);
166         letterIndex++;
167         if (height > maxHeight) {
168             maxHeight = height;
169         }
170         int16_t nextWidth = (curWidth > 0 && width > 0) ? (curWidth + space + width) : (curWidth + width);
171         if (isEliminateSpace) {
172             if (nextWidth > maxWidth) {
173                 curWidth = nextWidth;
174                 preIndex = byteIdx;
175                 continue;
176             }
177         } else {
178             if (nextWidth > maxWidth) {
179                 letterIndex--;
180                 if (lastIndex == 0) {
181                     break;
182                 }
183                 maxWidth = lastWidth;
184                 return lastIndex;
185             }
186         }
187 
188         if (unicode != ' ' && eliminateTrailingSpaces) {
189             preWidth = nextWidth;
190         }
191         curWidth = nextWidth;
192         preIndex = byteIdx;
193         if (byteIdx > 0 && ((text[byteIdx - 1] == '\r') || (text[byteIdx - 1] == '\n'))) {
194             break;
195         }
196     }
197 
198     maxWidth = eliminateTrailingSpaces ? preWidth : curWidth;
199     return preIndex;
200 }
201 
GetLetterWidth(uint32_t unicode,uint16_t & letterIndex,int16_t & height,uint16_t fontId,uint8_t fontSize,SpannableString * spannableString)202 int16_t UILineBreakEngine::GetLetterWidth(uint32_t unicode,
203                                           uint16_t& letterIndex,
204                                           int16_t& height,
205                                           uint16_t fontId,
206                                           uint8_t fontSize,
207                                           SpannableString* spannableString)
208 {
209     UIFont* fontEngine = UIFont::GetInstance();
210     if (spannableString != nullptr && spannableString->GetSpannable(letterIndex)) {
211         spannableString->GetFontSize(letterIndex, fontSize);
212         spannableString->GetFontHeight(letterIndex, height, fontId, fontSize);
213         int16_t width = fontEngine->GetWidth(unicode, fontId, fontSize, 0);
214         return width;
215     } else {
216         uint16_t tempHeight = fontEngine->GetHeight(fontId, fontSize);
217         height = static_cast<int16_t>(tempHeight);
218         return fontEngine->GetWidth(unicode, fontId, fontSize, 0);
219     }
220 }
221 
IsBreakPos(uint32_t unicode,uint16_t fontId,uint8_t fontSize,int32_t & state)222 bool UILineBreakEngine::IsBreakPos(uint32_t unicode, uint16_t fontId, uint8_t fontSize, int32_t& state)
223 {
224     if (TypedText::IsEmoji(unicode)) {
225         return true;
226     }
227     if ((unicode > TypedText::MAX_UINT16_HIGH_SCOPE) || (stateTbl_ == nullptr) || (lineBreakTrie_ == nullptr)) {
228         return true;
229     }
230     const RBBIStateTable* rbbStateTable = reinterpret_cast<const RBBIStateTable*>(stateTbl_);
231     const RBBIStateTableRow8* row =
232         reinterpret_cast<const RBBIStateTableRow8*>(rbbStateTable->fTableData + rbbStateTable->fRowLen * state);
233     uint16_t utf16 = 0;
234     if (unicode <= TypedText::MAX_UINT16_LOW_SCOPE) {
235         utf16 = (unicode & TypedText::MAX_UINT16_LOW_SCOPE);
236     } else if (unicode <= TypedText::MAX_UINT16_HIGH_SCOPE) {
237         utf16 = static_cast<uint16_t>(TypedText::UTF16_LOW_PARAM + (unicode & TypedText::UTF16_LOW_MASK)); // low
238         uint16_t category = UCPTRIE_FAST_GET(reinterpret_cast<UCPTrie*>(lineBreakTrie_), UCPTRIE_8, utf16);
239         // 0x4000: remove the dictionary flag bit
240         if ((category & 0x4000) != 0) {
241             // 0x4000: remove the dictionary flag bit
242             category &= ~0x4000;
243         }
244         state = row->fNextState[category];
245         row = reinterpret_cast<const RBBIStateTableRow8*>(rbbStateTable->fTableData + rbbStateTable->fRowLen * state);
246         utf16 = static_cast<uint16_t>(TypedText::UTF16_HIGH_PARAM1 + (unicode >> TypedText::UTF16_HIGH_SHIFT) -
247                                       TypedText::UTF16_HIGH_PARAM2); // high
248     }
249     uint16_t category = UCPTRIE_FAST_GET(reinterpret_cast<UCPTrie*>(lineBreakTrie_), UCPTRIE_8, utf16);
250     // 0x4000: remove the dictionary flag bit
251     if ((category & 0x4000) != 0) {
252         // 0x4000: remove the dictionary flag bit
253         category &= ~0x4000;
254     }
255     state = row->fNextState[category];
256     row = reinterpret_cast<const RBBIStateTableRow8*>(rbbStateTable->fTableData + rbbStateTable->fRowLen * state);
257     return (row->fAccepting > 1 || state == LINE_BREAK_STATE_STOP);
258 }
259 } // namespace OHOS
260 #endif // ENABLE_ICU
261