LCOV - code coverage report
Current view: top level - muse/framework/draw/internal - fontfaceft.cpp Coverage Total Hit
Test: filtered_coverage.info Lines: 69.9 % 256 179
Test Date: 2026-11-05 04:07:02

            Line data    Source code
       1              : /*
       2              :  * SPDX-License-Identifier: GPL-3.0-only
       3              :  * MuseScore-CLA-applies
       4              :  *
       5              :  * MuseScore Studio
       6              :  * Music Composition & Notation
       7              :  *
       8              :  * Copyright (C) 2024 MuseScore Limited and others
       9              :  *
      10              :  * This program is free software: you can redistribute it and/or modify
      11              :  * it under the terms of the GNU General Public License version 3 as
      12              :  * published by the Free Software Foundation.
      13              :  *
      14              :  * This program is distributed in the hope that it will be useful,
      15              :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      16              :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      17              :  * GNU General Public License for more details.
      18              :  *
      19              :  * You should have received a copy of the GNU General Public License
      20              :  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
      21              :  */
      22              : #include "fontfaceft.h"
      23              : 
      24              : #include <cmath>
      25              : #include <unordered_map>
      26              : 
      27              : #include <ft2build.h>
      28              : #include FT_FREETYPE_H
      29              : #include FT_GLYPH_H
      30              : #include FT_BBOX_H
      31              : #include FT_TRUETYPE_TABLES_H
      32              : #include <hb-ft.h>
      33              : 
      34              : #ifndef MUSE_MODULE_DRAW_USE_QTTEXTDRAW
      35              : #include <msdfgen.h>
      36              : #include <ext/import-font.h>
      37              : #endif
      38              : 
      39              : #include "global/types/bytearray.h"
      40              : 
      41              : #include "log.h"
      42              : 
      43              : using namespace muse::draw;
      44              : 
      45              : static const hb_tag_t KernTag = HB_TAG('k', 'e', 'r', 'n'); // kerning operations
      46              : static const hb_tag_t LigaTag = HB_TAG('l', 'i', 'g', 'a'); // standard ligature substitution
      47              : static const hb_tag_t CligTag = HB_TAG('c', 'l', 'i', 'g'); // contextual ligature substitution
      48              : static const hb_tag_t DligTag = HB_TAG('d', 'l', 'i', 'g'); // contextual ligature substitution
      49              : static const hb_tag_t HligTag = HB_TAG('h', 'l', 'i', 'g'); // contextual ligature substitution
      50              : 
      51              : static const std::vector<hb_feature_t> HB_FEATURES = {
      52              :     { KernTag, 1, HB_FEATURE_GLOBAL_START, HB_FEATURE_GLOBAL_END },
      53              :     { LigaTag, 1, HB_FEATURE_GLOBAL_START, HB_FEATURE_GLOBAL_END },
      54              :     { CligTag, 1, HB_FEATURE_GLOBAL_START, HB_FEATURE_GLOBAL_END },
      55              :     { DligTag, 1, HB_FEATURE_GLOBAL_START, HB_FEATURE_GLOBAL_END },
      56              :     { HligTag, 1, HB_FEATURE_GLOBAL_START, HB_FEATURE_GLOBAL_END }
      57              : };
      58              : 
      59              : static FT_Library ftlib = nullptr;
      60          110 : static bool _init_ft()
      61              : {
      62          110 :     int error = 0;
      63          110 :     if (!ftlib) {
      64            1 :         error = FT_Init_FreeType(&ftlib);
      65            1 :         if (!ftlib || error) {
      66            0 :             LOGE() << "init freetype library failed";
      67              :         }
      68              :     }
      69          110 :     return error == 0;
      70              : }
      71              : 
      72              : #define FLOOR(x)    ((x) & -64)
      73              : #define CEIL(x)     (((x) + 63) & -64)
      74              : #define TRUNC(x)    ((x) >> 6)
      75              : #define ROUND(x)    (((x) + 32) & -64)
      76              : 
      77              : struct muse::draw::GlyphMetrics
      78              : {
      79              :     FBBox bbox;
      80              :     FT_Fixed linearAdvance = 0;
      81              : };
      82              : 
      83              : struct muse::draw::SymbolMetrics
      84              : {
      85              :     glyph_idx_t idx = 0;
      86              :     FT_BBox bbox;
      87              :     FT_Fixed linearAdvance = 0;
      88              : };
      89              : 
      90              : struct HeightMetrics
      91              : {
      92              :     f26dot6_t ascent = 0;
      93              :     f26dot6_t descent = 0;
      94              :     f26dot6_t leading = 0;
      95              : };
      96              : 
      97              : struct muse::draw::FData
      98              : {
      99              :     ByteArray fontData;
     100              :     FT_Face face = nullptr;
     101              :     hb_font_t* hb_font = nullptr;
     102              :     std::unordered_map<glyph_idx_t, GlyphMetrics> glyphsMetrics;
     103              :     std::unordered_map<glyph_idx_t, SymbolMetrics> symbolMetrics;
     104              :     FT_Size_Metrics metrics;
     105              :     HeightMetrics heightMetrics;
     106              : };
     107              : 
     108              : #ifndef MUSE_MODULE_DRAW_USE_QTTEXTDRAW
     109              : static const int SDF_DIM = 28; //! default dimension of generated SDF. Real SDF can have another size
     110              : static const int SDF_SHAPE_SIZE = 100; //! effective shape size
     111              : static const double MIN_SDF_OUTLINE_PIXELS = 7.0;
     112              : static const double MAX_SDF_SCALE = 4.0;
     113              : 
     114              : static double sdfScaleForShape(const msdfgen::Vector2& shapeDims, double baseScale)
     115              : {
     116              :     double minDim = std::min(shapeDims.x, shapeDims.y);
     117              :     if (minDim <= 0.0) {
     118              :         return baseScale;
     119              :     }
     120              : 
     121              :     return std::min(std::max(baseScale, MIN_SDF_OUTLINE_PIXELS / minDim), MAX_SDF_SCALE);
     122              : }
     123              : 
     124              : static void generateSdf(GlyphImage& out, msdfgen::Shape& shape)
     125              : {
     126              :     struct Bounds
     127              :     {
     128              :         double l, b, r, t;
     129              :     };
     130              :     Bounds bounds = { 1e240, 1e240, -1e240, -1e240 };
     131              : 
     132              :     shape.normalize();
     133              :     shape.orientContours();
     134              : 
     135              :     shape.bound(bounds.l, bounds.b, bounds.r, bounds.t);
     136              :     msdfgen::Vector2 shapeDims(bounds.r - bounds.l, bounds.t - bounds.b);
     137              : 
     138              :     int pxRange = 4;
     139              :     int pixelFrameWidth = 3;
     140              :     double scale = static_cast<double>(SDF_DIM) / SDF_SHAPE_SIZE;
     141              :     scale = sdfScaleForShape(shapeDims, scale);
     142              : 
     143              :     int sdfWidth = static_cast<int>(std::ceil(scale * shapeDims.x));
     144              :     int sdfHeight = static_cast<int>(std::ceil(scale * shapeDims.y));
     145              : 
     146              :     sdfWidth += 2 * pixelFrameWidth;
     147              :     sdfHeight += 2 * pixelFrameWidth;
     148              : 
     149              :     msdfgen::Vector2 translate = { -bounds.l, -bounds.b };
     150              : 
     151              :     double pxRangeScaled = pixelFrameWidth / scale;
     152              : 
     153              :     double left = bounds.l - pxRangeScaled;
     154              :     double top = -bounds.t - pxRangeScaled;
     155              :     double width = shapeDims.x + pxRangeScaled * 2;
     156              :     double height = shapeDims.y + pxRangeScaled * 2;
     157              : 
     158              :     double range = pxRange / scale;
     159              :     translate += pxRangeScaled;
     160              : 
     161              :     auto sdf = msdfgen::Bitmap<float, 1>(sdfWidth, sdfHeight);
     162              :     msdfgen::SDFTransformation t(msdfgen::Projection(scale, translate), msdfgen::Range(range));
     163              :     msdfgen::generateSDF(sdf, shape, t);
     164              :     msdfgen::distanceSignCorrection(sdf, shape, t, msdfgen::FILL_NONZERO);
     165              : 
     166              :     out.sdf.bitmap.reserve(sdfWidth * sdfHeight);
     167              :     for (int y = 0; y < sdf.height(); ++y) {
     168              :         for (int x = 0; x < sdf.width(); ++x) {
     169              :             uint8_t px = static_cast<uint8_t>(msdfgen::pixelFloatToByte(*sdf(x, y)));
     170              :             out.sdf.bitmap.push_back(px);
     171              :         }
     172              :     }
     173              :     out.sdf.width = sdfWidth;
     174              :     out.sdf.height = sdfHeight;
     175              :     out.range = range;
     176              : 
     177              :     out.rect.setTop(top);
     178              :     out.rect.setLeft(left);
     179              :     out.rect.setWidth(width);
     180              :     out.rect.setHeight(height);
     181              : 
     182              :     out.sdf.hash = std::hash<std::string_view> {}({ reinterpret_cast<const char*>(out.sdf.bitmap.data()), out.sdf.bitmap.size() });
     183              : }
     184              : 
     185              : #endif
     186              : 
     187          660 : static f26dot6_t tableValueToF26Dot6(FT_Face face, long value, int pixelSize)
     188              : {
     189          660 :     if (!face || face->units_per_EM == 0) {
     190            0 :         return 0;
     191              :     }
     192              : 
     193          660 :     return static_cast<f26dot6_t>(value * pixelSize * 64.0 / face->units_per_EM);
     194              : }
     195              : 
     196          110 : static bool processHheaTable(FT_Face face, int pixelSize, HeightMetrics& metrics)
     197              : {
     198          110 :     TT_HoriHeader* hhea = (TT_HoriHeader*)FT_Get_Sfnt_Table(face, ft_sfnt_hhea);
     199          110 :     if (!hhea) {
     200            0 :         return false;
     201              :     }
     202              : 
     203          110 :     if (hhea->Ascender == 0 && hhea->Descender == 0) {
     204            0 :         return false;
     205              :     }
     206              : 
     207          110 :     metrics.ascent = tableValueToF26Dot6(face, hhea->Ascender, pixelSize);
     208          110 :     metrics.descent = -tableValueToF26Dot6(face, hhea->Descender, pixelSize);
     209          110 :     metrics.leading = tableValueToF26Dot6(face, hhea->Line_Gap, pixelSize);
     210          110 :     return true;
     211              : }
     212              : 
     213          110 : static bool processOS2Table(FT_Face face, int pixelSize, bool preferTypoLineMetrics, HeightMetrics& metrics)
     214              : {
     215          110 :     TT_OS2* os2 = (TT_OS2*)FT_Get_Sfnt_Table(face, ft_sfnt_os2);
     216          110 :     if (!os2) {
     217            0 :         return false;
     218              :     }
     219              : 
     220              :     enum {
     221              :         USE_TYPO_METRICS = 0x80
     222              :     };
     223          110 :     if (preferTypoLineMetrics || (os2->fsSelection & USE_TYPO_METRICS)) {
     224          110 :         if (os2->sTypoAscender == 0 && os2->sTypoDescender == 0) {
     225            0 :             return false;
     226              :         }
     227              : 
     228          110 :         metrics.ascent = tableValueToF26Dot6(face, os2->sTypoAscender, pixelSize);
     229          110 :         metrics.descent = -tableValueToF26Dot6(face, os2->sTypoDescender, pixelSize);
     230          110 :         metrics.leading = tableValueToF26Dot6(face, os2->sTypoLineGap, pixelSize);
     231              :     } else {
     232            0 :         if (os2->usWinAscent == 0 && os2->usWinDescent == 0) {
     233            0 :             return false;
     234              :         }
     235              : 
     236            0 :         metrics.ascent = tableValueToF26Dot6(face, os2->usWinAscent, pixelSize);
     237            0 :         metrics.descent = tableValueToF26Dot6(face, os2->usWinDescent, pixelSize);
     238            0 :         metrics.leading = 0;
     239              :     }
     240              : 
     241          110 :     return true;
     242              : }
     243              : 
     244          110 : static HeightMetrics calculateHeightMetrics(FData* data, int pixelSize)
     245              : {
     246          110 :     const FT_Size_Metrics& metrics = data->metrics;
     247          110 :     const double scale = static_cast<double>(pixelSize) / metrics.y_ppem;
     248              : 
     249          110 :     HeightMetrics result;
     250          110 :     result.ascent = static_cast<f26dot6_t>(metrics.ascender * scale);
     251          110 :     result.descent = static_cast<f26dot6_t>(-metrics.descender * scale);
     252          110 :     result.leading = static_cast<f26dot6_t>((metrics.height - metrics.ascender + metrics.descender) * scale);
     253              : 
     254          110 :     processHheaTable(data->face, pixelSize, result);
     255          110 :     processOS2Table(data->face, pixelSize, true, result);
     256          110 :     return result;
     257              : }
     258              : 
     259          110 : static void initializeHeightMetrics(FData* data, int pixelSize)
     260              : {
     261          110 :     data->heightMetrics = calculateHeightMetrics(data, pixelSize);
     262          110 : }
     263              : 
     264          110 : FontFaceFT::FontFaceFT()
     265              : {
     266          110 :     m_data = new FData();
     267          110 : }
     268              : 
     269          214 : FontFaceFT::~FontFaceFT()
     270              : {
     271          110 :     if (m_data->hb_font) {
     272          108 :         hb_font_destroy(m_data->hb_font);
     273              :     }
     274          110 :     FT_Done_Face(m_data->face);
     275          110 :     delete m_data;
     276          214 : }
     277              : 
     278          110 : bool FontFaceFT::load(const FaceKey& key, const FontData& fontData, bool isSymbolMode)
     279              : {
     280          110 :     if (!_init_ft()) {
     281            0 :         return false;
     282              :     }
     283              : 
     284          110 :     m_key = key;
     285          110 :     m_isSymbolMode = isSymbolMode;
     286              : 
     287          110 :     if (!fontData.valid()) {
     288            0 :         LOGE() << "empty font data: " << m_key.dataKey.family().id();
     289            0 :         return false;
     290              :     }
     291              : 
     292          110 :     m_data->fontData = fontData.data;
     293              : 
     294          110 :     int rval = FT_New_Memory_Face(ftlib, (FT_Byte*)m_data->fontData.constData(),
     295          110 :                                   (FT_Long)m_data->fontData.size(), 0, &m_data->face);
     296          110 :     if (rval) {
     297            0 :         LOGE() << "freetype: cannot create face: " << m_key.dataKey.family().id() << ", rval: " << rval;
     298            0 :         return false;
     299              :     }
     300              : 
     301          110 :     if (m_isSymbolMode) {
     302            2 :         FT_Set_Pixel_Sizes(m_data->face, 0, int(m_key.pixelSize + .5));
     303              :     } else {
     304          108 :         FT_Set_Char_Size(m_data->face, to_f26d6(m_key.pixelSize), to_f26d6(m_key.pixelSize), 0, 0);
     305              : 
     306              :         static FT_Matrix matrix;
     307          108 :         matrix.xx = 0x10000;
     308          108 :         matrix.yy = 0x10000;
     309          108 :         matrix.xy = 0;
     310          108 :         matrix.yx = 0;
     311          108 :         FT_Set_Transform(m_data->face, &matrix, nullptr);
     312              : 
     313          108 :         m_data->hb_font = hb_ft_font_create(m_data->face, NULL);
     314              :     }
     315              : 
     316          110 :     m_data->metrics = m_data->face->size->metrics;
     317          110 :     initializeHeightMetrics(m_data, m_key.pixelSize);
     318              : 
     319          110 :     return true;
     320              : }
     321              : 
     322         1513 : const FaceKey& FontFaceFT::key() const
     323              : {
     324         1513 :     return m_key;
     325              : }
     326              : 
     327         2312 : bool FontFaceFT::isSymbolMode() const
     328              : {
     329         2312 :     return m_isSymbolMode;
     330              : }
     331              : 
     332          307 : std::vector<ShapedGlyph> FontFaceFT::glyphs(const char32_t* text, int text_length) const
     333              : {
     334          307 :     if (text_length < 1) {
     335            0 :         return std::vector<ShapedGlyph>();
     336              :     }
     337              : 
     338          307 :     std::vector<ShapedGlyph> result;
     339          307 :     if (m_isSymbolMode) {
     340            0 :         for (int i = 0; i < text_length; ++i) {
     341            0 :             glyph_idx_t idx = glyphIndex(text[i]);
     342            0 :             SymbolMetrics* sm = symbolMetrics(idx);
     343            0 :             IF_ASSERT_FAILED(sm) {
     344            0 :                 return std::vector<ShapedGlyph>();
     345              :             }
     346              : 
     347            0 :             ShapedGlyph p;
     348            0 :             p.idx = sm->idx;
     349            0 :             p.x_advance = sm->linearAdvance;
     350            0 :             p.cluster = static_cast<uint32_t>(i);
     351              : 
     352            0 :             result.push_back(std::move(p));
     353              :         }
     354              :     } else {
     355          307 :         hb_buffer_t* hb_buffer = hb_buffer_create();
     356          307 :         hb_segment_properties_t props = HB_SEGMENT_PROPERTIES_DEFAULT;
     357              : 
     358          307 :         hb_buffer_add_utf32(hb_buffer, (uint32_t*)text, text_length, 0, -1);
     359          307 :         hb_buffer_set_direction(hb_buffer, props.direction);
     360          307 :         hb_buffer_set_script(hb_buffer, props.script);
     361              : 
     362          307 :         hb_buffer_set_segment_properties(hb_buffer, &props);
     363          307 :         hb_buffer_guess_segment_properties(hb_buffer);
     364              : 
     365          307 :         hb_shape(m_data->hb_font, hb_buffer, &HB_FEATURES[0], static_cast<unsigned int>(HB_FEATURES.size()));
     366          307 :         unsigned int len = hb_buffer_get_length(hb_buffer);
     367          307 :         result.reserve(len);
     368              : 
     369          307 :         hb_glyph_info_t* info = hb_buffer_get_glyph_infos(hb_buffer, NULL);
     370          307 :         hb_glyph_position_t* pos = hb_buffer_get_glyph_positions(hb_buffer, NULL);
     371              : 
     372         1583 :         for (unsigned int i = 0; i < len; i++) {
     373         1276 :             f26dot6_t xAdvance = static_cast<f26dot6_t>(pos[i].x_advance);
     374         1276 :             hb_position_t hbAdvance = hb_font_get_glyph_h_advance(m_data->hb_font, info[i].codepoint);
     375         1276 :             if (GlyphMetrics* metrics = glyphMetrics(info[i].codepoint)) {
     376         1274 :                 xAdvance += metrics->linearAdvance - hbAdvance;
     377              :             }
     378              : 
     379         1276 :             result.push_back({ info[i].codepoint, xAdvance, info[i].cluster });
     380              :         }
     381              : 
     382          307 :         hb_buffer_destroy(hb_buffer);
     383              :     }
     384              : 
     385          307 :     return result;
     386          307 : }
     387              : 
     388         1664 : glyph_idx_t FontFaceFT::glyphIndex(char32_t ucs4) const
     389              : {
     390         1664 :     if (ucs4 == 0) {
     391            0 :         return 0;
     392              :     }
     393              : 
     394         1664 :     FT_UInt index = FT_Get_Char_Index(m_data->face, ucs4);
     395         1664 :     return static_cast<glyph_idx_t>(index);
     396              : }
     397              : 
     398            0 : glyph_idx_t FontFaceFT::glyphIndex(const std::string& glyphName) const
     399              : {
     400            0 :     FT_UInt index = FT_Get_Name_Index(m_data->face, glyphName.c_str());
     401            0 :     return static_cast<glyph_idx_t>(index);
     402              : }
     403              : 
     404           19 : char32_t FontFaceFT::findCharCode(glyph_idx_t idx) const
     405              : {
     406           19 :     auto findC = [this](glyph_idx_t idx)
     407              :     {
     408           19 :         FT_UInt gindex = 0;
     409           19 :         FT_ULong charcode = FT_Get_First_Char(m_data->face, &gindex);
     410           19 :         if (gindex == idx) {
     411            0 :             return charcode;
     412              :         }
     413              : 
     414         1192 :         while (gindex != 0)
     415              :         {
     416         1192 :             charcode = FT_Get_Next_Char(m_data->face, charcode, &gindex);
     417         1192 :             if (gindex == idx) {
     418           19 :                 return charcode;
     419              :             }
     420              :         }
     421              : 
     422            0 :         return FT_ULong(0);
     423           19 :     };
     424              : 
     425           19 :     char32_t c = findC(idx);
     426              : 
     427              :     // check
     428           19 :     assert(glyphIndex(c) == idx);
     429              : 
     430           19 :     return c;
     431              : }
     432              : 
     433          942 : FBBox FontFaceFT::glyphBbox(glyph_idx_t idx) const
     434              : {
     435          942 :     if (isSymbolMode()) {
     436           58 :         FT_UInt index = static_cast<FT_UInt>(idx);
     437           58 :         if (index == 0) {
     438            0 :             return FBBox();
     439              :         }
     440              : 
     441           58 :         if (FT_Load_Glyph(m_data->face, index, FT_LOAD_NO_BITMAP) != 0) {
     442            0 :             return FBBox();
     443              :         }
     444              : 
     445           58 :         FT_GlyphSlot slot = m_data->face->glyph;
     446           58 :         if (slot->format != FT_GLYPH_FORMAT_OUTLINE) {
     447            0 :             return FBBox();
     448              :         }
     449              : 
     450              :         FT_BBox outlineBox;
     451           58 :         FT_Outline_Get_BBox(&slot->outline, &outlineBox);
     452              : 
     453           58 :         FBBox bbox;
     454           58 :         bbox.setCoords(outlineBox.xMin, -outlineBox.yMax, outlineBox.xMax, -outlineBox.yMin);
     455           58 :         return bbox;
     456              :     } else {
     457          884 :         GlyphMetrics* gm = glyphMetrics(idx);
     458          884 :         if (!gm) {
     459            0 :             return FBBox();
     460              :         }
     461          884 :         return gm->bbox;
     462              :     }
     463              : }
     464              : 
     465          685 : f26dot6_t FontFaceFT::glyphAdvance(glyph_idx_t idx) const
     466              : {
     467          685 :     if (isSymbolMode()) {
     468            0 :         SymbolMetrics* sm = symbolMetrics(idx);
     469            0 :         IF_ASSERT_FAILED(sm) {
     470            0 :             return 0.0;
     471              :         }
     472            0 :         return sm->linearAdvance;
     473              :     } else {
     474          685 :         GlyphMetrics* gm = glyphMetrics(idx);
     475          685 :         if (!gm) {
     476            0 :             return 0.0;
     477              :         }
     478          685 :         return gm->linearAdvance;
     479              :     }
     480              : }
     481              : 
     482            0 : const GlyphImage& FontFaceFT::glyphImage(glyph_idx_t idx) const
     483              : {
     484            0 :     static GlyphImage null;
     485              : 
     486              : #ifndef MUSE_MODULE_DRAW_USE_QTTEXTDRAW
     487              :     FT_UInt index = static_cast<FT_UInt>(idx);
     488              :     if (index == 0) {
     489              :         return null;
     490              :     }
     491              : 
     492              :     auto it = m_cache.find(idx);
     493              :     if (it != m_cache.end()) {
     494              :         return it->second;
     495              :     }
     496              : 
     497              :     if (FT_Load_Glyph(m_data->face, index, FT_LOAD_DEFAULT) != 0) {
     498              :         return null;
     499              :     }
     500              : 
     501              :     msdfgen::Shape shape;
     502              :     msdfgen::readFreetypeOutline(shape, &m_data->face->glyph->outline);
     503              :     if (shape.contours.empty()) {
     504              :         return m_cache.insert({ idx, GlyphImage() }).first->second;
     505              :     }
     506              : 
     507              :     std::pair<glyph_idx_t, GlyphImage> v;
     508              :     v.first = idx;
     509              :     shape.inverseYAxis = true;
     510              :     generateSdf(v.second, shape);
     511              : 
     512              :     return m_cache.insert(std::move(v)).first->second;
     513              : #else
     514              :     UNUSED(idx);
     515            0 :     return null;
     516              : #endif
     517              : }
     518              : 
     519            7 : f26dot6_t FontFaceFT::leading() const
     520              : {
     521            7 :     return m_data->heightMetrics.leading;
     522              : }
     523              : 
     524          121 : f26dot6_t FontFaceFT::ascent() const
     525              : {
     526          121 :     return m_data->heightMetrics.ascent;
     527              : }
     528              : 
     529           67 : f26dot6_t FontFaceFT::descent() const
     530              : {
     531           67 :     return m_data->heightMetrics.descent;
     532              : }
     533              : 
     534            7 : f26dot6_t FontFaceFT::xHeight() const
     535              : {
     536            7 :     TT_OS2* os2 = (TT_OS2*)FT_Get_Sfnt_Table(m_data->face, ft_sfnt_os2);
     537            7 :     if (os2 && os2->sxHeight) {
     538            7 :         f26dot6_t result = std::round(os2->sxHeight * m_data->face->size->metrics.y_ppem * 64.0 / (double)m_data->face->units_per_EM);
     539            7 :         return result;
     540              :     }
     541              : 
     542            0 :     const glyph_idx_t glyph = glyphIndex('x');
     543            0 :     if (glyph == 0) {
     544            0 :         return 0;
     545              :     }
     546              : 
     547            0 :     GlyphMetrics* gm = glyphMetrics(glyph);
     548            0 :     IF_ASSERT_FAILED(gm) {
     549            0 :         return 0;
     550              :     }
     551            0 :     return gm->bbox.height();
     552              : }
     553              : 
     554            1 : f26dot6_t FontFaceFT::capHeight() const
     555              : {
     556            1 :     TT_OS2* os2 = (TT_OS2*)FT_Get_Sfnt_Table(m_data->face, ft_sfnt_os2);
     557            1 :     if (os2 && os2->sCapHeight) {
     558            1 :         f26dot6_t result = std::round(os2->sCapHeight * m_data->face->size->metrics.y_ppem * 64.0 / (double)m_data->face->units_per_EM);
     559            1 :         return result;
     560              :     }
     561              : 
     562            0 :     const glyph_idx_t glyph = glyphIndex('X');
     563            0 :     if (glyph == 0) {
     564            0 :         return 0;
     565              :     }
     566              : 
     567            0 :     GlyphMetrics* gm = glyphMetrics(glyph);
     568            0 :     IF_ASSERT_FAILED(gm) {
     569            0 :         return 0;
     570              :     }
     571            0 :     return gm->bbox.height();
     572              : }
     573              : 
     574            7 : f26dot6_t FontFaceFT::underlinePos() const
     575              : {
     576            7 :     f26dot6_t thickness = FT_MulFix(m_data->face->underline_thickness, m_data->face->size->metrics.y_scale);
     577            7 :     f26dot6_t centerPos = -FT_MulFix(m_data->face->underline_position, m_data->face->size->metrics.y_scale);
     578            7 :     return centerPos - thickness / 2;
     579              : }
     580              : 
     581            7 : f26dot6_t FontFaceFT::lineWidth() const
     582              : {
     583            7 :     return FT_MulFix(m_data->face->underline_thickness, m_data->face->size->metrics.y_scale);
     584              : }
     585              : 
     586         2845 : GlyphMetrics* FontFaceFT::glyphMetrics(glyph_idx_t idx) const
     587              : {
     588         2845 :     if (m_data->glyphsMetrics.find(idx) != m_data->glyphsMetrics.end()) {
     589         1859 :         return &m_data->glyphsMetrics.at(idx);
     590              :     }
     591              : 
     592          986 :     FT_UInt index = static_cast<FT_UInt>(idx);
     593          986 :     if (index == 0) {
     594            2 :         return nullptr;
     595              :     }
     596              : 
     597          984 :     if (FT_Load_Glyph(m_data->face, index, FT_LOAD_DEFAULT | FT_LOAD_NO_BITMAP | FT_LOAD_NO_HINTING) != 0) {
     598            0 :         return nullptr;
     599              :     }
     600              : 
     601          984 :     FT_GlyphSlot slot = m_data->face->glyph;
     602              : 
     603          984 :     GlyphMetrics& gm = m_data->glyphsMetrics[idx];
     604              : 
     605         1968 :     auto floorF26Dot6 = [](FT_Pos value) {
     606         1968 :         return value & -64;
     607              :     };
     608              : 
     609         1968 :     auto ceilF26Dot6 = [](FT_Pos value) {
     610         1968 :         return (value + 63) & -64;
     611              :     };
     612              : 
     613          984 :     FT_Pos left = floorF26Dot6(slot->metrics.horiBearingX);
     614          984 :     FT_Pos right = ceilF26Dot6(slot->metrics.horiBearingX + slot->metrics.width);
     615          984 :     FT_Pos top = ceilF26Dot6(slot->metrics.horiBearingY);
     616          984 :     FT_Pos bottom = floorF26Dot6(slot->metrics.horiBearingY - slot->metrics.height);
     617              : 
     618          984 :     gm.bbox.setLeft(left);
     619          984 :     gm.bbox.setTop(-top);
     620          984 :     gm.bbox.setWidth(right - left);
     621          984 :     gm.bbox.setHeight(top - bottom);
     622              : 
     623          984 :     gm.linearAdvance = slot->linearHoriAdvance >> 10;
     624              : 
     625          984 :     return &gm;
     626              : }
     627              : 
     628            0 : SymbolMetrics* FontFaceFT::symbolMetrics(glyph_idx_t idx) const
     629              : {
     630            0 :     if (m_data->symbolMetrics.find(idx) != m_data->symbolMetrics.end()) {
     631            0 :         return &m_data->symbolMetrics.at(idx);
     632              :     }
     633              : 
     634            0 :     FT_UInt index = static_cast<FT_UInt>(idx);
     635            0 :     if (index == 0) {
     636            0 :         return nullptr;
     637              :     }
     638              : 
     639            0 :     if (FT_Load_Glyph(m_data->face, index, FT_LOAD_DEFAULT | FT_LOAD_NO_BITMAP | FT_LOAD_NO_HINTING) != 0) {
     640            0 :         return nullptr;
     641              :     }
     642              : 
     643            0 :     SymbolMetrics& sm = m_data->symbolMetrics[idx];
     644              : 
     645            0 :     sm.idx = static_cast<glyph_idx_t>(index);
     646              : 
     647            0 :     FT_GlyphSlot slot = m_data->face->glyph;
     648            0 :     sm.bbox.xMin = slot->metrics.horiBearingX;
     649            0 :     sm.bbox.xMax = slot->metrics.horiBearingX + slot->metrics.width;
     650            0 :     sm.bbox.yMin = slot->metrics.horiBearingY - slot->metrics.height;
     651            0 :     sm.bbox.yMax = slot->metrics.horiBearingY;
     652              : 
     653              :     //! NOTE Moved form MSS FontEngineFT::advance
     654              :     //! double advance = linearHoriAdvance * dpi_f / 655360.0;
     655              :     //! -> f26dot6_t advance = linearHoriAdvance * dpi_f * 64.0 / 655360.0;
     656              :     //! -> dpi_f = 5.0 constant
     657              :     //! -> f26dot6_t advance = linearHoriAdvance * 320.0 / 655360.0;
     658              :     //! -> f26dot6_t advance = linearHoriAdvance / 2048;
     659            0 :     sm.linearAdvance = m_data->face->glyph->linearHoriAdvance / 2048;
     660              : 
     661            0 :     return &sm;
     662              : }
        

Generated by: LCOV version 2.0-1