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

Google font synthesis? #17

Open
Jonesie opened this issue Jul 27, 2020 · 7 comments
Open

Google font synthesis? #17

Jonesie opened this issue Jul 27, 2020 · 7 comments

Comments

@Jonesie
Copy link

Jonesie commented Jul 27, 2020

Hi. We are using google fonts. I cant figure out how to get bold or italics to work. In the browser, these are synthesised from regular. Ive tried loading a specific bold font into the style, but it still comes out at 400. I would rather the font was faked to match the browser (not all google fonts come with italic and bold).

Is this possible? I read that Chrome uses Skia for this so maybe this is not part of SkiaSharp?

Same issue on windows and linux.

@toptensoftware
Copy link
Owner

Thanks for reporting this. I've always just used fonts with the various weights/italics available and served them up to RichTextKit with a custom font mapper.

I'm not sure how to simulate bold/italic when not available in the font directly - perhaps this is built into Skia but I don't know and would have to research it. (If you figure it out, please let me know)

For reference here's the important parts of my font mapper from my UI toolkit:

    /// <summary>
    /// Handles mapping of font family names to SKTypefaces
    /// </summary>
    public class FontMapper : Topten.RichTextKit.FontMapper
    {
        /// <summary>
        /// Loads a private font from a stream
        /// </summary>
        /// <param name="stream">The stream to load from</param>
        /// <param name="familyName">An optional family name to override the font's built in name</param>
        /// <returns>True if the font was successully loaded</returns>
        public static bool LoadPrivateFont(System.IO.Stream stream, string familyName=null)
        {
            var tf = SKTypeface.FromStream(stream);
            if (tf == null)
                return false;

            var qualifiedName = familyName ?? tf.FamilyName;
            if (tf.FontSlant != SKFontStyleSlant.Upright)
            {
                qualifiedName += "-Italic";
            }

            // Get a list of typefaces with this family
            if (!_customFonts.TryGetValue(qualifiedName, out var listFonts))
            {
                listFonts = new List<SKTypeface>();
                _customFonts[qualifiedName] = listFonts;
            }

            // Add to the list
            listFonts.Add(tf);

            return true;
        }

        /// <summary>
        /// Map a RichTextKit style to an SKTypeface
        /// </summary>
        /// <param name="style">The style</param>
        /// <param name="ignoreFontVariants">True to ignore variants (super/subscript)</param>
        /// <returns>The mapped typeface</returns>
        public override SKTypeface TypefaceFromStyle(IStyle style, bool ignoreFontVariants)
        {
            // Work out the qualified name
            var qualifiedName = style.FontFamily;
            if (style.FontItalic)
                qualifiedName += "-Italic";

            // Look up custom fonts
            List<SKTypeface> listFonts;
            if (_customFonts.TryGetValue(qualifiedName, out listFonts))
            {
                // Find closest weight
                return listFonts.MinBy(x => Math.Abs(x.FontWeight - style.FontWeight));
            }

            // Do default mapping
            return base.TypefaceFromStyle(style, ignoreFontVariants);
        }

        static FontMapper()
        {
            // Install self as the default RichTextKit font mapper
            Topten.RichTextKit.FontMapper.Default = new FontMapper();
        }

        // Constructor
        private FontMapper()
        {
        }

        static Dictionary<string, List<SKTypeface>> _customFonts = new Dictionary<string, List<SKTypeface>>();
}

@Jonesie
Copy link
Author

Jonesie commented Aug 5, 2020

I had a stupid error in my font loading so now at least it selects the correct font variants and I get bold and/or italic - BUT, only if the google font supports it. To simulate/synthesize these for the other fonts, it needs to look at the typeface that is created from the stream and change the paint stroke weight and skew - I think. I'll fork and see if I can mangle that :)

@Jonesie
Copy link
Author

Jonesie commented Aug 5, 2020

This is well above my pay grade, but I think FontRun.cs is the place to start:

image

After creating the font, it needs to check that the typeface supports the requested styles and if not, create a SKPaint with stroke weight and skew to simulate it.

I cant see where SKTextBlob.CreatePositioned() comes from. It's not in the SkiaSharp docs.

@toptensoftware
Copy link
Owner

toptensoftware commented Aug 6, 2020

Hi @Jonesie,

I haven't had a chance to dive into this myself, but I noticed when updating to the latest skia sharp that SKFont has a method "Embolden" which maps to this:

Increases stroke width when creating glyph bitmaps to approximate a bold typeface.

That sounds like it would handle the visual side of it however since RichTextKit uses HarfBuzz to actually place the glyphs presumably that would also need to be updated to be place each glyph slightly further apart.

Also, I noticed setSkewX which could probably be used to simulate italic (and shouldn't require updating the glyph placement)

Brad

@toptensoftware toptensoftware reopened this Aug 7, 2020
@Gillibald
Copy link

Gillibald commented Aug 8, 2020

Setting isEmbolden and setSkewX should work. But keep in mind that these values don't effect harfbuzz during the shaping process. You need to register font function callbacks to make this work.

@toptensoftware
Copy link
Owner

Hi @Gillibald ,

Thanks. I wasn't aware of the font function callbacks... that's interesting. I wonder if emboldening a SKFont causes it's returned font metrics to be updated. I guess that's my main question - after emboldening a font how to get it's new metrics.

Am I correct in assuming the simulating italics shouldn't require any change in the shaping? The characters are just drawn sloped but at their same shaped positions.

Brad

@Gillibald
Copy link

That assumption is right. Advances stay the same just the combined bounds of your shaped text change.

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

No branches or pull requests

3 participants