2 * Copyright (c) 2021 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.
17 #include <dali/integration-api/debug.h>
18 #include <dali/internal/text/text-abstraction/plugin/font-client-utils.h>
19 #include <dali/internal/text/text-abstraction/plugin/font-face-cache-item.h>
21 #if defined(DEBUG_ENABLED)
22 extern Dali::Integration::Log::Filter* gFontClientLogFilter;
25 namespace Dali::TextAbstraction::Internal
27 const float FROM_266 = 1.0f / 64.0f;
28 const float POINTS_PER_INCH = 72.f;
30 FontFaceCacheItem::FontFaceCacheItem(FT_Library& freeTypeLibrary,
33 PointSize26Dot6 requestedPointSize,
35 const FontMetrics& metrics)
36 : mFreeTypeLibrary(freeTypeLibrary),
37 mFreeTypeFace(ftFace),
39 mRequestedPointSize(requestedPointSize),
42 mCharacterSet(nullptr),
44 mFixedWidthPixels(0.f),
45 mFixedHeightPixels(0.f),
48 mIsFixedSizeBitmap(false),
49 mHasColorTables(false)
53 FontFaceCacheItem::FontFaceCacheItem(FT_Library& freeTypeLibrary,
56 PointSize26Dot6 requestedPointSize,
58 const FontMetrics& metrics,
63 : mFreeTypeLibrary(freeTypeLibrary),
64 mFreeTypeFace(ftFace),
66 mRequestedPointSize(requestedPointSize),
69 mCharacterSet(nullptr),
70 mFixedSizeIndex(fixedSizeIndex),
71 mFixedWidthPixels(fixedWidth),
72 mFixedHeightPixels(fixedHeight),
75 mIsFixedSizeBitmap(true),
76 mHasColorTables(hasColorTables)
80 void FontFaceCacheItem::GetFontMetrics(FontMetrics& metrics, unsigned int dpiVertical) const
84 // Adjust the metrics if the fixed-size font should be down-scaled
85 if(mIsFixedSizeBitmap)
87 const float desiredFixedSize = static_cast<float>(mRequestedPointSize) * FROM_266 / POINTS_PER_INCH * dpiVertical;
89 if(desiredFixedSize > 0.f)
91 const float scaleFactor = desiredFixedSize / mFixedHeightPixels;
93 metrics.ascender = metrics.ascender * scaleFactor;
94 metrics.descender = metrics.descender * scaleFactor;
95 metrics.height = metrics.height * scaleFactor;
96 metrics.underlinePosition = metrics.underlinePosition * scaleFactor;
97 metrics.underlineThickness = metrics.underlineThickness * scaleFactor;
102 bool FontFaceCacheItem::GetGlyphMetrics(GlyphInfo& glyph, unsigned int dpiVertical, bool horizontal) const
106 FT_Face ftFace = mFreeTypeFace;
108 #ifdef FREETYPE_BITMAP_SUPPORT
109 // Check to see if we should be loading a Fixed Size bitmap?
110 if(mIsFixedSizeBitmap)
112 FT_Select_Size(ftFace, mFixedSizeIndex); ///< @todo: needs to be investigated why it's needed to select the size again.
113 int error = FT_Load_Glyph(ftFace, glyph.index, FT_LOAD_COLOR);
114 if(FT_Err_Ok == error)
116 glyph.width = mFixedWidthPixels;
117 glyph.height = mFixedHeightPixels;
118 glyph.advance = mFixedWidthPixels;
119 glyph.xBearing = 0.0f;
120 glyph.yBearing = mFixedHeightPixels;
122 // Adjust the metrics if the fixed-size font should be down-scaled
123 const float desiredFixedSize = static_cast<float>(mRequestedPointSize) * FROM_266 / POINTS_PER_INCH * dpiVertical;
125 if(desiredFixedSize > 0.f)
127 const float scaleFactor = desiredFixedSize / mFixedHeightPixels;
129 glyph.width = glyph.width * scaleFactor;
130 glyph.height = glyph.height * scaleFactor;
131 glyph.advance = glyph.advance * scaleFactor;
132 glyph.xBearing = glyph.xBearing * scaleFactor;
133 glyph.yBearing = glyph.yBearing * scaleFactor;
135 glyph.scaleFactor = scaleFactor;
140 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "FontClient::Plugin::GetBitmapMetrics. FreeType Bitmap Load_Glyph error %d\n", error);
147 // FT_LOAD_DEFAULT causes some issues in the alignment of the glyph inside the bitmap.
148 // i.e. with the SNum-3R font.
149 // @todo: add an option to use the FT_LOAD_DEFAULT if required?
150 int error = FT_Load_Glyph(ftFace, glyph.index, FT_LOAD_NO_AUTOHINT);
152 // Keep the width of the glyph before doing the software emboldening.
153 // It will be used to calculate a scale factor to be applied to the
154 // advance as Harfbuzz doesn't apply any SW emboldening to calculate
155 // the advance of the glyph.
156 const float width = static_cast<float>(ftFace->glyph->metrics.width) * FROM_266;
158 if(FT_Err_Ok == error)
160 const bool isEmboldeningRequired = glyph.isBoldRequired && !(ftFace->style_flags & FT_STYLE_FLAG_BOLD);
161 if(isEmboldeningRequired)
163 // Does the software bold.
164 FT_GlyphSlot_Embolden(ftFace->glyph);
167 glyph.width = static_cast<float>(ftFace->glyph->metrics.width) * FROM_266;
168 glyph.height = static_cast<float>(ftFace->glyph->metrics.height) * FROM_266;
171 glyph.xBearing += static_cast<float>(ftFace->glyph->metrics.horiBearingX) * FROM_266;
172 glyph.yBearing += static_cast<float>(ftFace->glyph->metrics.horiBearingY) * FROM_266;
176 glyph.xBearing += static_cast<float>(ftFace->glyph->metrics.vertBearingX) * FROM_266;
177 glyph.yBearing += static_cast<float>(ftFace->glyph->metrics.vertBearingY) * FROM_266;
180 if(isEmboldeningRequired && !Dali::EqualsZero(width))
182 // If the glyph is emboldened by software, the advance is multiplied by a
183 // scale factor to make it slightly bigger.
184 glyph.advance *= (glyph.width / width);
187 // Use the bounding box of the bitmap to correct the metrics.
188 // For some fonts i.e the SNum-3R the metrics need to be corrected,
189 // otherwise the glyphs 'dance' up and down depending on the
190 // font's point size.
193 error = FT_Get_Glyph(ftFace->glyph, &ftGlyph);
196 FT_Glyph_Get_CBox(ftGlyph, FT_GLYPH_BBOX_GRIDFIT, &bbox);
198 const float descender = glyph.height - glyph.yBearing;
199 glyph.height = (bbox.yMax - bbox.yMin) * FROM_266;
200 glyph.yBearing = glyph.height - round(descender);
202 // Created FT_Glyph object must be released with FT_Done_Glyph
203 FT_Done_Glyph(ftGlyph);
214 * @brief Create a bitmap representation of a glyph from a face font
216 * @param[in] glyphIndex The index of a glyph within the specified font.
217 * @param[in] isItalicRequired Whether the glyph requires italic style.
218 * @param[in] isBoldRequired Whether the glyph requires bold style.
219 * @param[out] data The bitmap data.
220 * @param[in] outlineWidth The width of the glyph outline in pixels.
222 void FontFaceCacheItem::CreateBitmap(
223 GlyphIndex glyphIndex, Dali::TextAbstraction::FontClient::GlyphBufferData& data, int outlineWidth, bool isItalicRequired, bool isBoldRequired) const
225 FT_Face ftFace = mFreeTypeFace;
227 // For the software italics.
228 bool isShearRequired = false;
230 #ifdef FREETYPE_BITMAP_SUPPORT
231 // Check to see if this is fixed size bitmap
232 if(mIsFixedSizeBitmap)
234 error = FT_Load_Glyph(ftFace, glyphIndex, FT_LOAD_COLOR);
239 // FT_LOAD_DEFAULT causes some issues in the alignment of the glyph inside the bitmap.
240 // i.e. with the SNum-3R font.
241 // @todo: add an option to use the FT_LOAD_DEFAULT if required?
242 error = FT_Load_Glyph(ftFace, glyphIndex, FT_LOAD_NO_AUTOHINT);
244 if(FT_Err_Ok == error)
246 if(isBoldRequired && !(ftFace->style_flags & FT_STYLE_FLAG_BOLD))
248 // Does the software bold.
249 FT_GlyphSlot_Embolden(ftFace->glyph);
252 if(isItalicRequired && !(ftFace->style_flags & FT_STYLE_FLAG_ITALIC))
254 // Will do the software italic.
255 isShearRequired = true;
259 error = FT_Get_Glyph(ftFace->glyph, &glyph);
261 // Convert to bitmap if necessary
262 if(FT_Err_Ok == error)
264 if(glyph->format != FT_GLYPH_FORMAT_BITMAP)
266 int offsetX = 0, offsetY = 0;
267 bool isOutlineGlyph = (glyph->format == FT_GLYPH_FORMAT_OUTLINE && outlineWidth > 0);
269 // Create a bitmap for the outline
272 // Retrieve the horizontal and vertical distance from the current pen position to the
273 // left and top border of the glyph bitmap for a normal glyph before applying the outline.
274 if(FT_Err_Ok == error)
276 FT_Glyph normalGlyph;
277 error = FT_Get_Glyph(ftFace->glyph, &normalGlyph);
279 error = FT_Glyph_To_Bitmap(&normalGlyph, FT_RENDER_MODE_NORMAL, 0, 1);
280 if(FT_Err_Ok == error)
282 FT_BitmapGlyph bitmapGlyph = reinterpret_cast<FT_BitmapGlyph>(normalGlyph);
284 offsetX = bitmapGlyph->left;
285 offsetY = bitmapGlyph->top;
288 // Created FT_Glyph object must be released with FT_Done_Glyph
289 FT_Done_Glyph(normalGlyph);
292 // Now apply the outline
296 error = FT_Stroker_New(mFreeTypeLibrary, &stroker);
298 if(FT_Err_Ok == error)
300 FT_Stroker_Set(stroker, outlineWidth * 64, FT_STROKER_LINECAP_ROUND, FT_STROKER_LINEJOIN_ROUND, 0);
301 error = FT_Glyph_StrokeBorder(&glyph, stroker, 0, 1);
303 if(FT_Err_Ok == error)
305 FT_Stroker_Done(stroker);
309 DALI_LOG_ERROR("FT_Glyph_StrokeBorder Failed with error: %d\n", error);
314 DALI_LOG_ERROR("FT_Stroker_New Failed with error: %d\n", error);
318 error = FT_Glyph_To_Bitmap(&glyph, FT_RENDER_MODE_NORMAL, 0, 1);
319 if(FT_Err_Ok == error)
321 FT_BitmapGlyph bitmapGlyph = reinterpret_cast<FT_BitmapGlyph>(glyph);
325 // Calculate the additional horizontal and vertical offsets needed for the position of the outline glyph
326 data.outlineOffsetX = offsetX - bitmapGlyph->left - outlineWidth;
327 data.outlineOffsetY = bitmapGlyph->top - offsetY - outlineWidth;
330 ConvertBitmap(data, bitmapGlyph->bitmap, isShearRequired);
334 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "FontClient::Plugin::CreateBitmap. FT_Get_Glyph Failed with error: %d\n", error);
339 ConvertBitmap(data, ftFace->glyph->bitmap, isShearRequired);
342 data.isColorEmoji = mIsFixedSizeBitmap;
344 // Created FT_Glyph object must be released with FT_Done_Glyph
345 FT_Done_Glyph(glyph);
350 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "FontClient::Plugin::CreateBitmap. FT_Load_Glyph Failed with error: %d\n", error);
354 bool FontFaceCacheItem::IsColorGlyph(GlyphIndex glyphIndex) const
358 #ifdef FREETYPE_BITMAP_SUPPORT
359 // Check to see if this is fixed size bitmap
362 error = FT_Load_Glyph(mFreeTypeFace, glyphIndex, FT_LOAD_COLOR);
365 return FT_Err_Ok == error;
369 * Check if the character is supported by this font
370 * @param[in] character The character to test
372 bool FontFaceCacheItem::IsCharacterSupported(Character character)
374 if(nullptr == mCharacterSet)
376 // Create again the character set.
377 // It can be null if the ResetSystemDefaults() method has been called.
379 FontDescription description;
380 description.path = mPath;
381 description.family = std::move(FontFamily(mFreeTypeFace->family_name));
382 description.weight = FontWeight::NONE;
383 description.width = FontWidth::NONE;
384 description.slant = FontSlant::NONE;
386 // Note FreeType doesn't give too much info to build a proper font style.
387 if(mFreeTypeFace->style_flags & FT_STYLE_FLAG_ITALIC)
389 description.slant = FontSlant::ITALIC;
391 if(mFreeTypeFace->style_flags & FT_STYLE_FLAG_BOLD)
393 description.weight = FontWeight::BOLD;
396 mCharacterSet = FcCharSetCopy(CreateCharacterSetFromDescription(description));
399 return FcCharSetHasChar(mCharacterSet, character);
402 GlyphIndex FontFaceCacheItem::GetGlyphIndex(Character character) const
404 return FT_Get_Char_Index(mFreeTypeFace, character);
407 } // namespace Dali::TextAbstraction::Internal