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