toptensoftware / RichTextKit

Rich text rendering for SkiaSharp
Other
366 stars 73 forks source link

Topten.RichTextKit.Style FontFamily from custom typeface in SKTypeface #7

Closed Jcook65 closed 4 years ago

Jcook65 commented 4 years ago

I have a custom Typeface that I am using for text in my application. For painting text with SKPaint I can set the Typeface from an custom font that I load into a SKTypeface object. I can't seem to find a way to do that for a Style in the RichTextKit since Style.FontFamily only takes a string. I have tried using SKTypeface.FamilyName but the style doesn't seem to recognize it since it is a custom font. Is there a way to do this? I have looked around but I can't seem to find anywhere that this is referenced. If it isn't possible could someone point me to a list of acceptable values for Style.FontFamily?

toptensoftware commented 4 years ago

Have a look at the FontMapper class:

https://github.com/toptensoftware/RichTextKit/blob/master/Topten.RichTextKit/FontMapper.cs

You can provide your own implementation this (it's just one method that maps an IStyle to a typeface) and plug it in by either setting FontMapper.Default or setting the font mapper on the individual text blocks you're using.

toptensoftware commented 3 years ago

For reference, here's some sample code from one of my projects that show how to do this:

using SkiaSharp;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Topten.RichTextKit;

namespace Topten.GuiKit
{
    /// <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)
//            if (tf.IsItalic)
            {
                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);
        }

        /// <summary>
        /// Maps a GuiKit font to a SKTypeface
        /// </summary>
        /// <param name="font">The font to map</param>
        /// <returns>A mapped SKTypeface</returns>
        public static SKTypeface MapFontToSKTypeface(Font font)
        {
            // Look up custom fonts
            List<SKTypeface> listFonts;
            if (_customFonts.TryGetValue(font.FamilyName, out listFonts))
            {
                return listFonts.MinBy(x => Math.Abs(x.FontWeight - font.Weight));
            }

            // Skia's family name mapper seems to map apple system font
            // to a very bold version SF.  Remap it...
            var name = font.FamilyName;
            if (font.FamilyName == ".AppleSystemUIFont" && !font.Bold)
            {
                name = "San Francisco";
            }

            // Default...
            return SKTypeface.FromFamilyName(name, (SKFontStyleWeight)font.Weight, 0, SKFontStyleSlant.Upright);
        }

        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>>();
    }
}