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