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
26using namespace U_ICU_NAMESPACE;
27namespace OHOS {
28static void* MemAlloc(const void* context, size_t size)
29{
30    return UIMalloc(size);
31}
32
33static void MemFree(const void* context, void* mem)
34{
35    if (mem == nullptr) {
36        return;
37    }
38    UIFree(mem);
39}
40
41static void* MemRealloc(const void* context, void* mem, size_t size)
42{
43    return UIRealloc(mem, size);
44}
45
46UILineBreakEngine& UILineBreakEngine::GetInstance()
47{
48    static UILineBreakEngine instance;
49    return instance;
50}
51
52uint16_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
80void 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
114uint32_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
193int16_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
213bool 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