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