Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for overriding vertical font metrics #67

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

handerss-spotfire
Copy link
Contributor

This PR adds the ability to provide custom vertical font metrics when layouting text.

The background here is that Skia report different font metrics depending on platform. Windows uses usWinAscent, usWinDescent, and this lovely formula for calculating leading: MAX(0, (hhea.ascender - hhea.descender + hhea.lineGap) - (usWinAscent + usWinDescent)). If I remember correctly Linux uses the sTypo* metrics.

For cross-platform .NET applications that want to have similar rendering on both Windows and Linux (in particular vertical positioning) there needs to be a way to override the metrics reported by Skia.

I realize this may be out-of-scope for this library, feel free to close the PR in that case.

@dbriard
Copy link
Contributor

dbriard commented Mar 6, 2024

Hi @handerss-spotfire, your PR looks very interresting as after reading your explanation, I noticed that the vertical positioning of my text is not the same on Windows and WebAssembly version of my app.
Just one question: you recommand to use "Typo" metrics for cross-plarform, can I get that from Skia? or do I have to parse the font manually to extract those values?
Thank you!

@handerss-spotfire
Copy link
Contributor Author

handerss-spotfire commented Mar 6, 2024

You can get the relevant data tables using SKTypeface.TryGetTableData but then you have to parse them yourself. This is roughly we do it to get "Windows" style rendering on all platforms:

private static uint GetIntTag(string v)
{
    return
        (uint)v[0] << 24 |
        (uint)v[1] << 16 |
        (uint)v[2] << 08 |
        (uint)v[3] << 00;
}

public static bool TryReadFontMetrics(SKTypeface typeface, out (int Ascent, int Descent, int LineGap) fontMetrics)
{
    fontMetrics = (0, 0, 0);
    (int Ascent, int Descent, int? LineGap) os2Metrics = (0, 0, null);

    if (typeface.TryGetTableData(GetIntTag("OS/2"), out var os2Table))
    {
        os2Metrics = ReadOS2Table(os2Table);
    }

    if (os2Metrics.LineGap == null && typeface.TryGetTableData(GetIntTag("hhea"), out var hheaTable))
    {
        var hhea = ReadHheaTable(hheaTable);

        // See: https://learn.microsoft.com/en-us/typography/opentype/spec/recom#baseline-to-baseline-distances
        var lineGap = Math.Max(
            0,
            (hhea.Ascent - hhea.Descent + hhea.LineGap) - (os2Metrics.Ascent + os2Metrics.Descent));
        fontMetrics = (os2Metrics.Ascent, os2Metrics.Descent, lineGap);
    }

    return fontMetrics.Ascent > 0 && fontMetrics.Descent > 0;
}

Where ReadOS2Table and ReadHheaTable parse using a BinaryReader according the specs OS/2 hhea.

Note that when parsing the OS/2 table it's important to read metrics as specified by the font selection flags: https://learn.microsoft.com/en-us/typography/opentype/spec/os2#fsselection.

@dbriard
Copy link
Contributor

dbriard commented Mar 6, 2024

Thanks a lot for the details, I found tables and the properties, just need to find how to convert them to float correctly but I will look at the spec links.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants