Skip to content

Commit

Permalink
Add APIs to load fonts from file
Browse files Browse the repository at this point in the history
- Added FontFamily.FromFiles/FromStreams
- Added Font.FromFile/FromStream
- Added FontTypeface(string/Stream)
- Add ability to use typographic font names in WinForms by setting `FontsHandler.UseTypographicFonts = true` (off by default for now)
Fixes picoe#2120
  • Loading branch information
cwensley committed Mar 31, 2022
1 parent cd6bf51 commit 061ac58
Show file tree
Hide file tree
Showing 35 changed files with 2,132 additions and 213 deletions.
15 changes: 13 additions & 2 deletions src/Eto.Direct2D/Drawing/FontFamilyHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using sd = SharpDX.Direct2D1;
using sw = SharpDX.DirectWrite;
using System.Globalization;
using System.IO;

namespace Eto.Direct2D.Drawing
{
Expand All @@ -30,11 +31,11 @@ public string LocalizedName
}
}

FontTypeface[] typefaces;
FontTypeface[] _typefaces;
public IEnumerable<FontTypeface> Typefaces
{
get {
return typefaces ?? (typefaces = Enumerable.Range(0, Control.FontCount)
return _typefaces ?? (_typefaces = Enumerable.Range(0, Control.FontCount)
.Select(r => Control.GetFont(r))
.Select(r => new FontTypeface(Widget, new FontTypefaceHandler(r)))
.ToArray());
Expand Down Expand Up @@ -79,5 +80,15 @@ public void Create(string familyName)

Control = FontHandler.GetFontFamily(translatedName);
}

public void CreateFromFiles(IEnumerable<string> fileNames)
{
throw new NotImplementedException();
}

public void CreateFromStreams(IEnumerable<Stream> streams)
{
throw new NotImplementedException();
}
}
}
18 changes: 18 additions & 0 deletions src/Eto.Direct2D/Drawing/FontTypefaceHandler.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using Eto;
Expand Down Expand Up @@ -39,6 +40,8 @@ public string LocalizedName

public bool IsSymbol => Font.IsSymbolFont;

public FontFamily Family { get; private set; }

public bool HasCharacterRanges(IEnumerable<Range<int>> ranges)
{
foreach (var range in ranges)
Expand All @@ -49,5 +52,20 @@ public bool HasCharacterRanges(IEnumerable<Range<int>> ranges)
}
return true;
}

public void Create(Stream stream)
{
throw new NotSupportedException();
}

public void Create(string fileName)
{
throw new NotSupportedException();
}

public void Create(FontFamily family)
{
Family = family;
}
}
}
48 changes: 46 additions & 2 deletions src/Eto.Gtk/Drawing/FontFamilyHandler.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using Eto.Drawing;

Expand All @@ -12,9 +13,12 @@ public class FontFamilyHandler : WidgetHandler<Pango.FontFamily, FontFamily>, Fo

public string LocalizedName => Control?.Name ?? Name;

public IEnumerable<FontTypeface> Typefaces
FontTypeface[] _typefaces;
public IEnumerable<FontTypeface> Typefaces => _typefaces ?? (_typefaces = GetTypefaces().ToArray());

IEnumerable<FontTypeface> GetTypefaces()
{
get { return Control.Faces.Where(r => r != null).Select(r => new FontTypeface(Widget, new FontTypefaceHandler(r))); }
return Control.Faces.Where(r => r != null).Select(r => new FontTypeface(Widget, new FontTypefaceHandler(r)));
}

public FontFamilyHandler()
Expand All @@ -26,6 +30,14 @@ public FontFamilyHandler(Pango.FontFamily pangoFamily)
Control = pangoFamily;
Name = Control.Name;
}

public FontFamilyHandler(string familyName, FontTypefaceHandler typeface)
{
Name = familyName;
_typefaces = new[] { typeface.Widget };
var fm = FontsHandler.Context.FontMap;
Control = FindCorrectedFamily(familyName);
}

public static Pango.FontFamily FindCorrectedFamily(string familyName)
{
Expand Down Expand Up @@ -106,6 +118,38 @@ public static Pango.FontFamily GetFontFamily(string familyName)
return null;
return FontsHandler.Context.Families.FirstOrDefault(r => string.Equals(r.Name, familyName, StringComparison.InvariantCultureIgnoreCase));
}

public void CreateFromFiles(IEnumerable<string> fileNames)
{
foreach (var fileName in fileNames)
{
var familyName = FontTypefaceHandler.LoadFontFromFile(fileName);
if (Name == null)
Name = familyName;
else if (Name != familyName)
throw new InvalidOperationException($"Family name of the supplied font files do not match. '{Name}' and '{familyName}'");

}

FontsHandler.ResetFontMap();
Control = FindCorrectedFamily(Name);
}

public void CreateFromStreams(IEnumerable<Stream> streams)
{
foreach (var stream in streams)
{
var familyName = FontTypefaceHandler.LoadFontFromStream(stream);
if (Name == null)
Name = familyName;
else if (Name != familyName)
throw new InvalidOperationException($"Family name of the supplied font files do not match. '{Name}' and '{familyName}'");

}

FontsHandler.ResetFontMap();
Control = FindCorrectedFamily(Name);
}
}
}

119 changes: 119 additions & 0 deletions src/Eto.Gtk/Drawing/FontTypefaceHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
using Eto.Forms;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;

namespace Eto.GtkSharp.Drawing
{
Expand All @@ -13,6 +15,10 @@ public FontTypefaceHandler(Pango.FontFace pangoFace)
Control = pangoFace;
}

public FontTypefaceHandler()
{
}

public string Name => Control?.FaceName;

