1/*
2 * Copyright (c) 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#include "font/glyphs_file.h"
17#include "draw/draw_utils.h"
18#include "font/font_ram_allocator.h"
19#include "font/ui_font_builder.h"
20#include "gfx_utils/file.h"
21#include "gfx_utils/graphic_log.h"
22#include "securec.h"
23
24namespace OHOS {
25GlyphsFile::GlyphsFile()
26    : binHeader_{{0}},
27      start_(0),
28      fontHeaderSectionStart_(0),
29      fontIndexSectionStart_(0),
30      glyphNodeSectionStart_(0),
31      bitMapSectionStart_(0),
32      fontHeaderCache_(nullptr),
33      indexCache_(nullptr),
34      fontName_(nullptr),
35      fp_(-1),
36      isFileSet_(false),
37      fontNum_(0)
38{
39}
40GlyphsFile::~GlyphsFile()
41{
42    if (fontName_) {
43        UIFree(fontName_);
44        fontName_ = nullptr;
45    }
46}
47
48int8_t GlyphsFile::CacheInit()
49{
50    uint32_t size = 0;
51    for (int32_t i = 0; i < fontNum_; i++) {
52        size += fontHeaderCache_[i].indexLen;
53    }
54
55    indexCache_ = reinterpret_cast<uint8_t*>(FontRamAllocator::GetInstance().Allocate(size));
56    if (indexCache_ == nullptr) {
57        GRAPHIC_LOGE("GlyphsFile::CacheInit Allocate failed");
58        return INVALID_RET_VALUE;
59    }
60
61    int32_t ret = read(fp_, indexCache_, size);
62    if (ret != static_cast<int32_t>(size)) {
63        GRAPHIC_LOGE("GlyphsFile::CacheInit read failed");
64        return INVALID_RET_VALUE;
65    }
66
67    return RET_VALUE_OK;
68}
69
70int8_t GlyphsFile::GetNodeFromFile(uint32_t unicode, uint16_t fontId, GlyphNode& node)
71{
72    uint16_t idx = 0;
73    uint32_t offset;
74    GlyphInfo glyphInfo;
75    int8_t result = GetGlyphInfo(fontId, glyphInfo);
76    if (result != RET_VALUE_OK) {
77        return INVALID_RET_VALUE;
78    }
79    for (int32_t i = RADIX_SHIFT_START; i >= 0; i -= RADIX_TREE_BITS) {
80        offset = idx * sizeof(IndexNode);
81        uint8_t key = static_cast<uint8_t>((unicode >> static_cast<uint8_t>(i)) & RADIX_TREE_MASK);
82        offset += key * sizeof(uint16_t);
83        idx = *(reinterpret_cast<uint16_t*>(glyphInfo.indexCache + offset));
84        if (idx == 0) {
85            return INVALID_RET_VALUE;
86        }
87    }
88
89    offset = glyphInfo.glyphNodeSectionStart + (idx - 1) * sizeof(GlyphNode);
90    int32_t ret = lseek(fp_, offset, SEEK_SET);
91    if (ret != static_cast<int32_t>(offset)) {
92        GRAPHIC_LOGE("GlyphsFile::GetNodeFromFile lseek failed");
93        return INVALID_RET_VALUE;
94    }
95
96    ret = read(fp_, &node, sizeof(GlyphNode));
97    if (ret < 0) {
98        GRAPHIC_LOGE("GlyphsFile::GetNodeFromFile read failed");
99        return INVALID_RET_VALUE;
100    }
101
102    return RET_VALUE_OK;
103}
104
105void GlyphsFile::SetFontName(const char* fontName)
106{
107    if (fontName_ != nullptr) {
108        return;
109    }
110
111    if (fontName == nullptr) {
112        GRAPHIC_LOGE("GlyphsFile::SetFontName invalid parameters");
113        return;
114    }
115
116    uint32_t nameLen = strlen(fontName);
117    if (nameLen > FONT_NAME_LEN_MAX) {
118        nameLen = FONT_NAME_LEN_MAX;
119    } else if (nameLen == 0) {
120        return;
121    }
122
123    fontName_ = static_cast<char*>(UIMalloc(++nameLen));
124    if (fontName_ == nullptr) {
125        return;
126    }
127    if (memcpy_s(fontName_, nameLen, fontName, nameLen) != EOK) {
128        UIFree(fontName_);
129        fontName_ = nullptr;
130    }
131}
132
133int8_t GlyphsFile::SetFile(const char* fontName, int32_t fp, uint32_t start)
134{
135    SetFontName(fontName);
136    fp_ = fp;
137    start_ = start;
138    int32_t ret = lseek(fp_, start_, SEEK_SET);
139    if (ret < 0) {
140        GRAPHIC_LOGE("GlyphsFile::SetFile lseek failed");
141        return INVALID_RET_VALUE;
142    }
143    ret = read(fp_, &binHeader_, sizeof(binHeader_));
144    if (ret != sizeof(binHeader_)) {
145        GRAPHIC_LOGE("GlyphsFile::SetFile read failed");
146        return INVALID_RET_VALUE;
147    }
148    if (strncmp(binHeader_.fontMagic, FONT_MAGIC_NUMBER, FONT_MAGIC_NUM_LEN) != 0) {
149        return INVALID_RET_VALUE;
150    }
151    if (binHeader_.fontNum > UIFontBuilder::GetInstance()->GetBitmapFontIdMax()) {
152        GRAPHIC_LOGE("GlyphsFile::SetFile data error, fontNum need less than max fontId");
153        return INVALID_RET_VALUE;
154    }
155
156    fontNum_ = binHeader_.fontNum;
157    fontHeaderSectionStart_ = start_ + sizeof(binHeader_);
158    uint32_t size = sizeof(FontHeader) * fontNum_;
159    fontIndexSectionStart_ = fontHeaderSectionStart_ + size;
160
161    fontHeaderCache_ = reinterpret_cast<FontHeader*>(FontRamAllocator::GetInstance().Allocate(size));
162    if (fontHeaderCache_ == nullptr) {
163        GRAPHIC_LOGE("GlyphsFile::SetFile allocate font header cache failed");
164        return INVALID_RET_VALUE;
165    }
166
167    ret = read(fp_, fontHeaderCache_, size);
168    if (ret != static_cast<int32_t>(size)) {
169        GRAPHIC_LOGE("GlyphsFile::SetFile read failed");
170        return INVALID_RET_VALUE;
171    }
172
173    FontHeader* last = fontHeaderCache_ + fontNum_ - 1;
174    size = last->indexOffset + last->indexLen;
175    glyphNodeSectionStart_ = fontIndexSectionStart_ + size;
176
177    size = 0;
178    for (uint32_t i = 0; i < fontNum_; i++) {
179        size += fontHeaderCache_[i].glyphNum * sizeof(GlyphNode);
180    }
181    bitMapSectionStart_ = glyphNodeSectionStart_ + size;
182    ret = CacheInit();
183    if (ret == RET_VALUE_OK) {
184        isFileSet_ = true;
185    }
186
187    return ret;
188}
189
190bool GlyphsFile::IsSameFile(const char* fontName)
191{
192    if ((fontName_ == nullptr) || (fontName == nullptr)) {
193        return false;
194    }
195
196    uint32_t Offset = 0;
197    uint32_t nameLen = strlen(fontName);
198    if (nameLen > FONT_NAME_LEN_MAX) {
199        Offset = nameLen - FONT_NAME_LEN_MAX;
200    }
201    return (strcmp(fontName_, fontName + Offset) == 0);
202}
203
204int8_t GlyphsFile::GetGlyphInfo(uint16_t fontId, GlyphInfo& glyphInfo)
205{
206    uint16_t fontIdx = 0;
207    UIFontBuilder* fontBuilder = UIFontBuilder::GetInstance();
208    if (fontId > fontBuilder->GetBitmapFontIdMax()) {
209        GRAPHIC_LOGE("GlyphsFile::GetGlyphInfo fontId need less than max fontId");
210        return INVALID_RET_VALUE;
211    }
212    if (!isFileSet_) {
213        GRAPHIC_LOGE("GlyphsFile::GetGlyphInfo file not set");
214        return INVALID_RET_VALUE;
215    }
216
217    int32_t low = 0;
218    int32_t high = binHeader_.fontNum - 1;
219    bool found = false;
220
221    while (low <= high) {
222        int32_t mid = (low + high) / 2; // 2 means half
223        if (fontHeaderCache_[mid].fontId == fontId) {
224            fontIdx = mid;
225            found = true;
226            break;
227        } else if (fontHeaderCache_[mid].fontId > fontId) {
228            high = mid - 1;
229        } else if (fontHeaderCache_[mid].fontId < fontId) {
230            low = mid + 1;
231        }
232    }
233    if (!found) {
234        glyphInfo.fontHeader = nullptr;
235        glyphInfo.fontId = fontBuilder->GetBitmapFontIdMax();
236        return INVALID_RET_VALUE;
237    }
238
239    uint32_t size = 0;
240    glyphInfo.fontId = fontId;
241    glyphInfo.fontHeader = fontHeaderCache_ + fontIdx;
242    glyphInfo.fontIndexSectionStart = fontIndexSectionStart_ + glyphInfo.fontHeader->indexOffset;
243    for (uint32_t i = 0; i < fontIdx; i++) {
244        size += fontHeaderCache_[i].glyphNum * sizeof(GlyphNode);
245    }
246    glyphInfo.glyphNodeSectionStart = glyphNodeSectionStart_ + size;
247    glyphInfo.bitMapSectionStart = bitMapSectionStart_ + glyphInfo.fontHeader->glyphOffset;
248    glyphInfo.indexCache = indexCache_ + glyphInfo.fontHeader->indexOffset;
249
250    return RET_VALUE_OK;
251}
252
253int8_t GlyphsFile::GetFontVersion(const char* fontName, char* version, uint8_t len)
254{
255    if (!isFileSet_ || (version == nullptr) || (len > FONT_VERSION_LEN)) {
256        GRAPHIC_LOGE("GlyphsFile::GetFontVersion invalid parameters");
257        return INVALID_RET_VALUE;
258    }
259
260    if (!IsSameFile(fontName)) {
261        return INVALID_RET_VALUE;
262    }
263
264    if (memset_s(version, len, 0, len) != EOK) {
265        GRAPHIC_LOGE("GlyphsFile::GetFontVersion memset_s failed");
266        return INVALID_RET_VALUE;
267    }
268    if (strcpy_s(version, len, binHeader_.fontVersion) != EOK) {
269        GRAPHIC_LOGE("GlyphsFile::GetFontVersion strcpy_s failed");
270        return INVALID_RET_VALUE;
271    }
272    return RET_VALUE_OK;
273}
274
275const FontHeader* GlyphsFile::GetFontHeader(uint16_t fontId)
276{
277    GlyphInfo glyphInfo;
278    int8_t ret = GetGlyphInfo(fontId, glyphInfo);
279    if (ret != RET_VALUE_OK) {
280        return nullptr;
281    }
282
283    return glyphInfo.fontHeader;
284}
285
286int16_t GlyphsFile::GetFontHeight(uint16_t fontId)
287{
288    GlyphInfo glyphInfo;
289    int8_t ret = GetGlyphInfo(fontId, glyphInfo);
290    if (ret != RET_VALUE_OK) {
291        return INVALID_RET_VALUE;
292    }
293
294    return glyphInfo.fontHeader->fontHeight;
295}
296
297int8_t GlyphsFile::GetBitmap(GlyphNode& node, BufferInfo& bufInfo)
298{
299    if (bufInfo.virAddr == nullptr) {
300        GRAPHIC_LOGE("GlyphsFile::GetBitmap invalid parameter");
301        return INVALID_RET_VALUE;
302    }
303
304    GlyphInfo glyphInfo;
305    int8_t result = GetGlyphInfo(node.fontId, glyphInfo);
306    if (result != RET_VALUE_OK) {
307        return INVALID_RET_VALUE;
308    }
309
310    uint32_t tmpBitMapSectionStart = glyphInfo.bitMapSectionStart;
311    uint32_t offset = tmpBitMapSectionStart + node.dataOff;
312    uint32_t size = node.kernOff - node.dataOff;
313    int32_t ret = lseek(fp_, offset, SEEK_SET);
314    if (ret != static_cast<int32_t>(offset)) {
315        GRAPHIC_LOGE("GlyphsFile::GetBitmap lseek failed");
316        return INVALID_RET_VALUE;
317    }
318
319    int32_t readSize = read(fp_, bufInfo.virAddr, size);
320    if (readSize != static_cast<int32_t>(size)) {
321        GRAPHIC_LOGE("GlyphsFile::GetBitmap read failed");
322        return INVALID_RET_VALUE;
323    }
324    UIFontAllocator::RearrangeBitmap(bufInfo, size, false);
325
326    node.dataFlag = node.fontId;
327    return RET_VALUE_OK;
328}
329} // namespace OHOS
330