2 * Copyright (c) 2022 Samsung Electronics Co., Ltd.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
18 #include <dali/integration-api/debug.h>
19 #include <dali/internal/text/text-abstraction/plugin/harfbuzz-proxy-font.h>
23 #include <harfbuzz/hb-ft.h>
24 #include <harfbuzz/hb.h>
26 #if defined(DEBUG_ENABLED)
27 extern Dali::Integration::Log::Filter* gFontClientLogFilter;
30 namespace Dali::TextAbstraction::Internal
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.
36 struct HarfBuzzProxyFont::Impl
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.
44 Impl(FT_Face freeTypeFace, GlyphCacheManager* glyphCacheManager)
45 : mFreeTypeFace(freeTypeFace),
46 mGlyphCacheManager(glyphCacheManager),
47 mHarfBuzzFont(nullptr)
56 // It will reduce reference of freetype face automatically.
57 hb_font_destroy(mHarfBuzzFont);
63 * @brief Create new harfbuzz font.
65 * @param[in] requestedPointSize The requiested point size of font.
66 * @param[in] horizontalDpi Horizontal DPI.
67 * @param[in] verticalDpi Vertical DPI.
69 void CreateHarfBuzzFont(const PointSize26Dot6& requestedPointSize, const uint32_t& horizontalDpi, const uint32_t& verticalDpi);
73 * @brief Register harfbuzz callback functions into current harfbuzz font.
75 void SetHarfBuzzFunctions();
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.
81 hb_font_t* mHarfBuzzFont; ///< Harfbuzz font handle integrated with FT_Face.
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))
89 mImpl->CreateHarfBuzzFont(requestedPointSize, mHorizontalDpi, mVerticalDpi);
92 HarfBuzzProxyFont::~HarfBuzzProxyFont()
100 HarfBuzzFontHandle HarfBuzzProxyFont::GetHarfBuzzFont() const
104 return static_cast<HarfBuzzFontHandle>(mImpl->mHarfBuzzFont);
109 // Collection of harfbuzz custom callback functions.
110 // Reference : https://github.com/harfbuzz/harfbuzz/blob/main/src/hb-ft.cc
114 * @brief Get glyph informations by dali glyph cache system.
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] glyphData The result of cached glyph data.
119 * @return True if we success to get some glyph data. False otherwise.
121 static bool GetGlyphCacheData(void* font_data, const GlyphIndex& glyphIndex, GlyphCacheManager::GlyphCacheData& glyphData)
123 HarfBuzzProxyFont::Impl* impl = reinterpret_cast<HarfBuzzProxyFont::Impl*>(font_data);
125 // Note : HarfBuzz used only FT_LOAD_DEFAULT | FT_LOAD_NO_HINTING internally.
126 if(DALI_LIKELY(impl && impl->mGlyphCacheManager))
129 return impl->mGlyphCacheManager->GetGlyphCacheDataFromIndex(impl->mFreeTypeFace, glyphIndex, FT_LOAD_DEFAULT | FT_LOAD_NO_HINTING, false, glyphData, error);
135 * @brief Calculate font extents value both in vertical and horizontal.
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.
143 static hb_bool_t FontExtentsFunc(hb_font_t* font, void* font_data, hb_font_extents_t* extents, void* user_data)
145 HarfBuzzProxyFont::Impl* impl = reinterpret_cast<HarfBuzzProxyFont::Impl*>(font_data);
147 if(DALI_LIKELY(impl && impl->mFreeTypeFace))
149 FT_Size_Metrics& ftMetrics = impl->mFreeTypeFace->size->metrics;
151 extents->ascender = ftMetrics.ascender;
152 extents->descender = ftMetrics.descender;
153 extents->line_gap = ftMetrics.height - (extents->ascender - extents->descender);
161 * @brief Convert from character into index of glyph.
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.
170 static hb_bool_t GlyphNormalIndexConvertFunc(hb_font_t* font, void* font_data, hb_codepoint_t character, hb_codepoint_t* glyphIndex, void* user_data)
172 HarfBuzzProxyFont::Impl* impl = reinterpret_cast<HarfBuzzProxyFont::Impl*>(font_data);
174 if(DALI_LIKELY(impl && impl->mFreeTypeFace))
176 *glyphIndex = FT_Get_Char_Index(impl->mFreeTypeFace, character);
177 return *glyphIndex != 0;
183 * @brief Convert from character and variant selector into index of glyph.
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.
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)
195 HarfBuzzProxyFont::Impl* impl = reinterpret_cast<HarfBuzzProxyFont::Impl*>(font_data);
197 if(DALI_LIKELY(impl && impl->mFreeTypeFace))
199 *glyphIndex = FT_Face_GetCharVariantIndex(impl->mFreeTypeFace, character, variantSelector);
200 return *glyphIndex != 0;
206 * @brief Calculate glyph advance value in horizontal.
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)
214 static hb_position_t GlyphHorizontalAdvanceFunc(hb_font_t* font, void* font_data, hb_codepoint_t glyphIndex, void* user_data)
216 // Output data stored here.
217 GlyphCacheManager::GlyphCacheData glyphData;
218 if(GetGlyphCacheData(font_data, static_cast<GlyphIndex>(glyphIndex), glyphData))
220 // Note : It may return invalid value for fixed size bitmap glyph.
221 // But, Harfbuzz library also return Undefined advanced value if it is fixed size font.
222 // So we'll also ignore that case.
223 return static_cast<hb_position_t>(glyphData.mGlyphMetrics.horiAdvance);
228 * @brief Calculate glyph advance value in vertical.
230 * @param[in] font Current harfbuzz font data.
231 * @param[in] font_data HarfBuzzProxyFont::Impl pointer that register this callback as void* type.
232 * @param[in] glyphIndex Index of glyph.
233 * @param[in] user_data Registered user data.
234 * @return Vertical advance value of glyphIndex. (scale as 26.6)
236 static hb_position_t GlyphVerticalAdvanceFunc(hb_font_t* font, void* font_data, hb_codepoint_t glyphIndex, void* user_data)
238 // Output data stored here.
239 GlyphCacheManager::GlyphCacheData glyphData;
240 if(GetGlyphCacheData(font_data, static_cast<GlyphIndex>(glyphIndex), glyphData))
242 // Note : It may return invalid value for fixed size bitmap glyph.
243 // But, Harfbuzz library also return Undefined advanced value if it is fixed size font.
244 // So we'll also ignore that case.
245 return static_cast<hb_position_t>(glyphData.mGlyphMetrics.vertAdvance);
251 * @brief Calculate glyph origin position value in horizontal.
253 * @param[in] font Current harfbuzz font data.
254 * @param[in] font_data HarfBuzzProxyFont::Impl that register this callback as void* type.
255 * @param[in] glyphIndex Index of glyph.
256 * @param[out] x Origin position x (scale as 26.6)
257 * @param[out] y Origin position y (scale as 26.6)
258 * @param[in] user_data Registered user data.
259 * @return True if we get data successfully. False if some error occured.
261 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)
267 * @brief Calculate glyph origin position value in vertical.
269 * @param[in] font Current harfbuzz font data.
270 * @param[in] font_data HarfBuzzProxyFont::Impl pointer that register this callback as void* type.
271 * @param[in] glyphIndex Index of glyph.
272 * @param[out] x Origin position x (scale as 26.6)
273 * @param[out] y Origin position y (scale as 26.6)
274 * @param[in] user_data Registered user data.
275 * @return True if we get data successfully. False if some error occured.
277 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)
279 // Output data stored here.
280 GlyphCacheManager::GlyphCacheData glyphData;
281 if(GetGlyphCacheData(font_data, static_cast<GlyphIndex>(glyphIndex), glyphData))
283 *x = glyphData.mGlyphMetrics.horiBearingX - glyphData.mGlyphMetrics.vertBearingX;
284 *y = glyphData.mGlyphMetrics.horiBearingY + glyphData.mGlyphMetrics.vertBearingY;
291 * @brief Calculate glyph kerning value in horizontal.
293 * @param[in] font Current harfbuzz font data.
294 * @param[in] font_data HarfBuzzProxyFont::Impl pointer that register this callback as void* type.
295 * @param[in] glyphIndex1 First index of glyph to get kerning.
296 * @param[in] glyphIndex2 Second index of glyph to get kerning.
297 * @param[in] user_data Registered user data.
298 * @return Horizontal kerning position. (scale as 26.6)
300 static hb_position_t GlyphHorizontalKerningFunc(hb_font_t* font, void* font_data, hb_codepoint_t glyphIndex1, hb_codepoint_t glyphIndex2, void* user_data)
302 HarfBuzzProxyFont::Impl* impl = reinterpret_cast<HarfBuzzProxyFont::Impl*>(font_data);
304 if(DALI_LIKELY(impl && impl->mFreeTypeFace))
309 error = FT_Get_Kerning(impl->mFreeTypeFace, glyphIndex1, glyphIndex2, FT_KERNING_UNSCALED, &kerning);
310 if(error == FT_Err_Ok)
318 * @brief Calculate glyph kerning value in vertical.
320 * @param[in] font Current harfbuzz font data.
321 * @param[in] font_data HarfBuzzProxyFont::Impl pointer that register this callback as void* type.
322 * @param[in] glyphIndex1 First index of glyph to get kerning.
323 * @param[in] glyphIndex2 Second index of glyph to get kerning.
324 * @param[in] user_data Registered user data.
325 * @return Vertical kerning position. (scale as 26.6)
327 static hb_position_t GlyphVerticalKerningFunc(hb_font_t* font, void* font_data, hb_codepoint_t glyphIndex1, hb_codepoint_t glyphIndex2, void* user_data)
329 // FreeType doesn't support vertical kerning
334 * @brief Calculate glyph extents.
336 * @param[in] font Current harfbuzz font data.
337 * @param[in] font_data HarfBuzzProxyFont::Impl pointer that register this callback as void* type.
338 * @param[in] glyphIndex Index of glyph.
339 * @param[out] extents Extents value of glyph. (scale as 26.6)
340 * @param[in] user_data Registered user data.
341 * @return True if we get data successfully. False if some error occured.
343 static hb_bool_t GlyphExtentsFunc(hb_font_t* font, void* font_data, hb_codepoint_t glyphIndex, hb_glyph_extents_t* extents, void* user_data)
345 // Output data stored here.
346 GlyphCacheManager::GlyphCacheData glyphData;
347 if(!GetGlyphCacheData(font_data, static_cast<GlyphIndex>(glyphIndex), glyphData))
349 extents->x_bearing = glyphData.mGlyphMetrics.horiBearingX;
350 extents->y_bearing = glyphData.mGlyphMetrics.horiBearingY;
351 extents->width = glyphData.mGlyphMetrics.width;
352 extents->height = glyphData.mGlyphMetrics.height;
360 void HarfBuzzProxyFont::Impl::CreateHarfBuzzFont(const PointSize26Dot6& requestedPointSize, const uint32_t& horizontalDpi, const uint32_t& verticalDpi)
362 // Destroy previous hb_font_t if exist.
365 // It will reduce reference of freetype face automatically.
366 hb_font_destroy(mHarfBuzzFont);
367 mHarfBuzzFont = nullptr;
372 // Before create hb_font_t, we must set FT_Char_Size
373 FT_Set_Char_Size(mFreeTypeFace,
379 // Create font face with increase font face's reference.
380 mHarfBuzzFont = hb_ft_font_create_referenced(mFreeTypeFace);
382 SetHarfBuzzFunctions();
386 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);
390 DALI_LOG_ERROR("ERROR! failed to create harfbuzz font.");
395 DALI_LOG_ERROR("ERROR! freetype face is null! something unknown problem occured.");
399 void HarfBuzzProxyFont::Impl::SetHarfBuzzFunctions()
403 hb_font_funcs_t* customFunctions = hb_font_funcs_create();
407 // Bind custom functions here
408 hb_font_funcs_set_font_h_extents_func(customFunctions, FontExtentsFunc, 0, 0);
409 hb_font_funcs_set_font_v_extents_func(customFunctions, FontExtentsFunc, 0, 0);
411 hb_font_funcs_set_nominal_glyph_func(customFunctions, GlyphNormalIndexConvertFunc, 0, 0);
412 hb_font_funcs_set_variation_glyph_func(customFunctions, GlyphVariantIndexConvertFunc, 0, 0);
414 hb_font_funcs_set_glyph_h_advance_func(customFunctions, GlyphHorizontalAdvanceFunc, 0, 0);
415 hb_font_funcs_set_glyph_v_advance_func(customFunctions, GlyphVerticalAdvanceFunc, 0, 0);
416 hb_font_funcs_set_glyph_extents_func(customFunctions, GlyphExtentsFunc, 0, 0);
418 hb_font_funcs_set_glyph_h_origin_func(customFunctions, GlyphHorizontalOriginFunc, 0, 0);
419 hb_font_funcs_set_glyph_v_origin_func(customFunctions, GlyphVerticalOriginFunc, 0, 0);
420 hb_font_funcs_set_glyph_h_kerning_func(customFunctions, GlyphHorizontalKerningFunc, 0, 0);
421 hb_font_funcs_set_glyph_v_kerning_func(customFunctions, GlyphVerticalKerningFunc, 0, 0);
423 // Set custom functions into our own harfbuzz font
424 hb_font_set_funcs(mHarfBuzzFont, customFunctions, this, 0);
426 // We must release functions type what we create.
427 hb_font_funcs_destroy(customFunctions);
431 DALI_LOG_ERROR("ERROR! Fail to create custom harfbuzz functions.");
433 // Something wrong while create harfbuzz font. Destory it.
434 // It will reduce reference of freetype face automatically.
435 hb_font_destroy(mHarfBuzzFont);
436 mHarfBuzzFont = nullptr;
441 } // namespace Dali::TextAbstraction::Internal