public string LocalizedName => Name;
Expand All @@ -36,6 +42,8 @@ public FontStyle FontStyle

public bool IsSymbol => false; // todo, how do we get font info?

public FontFamily Family { get; private set; }

static Pango.AttrList noFallbackAttributes;
static object noFallbackLock = new object();

Expand Down Expand Up @@ -69,6 +77,117 @@ public bool HasCharacterRanges(IEnumerable<Range<int>> ranges)
}
return true;
}

public void Create(Stream stream)
{
var familyName = LoadFontFromStream(stream);
FontsHandler.ResetFontMap();
CreateFromFamilyName(familyName);
}

public unsafe void Create(string fileName)
{
var familyName = LoadFontFromFile(fileName);
FontsHandler.ResetFontMap();
CreateFromFamilyName(familyName);
}

private void CreateFromFamilyName(string familyName)
{
var familyHandler = new FontFamilyHandler(familyName, this);
Family = new FontFamily(familyHandler);
if (familyHandler.Control == null)
throw new ArgumentException("Font could not be loaded");
Control = familyHandler.Control.Faces[0];
}

internal static unsafe string LoadFontFromFile(string fileName)
{
#if GTKCORE
// note: FontMap is null on Mac currently. It's likely a bug.
if (FontsHandler.Context.FontMap?.NativeType.ToString() == "PangoCairoFcFontMap")
{
var fcconfig = NativeMethods.FcConfigGetCurrent();

if (!NativeMethods.FcConfigAppFontAddFile(fcconfig, fileName))
throw new ArgumentException(nameof(fileName), "Could not add font to fontconfig");

var fcfontsPtr = NativeMethods.FcConfigGetFonts(fcconfig, NativeMethods.FcSetName.FcSetApplication);
var fcfonts = Marshal.PtrToStructure<NativeMethods.FcFontSet>(fcfontsPtr);
IntPtr[] fonts = new IntPtr[fcfonts.nfont];
Marshal.Copy(fcfonts.fonts, fonts, 0, fcfonts.nfont);

// we're assuming, but probably correct that the last font added goes to the last entry in the array.
var fontDescriptionPtr = NativeMethods.pango_fc_font_description_from_pattern(fonts[fonts.Length - 1], false);
var fontdesc = new Pango.FontDescription(fontDescriptionPtr);
return fontdesc.Family;

}
else if (EtoEnvironment.Platform.IsMac)
{
IntPtr provider = NativeMacMethods.CGDataProviderCreateWithFilename(fileName);
var cgfont = NativeMacMethods.CGFontCreateWithDataProvider(provider);
NativeMacMethods.CGDataProviderRelease(provider);
var ctfont = NativeMacMethods.CTFontCreateWithGraphicsFont(cgfont, 10, IntPtr.Zero, IntPtr.Zero);
var fontFamily = NativeMacMethods.CTFontCopyName(ctfont, NativeMacMethods.CTFontNameKeyFamily);
NativeMacMethods.CFRelease(ctfont);

NativeMacMethods.CTFontManagerRegisterGraphicsFont(cgfont, out var error);

return NativeMacMethods.CFStringToString(fontFamily);
}

// todo: What do we do on Windows?? Maybe if someone cares enough they can help here..
#endif
throw new NotSupportedException("This platform does not support loading fonts directly");
}

internal static unsafe string LoadFontFromStream(Stream stream)
{
#if GTKCORE
// note: FontMap is null on Mac currently. It's likely a bug.
if (FontsHandler.Context.FontMap?.NativeType.ToString() == "PangoCairoFcFontMap")
{
// need to save to a temp file and use that.
// https://gitlab.freedesktop.org/fontconfig/fontconfig/-/issues/12
var tempFileName = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
using (var fs = File.Create(tempFileName))
{
stream.CopyTo(fs);
}
return LoadFontFromFile(tempFileName);
}
else if (EtoEnvironment.Platform.IsMac)
{
using (var ms = new MemoryStream())
{
stream.CopyTo(ms);
var buffer = ms.ToArray();
IntPtr provider;
fixed (byte* p = &buffer[0])
{
provider = NativeMacMethods.CGDataProviderCreateWithData(IntPtr.Zero, (IntPtr)p, new IntPtr(buffer.Length), IntPtr.Zero);
}
var cgfont = NativeMacMethods.CGFontCreateWithDataProvider(provider);
NativeMacMethods.CGDataProviderRelease(provider);
var ctfont = NativeMacMethods.CTFontCreateWithGraphicsFont(cgfont, 10, IntPtr.Zero, IntPtr.Zero);
var fontFamily = NativeMacMethods.CTFontCopyName(ctfont, NativeMacMethods.CTFontNameKeyFamily);
NativeMacMethods.CFRelease(ctfont);

NativeMacMethods.CTFontManagerRegisterGraphicsFont(cgfont, out var error);

return NativeMacMethods.CFStringToString(fontFamily);
}
}
// todo: What do we do on Windows?? Maybe if someone cares enough they can help here..
#endif
throw new NotSupportedException("This platform does not support loading fonts directly");
}

public void Create(FontFamily family)
{
Family = family;
}
}
}

9 changes: 9 additions & 0 deletions src/Eto.Gtk/Drawing/FontsHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,15 @@ public static Pango.Context Context
return context;
}
}

public static void ResetFontMap()
{
IntPtr fontMapPtr = NativeMethods.pango_cairo_font_map_new();
NativeMethods.pango_cairo_font_map_set_default(fontMapPtr);
ResetContext();
}

public static void ResetContext() => context = null;

public IEnumerable<FontFamily> AvailableFontFamilies
{
Expand Down
Loading

0 comments on commit 061ac58

Please sign in to comment.