1b206638a8e455716001d08fd5e02356df99a9a4
[platform/core/uifw/dali-adaptor.git] / dali / internal / text / text-abstraction / plugin / harfbuzz-proxy-font.cpp
1 /*
2  * Copyright (c) 2023 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 // INTERNAL INCLUDES
18 #include <dali/integration-api/debug.h>
19 #include <dali/internal/text/text-abstraction/plugin/harfbuzz-proxy-font.h>
20
21 // EXTERNAL INCLUDES
22 #include FT_GLYPH_H
23 #include <harfbuzz/hb-ft.h>
24 #include <harfbuzz/hb.h>
25
26 #if defined(DEBUG_ENABLED)
27 extern Dali::Integration::Log::Filter* gFontClientLogFilter;
28 #endif
29
30 namespace Dali::TextAbstraction::Internal
31 {
32 /**
33  * @brief Helper class to create and destroy harfbuzz font, and hold data in harfbuzz callback
34  * It also cache informations what harfbuzz font need to be created.
35  */
36 struct HarfBuzzProxyFont::Impl
37 {
38   /**
39    * @brief Constructor.
40    *
41    * @param[in] freeTypeFace The FreeType face.
42    * @param[in] glyphCacheManager Glyph caching system for this harfbuzz font. It will be used as harfbuzz callback data.
43    */
44   Impl(FT_Face freeTypeFace, GlyphCacheManager* glyphCacheManager)
45   : mFreeTypeFace(freeTypeFace),
46     mGlyphCacheManager(glyphCacheManager),
47     mHarfBuzzFont(nullptr)
48   {
49   }
50
51   // Destructor
52   ~Impl()
53   {
54     if(mHarfBuzzFont)
55     {
56       // It will reduce reference of freetype face automatically.
57       hb_font_destroy(mHarfBuzzFont);
58     }
59   }
60
61 public:
62   /**
63    * @brief Create new harfbuzz font.
64    *
65    * @param[in] requestedPointSize The requiested point size of font.
66    * @param[in] horizontalDpi Horizontal DPI.
67    * @param[in] verticalDpi Vertical DPI.
68    */
69   void CreateHarfBuzzFont(const PointSize26Dot6& requestedPointSize, const uint32_t& horizontalDpi, const uint32_t& verticalDpi);
70
71 private:
72   /**
73    * @brief Register harfbuzz callback functions into current harfbuzz font.
74    */
75   void SetHarfBuzzFunctions();
76
77 public:
78   FT_Face            mFreeTypeFace;      ///< The FreeType face. Owned from font-face-cache-item.
79   GlyphCacheManager* mGlyphCacheManager; ///< Glyph caching system for this harfbuzz font. Owned from font-client-plugin-cache-handler.
80
81   hb_font_t* mHarfBuzzFont; ///< Harfbuzz font handle integrated with FT_Face.
82 };
83
84 HarfBuzzProxyFont::HarfBuzzProxyFont(FT_Face freeTypeFace, const PointSize26Dot6& requestedPointSize, const uint32_t& horizontalDpi, const uint32_t& verticalDpi, GlyphCacheManager* glyphCacheManager)
85 : mHorizontalDpi(horizontalDpi),
86   mVerticalDpi(verticalDpi),
87   mImpl(new Impl(freeTypeFace, glyphCacheManager))
88 {
89   mImpl->CreateHarfBuzzFont(requestedPointSize, mHorizontalDpi, mVerticalDpi);
90 }
91
92 HarfBuzzProxyFont::~HarfBuzzProxyFont()
93 {
94   if(mImpl)
95   {
96     delete mImpl;
97   }
98 }
99
100 HarfBuzzFontHandle HarfBuzzProxyFont::GetHarfBuzzFont() const
101 {
102   if(mImpl)
103   {
104     return static_cast<HarfBuzzFontHandle>(mImpl->mHarfBuzzFont);
105   }
106   return nullptr;
107 }
108
109 // Collection of harfbuzz custom callback functions.
110 // Reference : https://github.com/harfbuzz/harfbuzz/blob/main/src/hb-ft.cc
111 namespace
112 {
113 /**
114  * @brief Get glyph informations by dali glyph cache system.
115  *
116  * @param[in] font_data HarfBuzzProxyFont::Impl pointer that register this callback as void* type.
117  * @param[in] glyphIndex Index of glyph.
118  * @param[out] glyphDataPtr The result of cached glyph data pointer.
119  * @return True if we success to get some glyph data. False otherwise.
120  */
121 static bool GetGlyphCacheData(void* font_data, const GlyphIndex& glyphIndex, GlyphCacheManager::GlyphCacheDataPtr& glyphDataPtr)
122 {
123   HarfBuzzProxyFont::Impl* impl = reinterpret_cast<HarfBuzzProxyFont::Impl*>(font_data);
124
125   // Note : HarfBuzz used only FT_LOAD_DEFAULT | FT_LOAD_NO_HINTING internally.
126   if(DALI_LIKELY(impl && impl->mGlyphCacheManager))
127   {
128     FT_Error error;
129     return impl->mGlyphCacheManager->GetGlyphCacheDataFromIndex(impl->mFreeTypeFace, glyphIndex, FT_LOAD_DEFAULT | FT_LOAD_NO_HINTING, false, glyphDataPtr, error);
130   }
131   return false;
132 }
133
134 /**
135  * @brief Calculate font extents value both in vertical and horizontal.
136  *
137  * @param[in] font Current harfbuzz font data.
138  * @param[in] font_data HarfBuzzProxyFont::Impl pointer that register this callback as void* type.
139  * @param[out] extents Extents value of font. (scale as 26.6)
140  * @param[in] user_data Registered user data.
141  * @return True if we success to get font extents. False otherwise.
142  */
143 static hb_bool_t FontExtentsFunc(hb_font_t* font, void* font_data, hb_font_extents_t* extents, void* user_data)
144 {
145   HarfBuzzProxyFont::Impl* impl = reinterpret_cast<HarfBuzzProxyFont::Impl*>(font_data);
146
147   if(DALI_LIKELY(impl && impl->mFreeTypeFace))
148   {
149     FT_Size_Metrics& ftMetrics = impl->mFreeTypeFace->size->metrics;
150
151     extents->ascender  = ftMetrics.ascender;
152     extents->descender = ftMetrics.descender;
153     extents->line_gap  = ftMetrics.height - (extents->ascender - extents->descender);
154
155     return true;
156   }
157   return false;
158 }
159
160 /**
161  * @brief Convert from character into index of glyph.
162  *
163  * @param[in] font Current harfbuzz font data.
164  * @param[in] font_data HarfBuzzProxyFont::Impl pointer that register this callback as void* type.
165  * @param[in] character The value of character what we want to get index.
166  * @param[out] glyphIndex Index of glyph that current font face used.
167  * @param[in] user_data Registered user data.
168  * @return True if we success to convert.
169  */
170 static hb_bool_t GlyphNormalIndexConvertFunc(hb_font_t* font, void* font_data, hb_codepoint_t character, hb_codepoint_t* glyphIndex, void* user_data)
171 {
172   HarfBuzzProxyFont::Impl* impl = reinterpret_cast<HarfBuzzProxyFont::Impl*>(font_data);
173
174   if(DALI_LIKELY(impl && impl->mFreeTypeFace))
175   {
176     *glyphIndex = FT_Get_Char_Index(impl->mFreeTypeFace, character);
177     return *glyphIndex != 0;
178   }
179   return false;
180 }
181
182 /**
183  * @brief Convert from character and variant selector into index of glyph.
184  *
185  * @param[in] font Current harfbuzz font data.
186  * @param[in] font_data HarfBuzzProxyFont::Impl pointer that register this callback as void* type.
187  * @param[in] character The value of character what we want to get index.
188  * @param[in] variantSelector Variant selector.
189  * @param[out] glyphIndex Index of glyph that current font face used.
190  * @param[in] user_data Registered user data.
191  * @return True if we success to convert.
192  */
193 static hb_bool_t GlyphVariantIndexConvertFunc(hb_font_t* font, void* font_data, hb_codepoint_t character, hb_codepoint_t variantSelector, hb_codepoint_t* glyphIndex, void* user_data)
194 {
195   HarfBuzzProxyFont::Impl* impl = reinterpret_cast<HarfBuzzProxyFont::Impl*>(font_data);
196
197   if(DALI_LIKELY(impl && impl->mFreeTypeFace))
198   {
199     *glyphIndex = FT_Face_GetCharVariantIndex(impl->mFreeTypeFace, character, variantSelector);
200     return *glyphIndex != 0;
201   }
202   return false;
203 }
204
205 /**
206  * @brief Calculate glyph advance value in horizontal.
207  *
208  * @param[in] font Current harfbuzz font data.
209  * @param[in] font_data HarfBuzzProxyFont::Impl pointer that register this callback as void* type.
210  * @param[in] glyphIndex Index of glyph.
211  * @param[in] user_data Registered user data.
212  * @return Horizontal advance value of glyphIndex. (scale as 26.6)
213  */
214 static hb_position_t GlyphHorizontalAdvanceFunc(hb_font_t* font, void* font_data, hb_codepoint_t glyphIndex, void* user_data)
215 {
216   // Output data stored here.
217   GlyphCacheManager::GlyphCacheDataPtr glyphDataPtr;
218   if(GetGlyphCacheData(font_data, static_cast<GlyphIndex>(glyphIndex), glyphDataPtr))
219   {
220     GlyphCacheManager::GlyphCacheData& glyphData = *glyphDataPtr.get();
221
222     // Note : It may return invalid value for fixed size bitmap glyph.
223     // But, Harfbuzz library also return Undefined advanced value if it is fixed size font.
224     // So we'll also ignore that case.
225     return static_cast<hb_position_t>(glyphData.mGlyphMetrics.horiAdvance);
226   }
227   return 0;
228 }
229 /**
230  * @brief Calculate glyph advance value in vertical.
231  *
232  * @param[in] font Current harfbuzz font data.
233  * @param[in] font_data HarfBuzzProxyFont::Impl pointer that register this callback as void* type.
234  * @param[in] glyphIndex Index of glyph.
235  * @param[in] user_data Registered user data.
236  * @return Vertical advance value of glyphIndex. (scale as 26.6)
237  */
238 static hb_position_t GlyphVerticalAdvanceFunc(hb_font_t* font, void* font_data, hb_codepoint_t glyphIndex, void* user_data)
239 {
240   // Output data stored here.
241   GlyphCacheManager::GlyphCacheDataPtr glyphDataPtr;
242   if(GetGlyphCacheData(font_data, static_cast<GlyphIndex>(glyphIndex), glyphDataPtr))
243   {
244     GlyphCacheManager::GlyphCacheData& glyphData = *glyphDataPtr.get();
245
246     // Note : It may return invalid value for fixed size bitmap glyph.
247     // But, Harfbuzz library also return Undefined advanced value if it is fixed size font.
248     // So we'll also ignore that case.
249     return static_cast<hb_position_t>(glyphData.mGlyphMetrics.vertAdvance);
250   }
251   return 0;
252 }
253
254 /**
255  * @brief Calculate glyph origin position value in horizontal.
256  *
257  * @param[in] font Current harfbuzz font data.
258  * @param[in] font_data HarfBuzzProxyFont::Impl that register this callback as void* type.
259  * @param[in] glyphIndex Index of glyph.
260  * @param[out] x Origin position x (scale as 26.6)
261  * @param[out] y Origin position y (scale as 26.6)
262  * @param[in] user_data Registered user data.
263  * @return True if we get data successfully. False if some error occured.
264  */
265 static hb_bool_t GlyphHorizontalOriginFunc(hb_font_t* font, void* font_data, hb_codepoint_t glyphIndex, hb_position_t* x, hb_position_t* y, void* user_data)
266 {
267   // Nothing to do
268   return true;
269 }
270 /**
271  * @brief Calculate glyph origin position value in vertical.
272  *
273  * @param[in] font Current harfbuzz font data.
274  * @param[in] font_data HarfBuzzProxyFont::Impl pointer that register this callback as void* type.
275  * @param[in] glyphIndex Index of glyph.
276  * @param[out] x Origin position x (scale as 26.6)
277  * @param[out] y Origin position y (scale as 26.6)
278  * @param[in] user_data Registered user data.
279  * @return True if we get data successfully. False if some error occured.
280  */
281 static hb_bool_t GlyphVerticalOriginFunc(hb_font_t* font, void* font_data, hb_codepoint_t glyphIndex, hb_position_t* x, hb_position_t* y, void* user_data)
282 {
283   // Output data stored here.
284   GlyphCacheManager::GlyphCacheDataPtr glyphDataPtr;
285   if(GetGlyphCacheData(font_data, static_cast<GlyphIndex>(glyphIndex), glyphDataPtr))
286   {
287     GlyphCacheManager::GlyphCacheData& glyphData = *glyphDataPtr.get();
288
289     *x = glyphData.mGlyphMetrics.horiBearingX - glyphData.mGlyphMetrics.vertBearingX;
290     *y = glyphData.mGlyphMetrics.horiBearingY + glyphData.mGlyphMetrics.vertBearingY;
291     return true;
292   }
293   return false;
294 }
295
296 /**
297  * @brief Calculate glyph kerning value in horizontal.
298  *
299  * @param[in] font Current harfbuzz font data.
300  * @param[in] font_data HarfBuzzProxyFont::Impl pointer that register this callback as void* type.
301  * @param[in] glyphIndex1 First index of glyph to get kerning.
302  * @param[in] glyphIndex2 Second index of glyph to get kerning.
303  * @param[in] user_data Registered user data.
304  * @return Horizontal kerning position. (scale as 26.6)
305  */
306 static hb_position_t GlyphHorizontalKerningFunc(hb_font_t* font, void* font_data, hb_codepoint_t glyphIndex1, hb_codepoint_t glyphIndex2, void* user_data)
307 {
308   HarfBuzzProxyFont::Impl* impl = reinterpret_cast<HarfBuzzProxyFont::Impl*>(font_data);
309
310   if(DALI_LIKELY(impl && impl->mFreeTypeFace))
311   {
312     FT_Error  error;
313     FT_Vector kerning;
314
315     error = FT_Get_Kerning(impl->mFreeTypeFace, glyphIndex1, glyphIndex2, FT_KERNING_UNSCALED, &kerning);
316     if(error == FT_Err_Ok)
317     {
318       return kerning.x;
319     }
320   }
321   return 0;
322 }
323 /**
324  * @brief Calculate glyph kerning value in vertical.
325  *
326  * @param[in] font Current harfbuzz font data.
327  * @param[in] font_data HarfBuzzProxyFont::Impl pointer that register this callback as void* type.
328  * @param[in] glyphIndex1 First index of glyph to get kerning.
329  * @param[in] glyphIndex2 Second index of glyph to get kerning.
330  * @param[in] user_data Registered user data.
331  * @return Vertical kerning position. (scale as 26.6)
332  */
333 static hb_position_t GlyphVerticalKerningFunc(hb_font_t* font, void* font_data, hb_codepoint_t glyphIndex1, hb_codepoint_t glyphIndex2, void* user_data)
334 {
335   // FreeType doesn't support vertical kerning
336   return 0;
337 }
338
339 /**
340  * @brief Calculate glyph extents.
341  *
342  * @param[in] font Current harfbuzz font data.
343  * @param[in] font_data HarfBuzzProxyFont::Impl pointer that register this callback as void* type.
344  * @param[in] glyphIndex Index of glyph.
345  * @param[out] extents Extents value of glyph. (scale as 26.6)
346  * @param[in] user_data Registered user data.
347  * @return True if we get data successfully. False if some error occured.
348  */
349 static hb_bool_t GlyphExtentsFunc(hb_font_t* font, void* font_data, hb_codepoint_t glyphIndex, hb_glyph_extents_t* extents, void* user_data)
350 {
351   // Output data stored here.
352   GlyphCacheManager::GlyphCacheDataPtr glyphDataPtr;
353   if(GetGlyphCacheData(font_data, static_cast<GlyphIndex>(glyphIndex), glyphDataPtr))
354   {
355     GlyphCacheManager::GlyphCacheData& glyphData = *glyphDataPtr.get();
356
357     extents->x_bearing = glyphData.mGlyphMetrics.horiBearingX;
358     extents->y_bearing = glyphData.mGlyphMetrics.horiBearingY;
359     extents->width     = glyphData.mGlyphMetrics.width;
360     extents->height    = glyphData.mGlyphMetrics.height;
361     return true;
362   }
363   return false;
364 }
365
366 } // namespace
367
368 void HarfBuzzProxyFont::Impl::CreateHarfBuzzFont(const PointSize26Dot6& requestedPointSize, const uint32_t& horizontalDpi, const uint32_t& verticalDpi)
369 {
370   // Destroy previous hb_font_t if exist.
371   if(mHarfBuzzFont)
372   {
373     // It will reduce reference of freetype face automatically.
374     hb_font_destroy(mHarfBuzzFont);
375     mHarfBuzzFont = nullptr;
376   }
377
378   if(mFreeTypeFace)
379   {
380     // Before create hb_font_t, we must set FT_Char_Size
381     FT_Set_Char_Size(mFreeTypeFace,
382                      0u,
383                      requestedPointSize,
384                      horizontalDpi,
385                      verticalDpi);
386
387     // Create font face with increase font face's reference.
388     mHarfBuzzFont = hb_ft_font_create_referenced(mFreeTypeFace);
389
390     SetHarfBuzzFunctions();
391
392     if(mHarfBuzzFont)
393     {
394       DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "FontClient::Plugin::HarfBuzzManager::GetHarfBuzzFont. Create new harfbuzz font : %p freetype face : %p. Requested point size : %u, dpi : horizon %u vertial %u\n", mHarfBuzzFont, mFreeTypeFace, requestedPointSize, horizontalDpi, verticalDpi);
395     }
396     else
397     {
398       DALI_LOG_ERROR("ERROR! failed to create harfbuzz font.");
399     }
400   }
401   else
402   {
403     DALI_LOG_ERROR("ERROR! freetype face is null! something unknown problem occured.");
404   }
405 }
406
407 void HarfBuzzProxyFont::Impl::SetHarfBuzzFunctions()
408 {
409   if(mHarfBuzzFont)
410   {
411     hb_font_funcs_t* customFunctions = hb_font_funcs_create();
412
413     if(customFunctions)
414     {
415       // Bind custom functions here
416       hb_font_funcs_set_font_h_extents_func(customFunctions, FontExtentsFunc, 0, 0);
417       hb_font_funcs_set_font_v_extents_func(customFunctions, FontExtentsFunc, 0, 0);
418
419       hb_font_funcs_set_nominal_glyph_func(customFunctions, GlyphNormalIndexConvertFunc, 0, 0);
420       hb_font_funcs_set_variation_glyph_func(customFunctions, GlyphVariantIndexConvertFunc, 0, 0);
421
422       hb_font_funcs_set_glyph_h_advance_func(customFunctions, GlyphHorizontalAdvanceFunc, 0, 0);
423       hb_font_funcs_set_glyph_v_advance_func(customFunctions, GlyphVerticalAdvanceFunc, 0, 0);
424       hb_font_funcs_set_glyph_extents_func(customFunctions, GlyphExtentsFunc, 0, 0);
425
426       hb_font_funcs_set_glyph_h_origin_func(customFunctions, GlyphHorizontalOriginFunc, 0, 0);
427       hb_font_funcs_set_glyph_v_origin_func(customFunctions, GlyphVerticalOriginFunc, 0, 0);
428       hb_font_funcs_set_glyph_h_kerning_func(customFunctions, GlyphHorizontalKerningFunc, 0, 0);
429       hb_font_funcs_set_glyph_v_kerning_func(customFunctions, GlyphVerticalKerningFunc, 0, 0);
430
431       // Set custom functions into our own harfbuzz font
432       hb_font_set_funcs(mHarfBuzzFont, customFunctions, this, 0);
433
434       // We must release functions type what we create.
435       hb_font_funcs_destroy(customFunctions);
436     }
437     else
438     {
439       DALI_LOG_ERROR("ERROR! Fail to create custom harfbuzz functions.");
440
441       // Something wrong while create harfbuzz font. Destory it.
442       // It will reduce reference of freetype face automatically.
443       hb_font_destroy(mHarfBuzzFont);
444       mHarfBuzzFont = nullptr;
445     }
446   }
447 }
448
449 } // namespace Dali::TextAbstraction::Internal