• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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