1 /*
2 * Copyright (c) 2023 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 "measurer_impl.h"
17
18 #include "texgine_exception.h"
19 #include "texgine/utils/exlog.h"
20 #ifdef LOGGER_ENABLE_SCOPE
21 #include "texgine/utils/trace.h"
22 #endif
23 #include "text_converter.h"
24
25 namespace OHOS {
26 namespace Rosen {
27 namespace TextEngine {
28 #define INVALID_TEXT_LENGTH (-1)
29 #define SUCCESSED 0
30 #define FAILED 1
31 #define POLL_MECHANISM 4999
32 #define POLL_NUM 1000
33 constexpr static uint8_t FIRST_BYTE = 24;
34 constexpr static uint8_t SECOND_BYTE = 16;
35 constexpr static uint8_t THIRD_BYTE = 8;
36 static std::string g_detectionName;
37
38 namespace {
DumpCharGroup(int32_t index,const CharGroup & cg,double glyphEm,const hb_glyph_info_t & info,const hb_glyph_position_t & position)39 void DumpCharGroup(int32_t index, const CharGroup &cg, double glyphEm,
40 const hb_glyph_info_t &info, const hb_glyph_position_t &position)
41 {
42 auto uTF8Str = TextConverter::ToUTF8(cg.chars);
43 // 0xffffff is the default char when the cg is null or invalid
44 auto ch = (cg.chars.size() > 0) ? cg.chars[0] : 0xffffff;
45 // the follow numbers is to align log
46 // 2 & 4 means output width,0 means fill with 0
47 LOGEX_FUNC_LINE_DEBUG() << std::setw(2) << std::setfill('0') << index <<
48 std::hex << std::uppercase <<
49 ": " << std::right << std::setw(4) << std::setfill(' ') << "\033[40m" << "'" <<
50 uTF8Str.data() << "'" << "\033[0m" <<
51 " (char: 0x" << std::setw(6) << std::setfill('0') << ch <<
52 ", codepoint: 0x" << std::setw(4) << std::setfill('0') << info.codepoint <<
53 ", cluster: " << std::setw(2) << std::setfill(' ') << std::dec << info.cluster << ")" <<
54 " {" << glyphEm * position.x_advance << ", " << glyphEm * position.y_advance <<
55 ", " << glyphEm * position.x_offset << ", " << glyphEm * position.y_offset << "}";
56 }
57 } // namespace
58
HbFaceReferenceTableTypeface(hb_face_t * face,hb_tag_t tag,void * context)59 hb_blob_t* HbFaceReferenceTableTypeface(hb_face_t* face, hb_tag_t tag, void* context)
60 {
61 if (context == nullptr) {
62 return nullptr;
63 }
64
65 std::shared_ptr<TexgineTypeface> typeface = std::make_shared<TexgineTypeface>(context);
66 if (typeface->GetTypeface() == nullptr) {
67 return nullptr;
68 }
69
70 // 584420 is five times of the tag`s max size now
71 const size_t maxSize = 584420;
72 const size_t tableSize = typeface->GetTableSize(tag);
73 if (tableSize == 0 || tableSize > maxSize) {
74 return nullptr;
75 }
76
77 void* buffer = malloc(tableSize);
78 if (buffer == nullptr) {
79 return nullptr;
80 }
81
82 size_t actualSize = typeface->GetTableData(tag, 0, tableSize, buffer);
83 if (tableSize != actualSize) {
84 free(buffer);
85 buffer = nullptr;
86 return nullptr;
87 }
88
89 return hb_blob_create(reinterpret_cast<char*>(buffer),
90 tableSize, HB_MEMORY_MODE_WRITABLE, buffer, free);
91 }
92
Create(const std::vector<uint16_t> & text,const FontCollection & fontCollection)93 std::unique_ptr<Measurer> Measurer::Create(const std::vector<uint16_t> &text, const FontCollection &fontCollection)
94 {
95 return std::make_unique<MeasurerImpl>(text, fontCollection);
96 }
97
MeasurerImpl(const std::vector<uint16_t> & text,const FontCollection & fontCollection)98 MeasurerImpl::MeasurerImpl(const std::vector<uint16_t> &text, const FontCollection &fontCollection)
99 : Measurer(text, fontCollection)
100 {
101 }
102
GetWordBoundary() const103 const std::vector<Boundary> &MeasurerImpl::GetWordBoundary() const
104 {
105 return boundaries_;
106 }
107
UpdateCache()108 void MeasurerImpl::UpdateCache()
109 {
110 auto iter = cache_.begin();
111 for (size_t index = 0; index < POLL_NUM; index++) {
112 cache_.erase(iter++);
113 }
114 }
115
GetInitKey(struct MeasurerCacheKey & key) const116 void MeasurerImpl::GetInitKey(struct MeasurerCacheKey &key) const
117 {
118 key.text = text_;
119 key.style = style_;
120 key.locale = locale_;
121 key.rtl = rtl_;
122 key.size = size_;
123 key.startIndex = startIndex_;
124 key.endIndex = endIndex_;
125 key.letterSpacing = letterSpacing_;
126 key.wordSpacing = wordSpacing_;
127 }
128
Measure(CharGroups & cgs)129 int MeasurerImpl::Measure(CharGroups &cgs)
130 {
131 #ifdef LOGGER_ENABLE_SCOPE
132 ScopedTrace scope("MeasurerImpl::Measure");
133 #endif
134 LOGSCOPED(sl, LOGEX_FUNC_LINE_DEBUG(), "MeasurerImpl::Measure");
135 MeasurerCacheKey key;
136 GetInitKey(key);
137 if (fontFeatures_ == nullptr || fontFeatures_->GetFeatures().size() == 0) {
138 std::lock_guard<std::mutex> lock(mutex_);
139 auto it = cache_.find(key);
140 if (it != cache_.end()) {
141 cgs = it->second.cgs.Clone();
142 boundaries_ = it->second.boundaries;
143 if (g_detectionName != cgs.GetTypefaceName()) {
144 cache_.erase(key);
145 } else if (cache_.size() > POLL_MECHANISM) {
146 UpdateCache();
147 }
148 return SUCCESSED;
149 }
150 }
151
152 WordBreaker wb;
153 wb.SetLocale(icu::Locale::createFromName(locale_.c_str()));
154 wb.SetRange(0, text_.size());
155 boundaries_ = wb.GetBoundary(text_);
156
157 std::list<struct MeasuringRun> runs;
158 SeekScript(runs);
159 SeekTypeface(runs);
160 if (auto ret = Shape(cgs, runs, boundaries_); ret) {
161 LOGEX_FUNC_LINE(ERROR) << "Shape failed";
162 return ret;
163 }
164
165 if (fontFeatures_ == nullptr || fontFeatures_->GetFeatures().size() == 0) {
166 if (cgs.CheckCodePoint()) {
167 g_detectionName = cgs.GetTypefaceName();
168 struct MeasurerCacheVal value = {cgs.Clone(), boundaries_};
169 std::lock_guard<std::mutex> lock(mutex_);
170 cache_[key] = value;
171 }
172 }
173 return SUCCESSED;
174 }
175
SeekTypeface(std::list<struct MeasuringRun> & runs)176 void MeasurerImpl::SeekTypeface(std::list<struct MeasuringRun> &runs)
177 {
178 #ifdef LOGGER_ENABLE_SCOPE
179 ScopedTrace scope("MeasurerImpl::SeekTypeface");
180 #endif
181 LOGSCOPED(sl, LOGEX_FUNC_LINE_DEBUG(), "typeface");
182 for (auto runsit = runs.begin(); runsit != runs.end(); runsit++) {
183 if (runsit->end > text_.size()) {
184 LOGEX_FUNC_LINE(ERROR) << "runsit->end overflow of text_";
185 break;
186 }
187 size_t utf16Index = static_cast<size_t>(runsit->start);
188 uint32_t cp = 0;
189 std::shared_ptr<Typeface> lastTypeface = nullptr;
190 while (utf16Index < runsit->end) {
191 U16_NEXT(text_.data(), utf16Index, runsit->end, cp);
192 if (lastTypeface && lastTypeface->Has(cp)) {
193 LOGCEX_DEBUG() << " cached";
194 continue;
195 }
196 if (runsit->typeface) {
197 LOGCEX_DEBUG() << " new";
198 auto next = runsit;
199 struct MeasuringRun run = {.start = utf16Index - U16_LENGTH(cp), .end = runsit->end,
200 .script = runsit->script};
201 runs.insert(++next, run);
202 runsit->end = utf16Index - U16_LENGTH(cp);
203 break;
204 }
205
206 char runScript[5] = {(char)(((runsit->script) >> FIRST_BYTE) & 0xFF),
207 (char)(((runsit->script) >> SECOND_BYTE) & 0xFF),
208 (char)(((runsit->script) >> THIRD_BYTE) & 0xFF),
209 (char)((runsit->script) & 0xFF), '\0'};
210 lastTypeface = fontCollection_.GetTypefaceForChar(cp, style_, runScript, locale_);
211 if (lastTypeface == nullptr) {
212 LOGCEX_DEBUG() << " no typeface";
213 continue;
214 }
215
216 LOGCEX_DEBUG() << " found at " << lastTypeface->GetName();
217 runsit->typeface = lastTypeface;
218 }
219 }
220 }
221
SeekScript(std::list<struct MeasuringRun> & runs)222 void MeasurerImpl::SeekScript(std::list<struct MeasuringRun> &runs)
223 {
224 #ifdef LOGGER_ENABLE_SCOPE
225 ScopedTrace scope("MeasurerImpl::SeekScript");
226 #endif
227 LOGSCOPED(sl, LOGEX_FUNC_LINE_DEBUG(), "script");
228 auto icuGetUnicodeFuncs = hb_unicode_funcs_create(hb_icu_get_unicode_funcs());
229 if (icuGetUnicodeFuncs == nullptr) {
230 LOGEX_FUNC_LINE(ERROR) << "hb_unicode_funcs_create return nullptr";
231 throw TEXGINE_EXCEPTION(API_FAILED);
232 }
233
234 struct MeasuringRun run = {.start = startIndex_, .end = endIndex_};
235 runs.push_back(run);
236 DoSeekScript(runs, icuGetUnicodeFuncs);
237 hb_unicode_funcs_destroy(icuGetUnicodeFuncs);
238 }
239
DoSeekScript(std::list<struct MeasuringRun> & runs,hb_unicode_funcs_t * icuGetUnicodeFuncs)240 void MeasurerImpl::DoSeekScript(std::list<struct MeasuringRun> &runs, hb_unicode_funcs_t* icuGetUnicodeFuncs)
241 {
242 int index = 0;
243 for (auto it = runs.begin(); it != runs.end(); it++) {
244 std::stringstream ss;
245 ss << "[" << it->start << ", " << it->end << ")";
246 LOGSCOPED(sl, LOGEX_FUNC_LINE_DEBUG(), ss.str());
247
248 size_t utf16Index = it->start;
249 uint32_t cp = 0;
250 auto &script = it->script;
251 while (utf16Index < it->end) {
252 if (utf16Index >= text_.size()) {
253 LOGEX_FUNC_LINE(ERROR) << "u16index overflow of text_";
254 throw TEXGINE_EXCEPTION(ERROR_STATUS);
255 }
256
257 U16_NEXT(text_.data(), utf16Index, it->end, cp);
258 auto s = hb_unicode_script(icuGetUnicodeFuncs, cp);
259 // 2 & 6 means output width,0 means fill with 0
260 LOGEX_FUNC_LINE_DEBUG() << "[" << std::setw(2) << std::setfill('0') << index++ << ": 0x"
261 << std::setw(6) << std::setfill('0') << std::hex << std::uppercase << cp << "]" << " " << s;
262 if (script == static_cast<size_t>(HB_SCRIPT_INVALID)) {
263 script = s;
264 }
265
266 if (s == script) {
267 continue;
268 }
269
270 if (script == HB_SCRIPT_INHERITED || script == HB_SCRIPT_COMMON) {
271 script = s;
272 } else if (s == HB_SCRIPT_INHERITED || s == HB_SCRIPT_COMMON) {
273 continue;
274 } else {
275 index--;
276 auto next = it;
277 struct MeasuringRun run = {.start = utf16Index - U16_LENGTH(cp), .end = it->end};
278 runs.insert(++next, run);
279 it->end = utf16Index - U16_LENGTH(cp);
280 break;
281 }
282 }
283
284 if (it->end <= it->start) {
285 LOGEX_FUNC_LINE(ERROR) << "run have error range";
286 throw TEXGINE_EXCEPTION(ERROR_STATUS);
287 }
288
289 it->script = script;
290 }
291 }
292
Shape(CharGroups & cgs,std::list<struct MeasuringRun> & runs,std::vector<Boundary> boundaries)293 int MeasurerImpl::Shape(CharGroups &cgs, std::list<struct MeasuringRun> &runs, std::vector<Boundary> boundaries)
294 {
295 #ifdef LOGGER_ENABLE_SCOPE
296 ScopedTrace scope("MeasurerImpl::Shape");
297 #endif
298 cgs = CharGroups::CreateEmpty();
299 LOGSCOPED(sl, LOGEX_FUNC_LINE_DEBUG(), "shape");
300 size_t index = 0;
301 for (auto &run : runs) {
302 if (run.end <= run.start) {
303 LOGEX_FUNC_LINE(ERROR) << "run have error range";
304 throw TEXGINE_EXCEPTION(ERROR_STATUS);
305 }
306
307 std::stringstream ss;
308 ss << "[" << run.start << ", " << run.end << ")";
309 LOGSCOPED(sl, LOGEX_FUNC_LINE_DEBUG(), ss.str());
310
311 if (DoShape(cgs, run, index)) {
312 return FAILED;
313 }
314 }
315
316 for (auto &[start, end] : boundaries) {
317 const auto &wordcgs = cgs.GetSubFromU16RangeAll(start, end);
318 auto cg = wordcgs.Get(wordcgs.GetNumberOfCharGroup() - 1);
319 bool isWhitespace = (u_isWhitespace(cg.chars[0]) == 1);
320 if (isWhitespace) {
321 cg.invisibleWidth += wordSpacing_;
322 }
323 cg.isWordEnd = true;
324 }
325 return SUCCESSED;
326 }
327
DoShape(CharGroups & cgs,MeasuringRun & run,size_t & index)328 int MeasurerImpl::DoShape(CharGroups &cgs, MeasuringRun &run, size_t &index)
329 {
330 auto typeface = run.typeface;
331 if (typeface == nullptr) {
332 LOGEX_FUNC_LINE(ERROR) << "there is no typeface";
333 return FAILED;
334 }
335
336 auto hbuffer = hb_buffer_create();
337 if (!hbuffer) {
338 LOGEX_FUNC_LINE(ERROR) << "hbuffer is nullptr";
339 return FAILED;
340 }
341
342 if (text_.empty()) {
343 LOGEX_FUNC_LINE(ERROR) << "text is nullptr";
344 return FAILED;
345 }
346 hb_buffer_add_utf16(hbuffer, text_.data(), text_.size(), run.start, run.end - run.start);
347 hb_buffer_set_direction(hbuffer, rtl_ ? HB_DIRECTION_RTL : HB_DIRECTION_LTR);
348 auto icuGetUnicodeFuncs = hb_unicode_funcs_create(hb_icu_get_unicode_funcs());
349 if (!icuGetUnicodeFuncs) {
350 LOGEX_FUNC_LINE(ERROR) << "icuGetUnicodeFuncs is nullptr";
351 HbDestroy(hbuffer, nullptr, nullptr, nullptr);
352 return FAILED;
353 }
354
355 hb_buffer_set_unicode_funcs(hbuffer, icuGetUnicodeFuncs);
356 hb_buffer_set_script(hbuffer, run.script);
357 hb_buffer_set_language(hbuffer, hb_language_from_string(locale_.c_str(), INVALID_TEXT_LENGTH));
358
359 auto hface = hb_face_create_for_tables(HbFaceReferenceTableTypeface, typeface->Get()->GetTypeface().get(), 0);
360 if (!hface) {
361 LOGEX_FUNC_LINE(ERROR) << "hface is nullptr";
362 HbDestroy(hbuffer, nullptr, nullptr, icuGetUnicodeFuncs);
363 return FAILED;
364 }
365
366 auto hfont = hb_font_create(hface);
367 if (!hfont) {
368 LOGEX_FUNC_LINE(ERROR) << "hfont is nullptr";
369 HbDestroy(hbuffer, nullptr, hface, icuGetUnicodeFuncs);
370 return FAILED;
371 }
372
373 std::vector<hb_feature_t> ff;
374 GenerateHBFeatures(ff, fontFeatures_);
375 hb_shape(hfont, hbuffer, ff.data(), ff.size());
376
377 if (GetGlyphs(cgs, run, index, hbuffer, typeface)) {
378 HbDestroy(hbuffer, hfont, hface, icuGetUnicodeFuncs);
379 return FAILED;
380 }
381
382 HbDestroy(hbuffer, hfont, hface, icuGetUnicodeFuncs);
383 return SUCCESSED;
384 }
385
HbDestroy(hb_buffer_t * hbuffer,hb_font_t * hfont,hb_face_t * hface,hb_unicode_funcs_t * icuGetUnicodeFuncs)386 void MeasurerImpl::HbDestroy(hb_buffer_t* hbuffer, hb_font_t* hfont, hb_face_t* hface,
387 hb_unicode_funcs_t* icuGetUnicodeFuncs)
388 {
389 if (hbuffer) {
390 hb_buffer_destroy(hbuffer);
391 }
392
393 if (hfont) {
394 hb_font_destroy(hfont);
395 }
396
397 if (hface) {
398 hb_face_destroy(hface);
399 }
400
401 if (icuGetUnicodeFuncs) {
402 hb_unicode_funcs_destroy(icuGetUnicodeFuncs);
403 }
404 }
405
GetGlyphs(CharGroups & cgs,MeasuringRun & run,size_t & index,hb_buffer_t * hbuffer,std::shared_ptr<TextEngine::Typeface> typeface)406 int MeasurerImpl::GetGlyphs(CharGroups &cgs, MeasuringRun &run, size_t &index, hb_buffer_t* hbuffer,
407 std::shared_ptr<TextEngine::Typeface> typeface)
408 {
409 uint32_t ng = 0u;
410 auto hginfos = hb_buffer_get_glyph_infos(hbuffer, &ng);
411 if (!hginfos) {
412 LOGEX_FUNC_LINE(ERROR) << "hginfos is nullptr";
413 return FAILED;
414 }
415
416 auto hgpositions = hb_buffer_get_glyph_positions(hbuffer, nullptr);
417 if (!hgpositions) {
418 LOGEX_FUNC_LINE(ERROR) << "hgpositions is nullptr";
419 return FAILED;
420 }
421
422 LOGEX_FUNC_LINE_DEBUG() << "ng: " << ng;
423 auto upe = typeface->Get()->GetUnitsPerEm();
424 if (!upe) {
425 LOGEX_FUNC_LINE(ERROR) << "upe is 0";
426 return FAILED;
427 }
428
429 auto glyphEm = 1.0 * size_ / upe;
430 std::map<uint32_t, CharGroup> cgsByCluster{{run.end, {}}};
431 for (uint32_t i = 0; i < ng; i++) {
432 auto &cg = cgsByCluster[hginfos[i].cluster];
433 struct Glyph glyph = {
434 .codepoint = hginfos[i].codepoint,
435 .advanceX = glyphEm * hgpositions[i].x_advance,
436 .advanceY = glyphEm * hgpositions[i].y_advance,
437 .offsetX = glyphEm * hgpositions[i].x_offset,
438 .offsetY = glyphEm * hgpositions[i].y_offset
439 };
440 cg.glyphs.push_back(glyph);
441 cg.typeface = typeface;
442 }
443
444 DoCgsByCluster(cgsByCluster);
445 cgsByCluster.erase(run.end);
446 for (uint32_t i = 0; i < ng; i++) {
447 DumpCharGroup(index++, cgsByCluster[hginfos[i].cluster], glyphEm, hginfos[i], hgpositions[i]);
448 }
449
450 for (const auto &[cluster, cg] : cgsByCluster) {
451 cgs.PushBack(cg);
452 }
453
454 if (rtl_) {
455 cgs.ReverseAll();
456 }
457 return SUCCESSED;
458 }
459
DoCgsByCluster(std::map<uint32_t,TextEngine::CharGroup> & cgsByCluster)460 void MeasurerImpl::DoCgsByCluster(std::map<uint32_t, TextEngine::CharGroup> &cgsByCluster)
461 {
462 for (auto it = cgsByCluster.begin(); it != cgsByCluster.end(); it++) {
463 auto start = (it++)->first;
464 if (it == cgsByCluster.end()) {
465 break;
466 }
467
468 auto end = (it--)->first;
469 if (start > text_.size() || end > text_.size()) {
470 LOGEX_FUNC_LINE(ERROR) << "cgsByCluster is not align with text_";
471 throw TEXGINE_EXCEPTION(ERROR_STATUS);
472 }
473
474 it->second.chars.insert(it->second.chars.end(), text_.begin() + start, text_.begin() + end);
475 if (it->second.IsHardBreak()) {
476 continue;
477 }
478 it->second.visibleWidth = 0;
479 for (const auto &glyph : it->second.glyphs) {
480 it->second.visibleWidth += glyph.advanceX;
481 }
482
483 it->second.invisibleWidth = letterSpacing_;
484 if (u_isWhitespace(it->second.chars[0]) != 0) {
485 it->second.invisibleWidth += it->second.visibleWidth;
486 it->second.visibleWidth = 0;
487 }
488 }
489 }
490
GenerateHBFeatures(std::vector<hb_feature_t> & fontFeatures,const FontFeatures * ff)491 void MeasurerImpl::GenerateHBFeatures(std::vector<hb_feature_t> &fontFeatures, const FontFeatures* ff)
492 {
493 LOGSCOPED(sl, LOGEX_FUNC_LINE_DEBUG(), "GenerateHBFeatures");
494 if (ff == nullptr) {
495 return;
496 }
497
498 auto features = ff->GetFeatures();
499 if (features.empty()) {
500 return;
501 }
502
503 for (auto &[ft, fv] : features) {
504 hb_feature_t hf;
505 if (hb_feature_from_string(ft.c_str(), ft.size(), &hf)) {
506 hf.value = static_cast<size_t>(fv);
507 fontFeatures.push_back(hf);
508 }
509
510 LOGEX_FUNC_LINE_DEBUG() << "feature tag:" << hf.tag << ", feature value:" << hf.value;
511 }
512 }
513 } // namespace TextEngine
514 } // namespace Rosen
515 } // namespace OHOS
516