Skip to content

Commit b831958

Browse files
cinthamodamiansalviadamiansalvia
authored
Added a Font Converter and align Font, FontFamily to System.Drawing interface (#13)
* Add GraphicsUnit * Move installed fonts loading to InstalledFontCollection class * Upgrade SkiaSharp to 2.88.8 * Move FaceName calculation to Font since FontFamily is not for any particular Style * Rearrange generic family * Create same public constructor as System.Drawing for FontFamily FontFamily represent all possibles styles from a family name. * Use new factory methods FromFIle, FromStream * Minor changes * Add FontStyle to Font * Fix Clone function (GetCellDescent will be added back later) * Fix Dispose, ToString, Equals * Group static properties * Fix Metrics * Add SizeInPoints property * Add FontConverter to convert from Font to string and back * Adjust unit tests for fonts * Fix unit tests in linux * minor changes * define Font and FontFamily as sealed * FromFile should not be public. Use the same way to load from a file as with System.Drawing * Disable the tests that don't work as expected with skia * Fix test exclusion for mac * Throw exceptions as the doc says * Fix merge of GraphicsUnit * Add typeface index in Font.ToString() * Inline GetFontFamilies() since it is only used in the constructor Make constructor public because it is available in System.Drawing * add test-cases for InstalledFontCollection and PrivateFontCollection * Calculate DPI instead of having it hardcoded * Fix exception message to be consistent with others * Add default font directory for Linux * Remove no longer used parameter * Add Factory region and move methods before Methods region, to be consistent with Image.cs * Remove Instance property since it is not part of System.Drawing interface and it is only used on FontFamily * Fix summaries in FontConverter class (use <see> tag and remove System.Drawing references) * relocate GetGenericFontFamily under Factory region and rename as FromGenericFontFamily * reuse FontFamily constructor logic * remove FontFamily(FontFamily[]) private constructor and relocate Match methods to a new Utilitiesregion * fix enclosed class definition * add missing Font constrcutors, reuse them, and redefine properties with private set for avoiding extra private property * define PrivateFontCollection and InstalledFontCollection as sealed * redefine InstalledFontCollection just based on SKFontManager * set ownership to false in FromFontCollection method * set ownership to false in FontFamily(FontFamily) constructor * redefine FontFamily(string, FontFamily[]) constructor and reuse Families instance (instead of creating a new InstalledFontCollection instance) * remove m_original property and define OriginalFontName instead (with get and private set) * add gdiCharSet and gdiVerticalFont param descriptions * fix comment in SizeInPoints for Display and Pixel values --------- Co-authored-by: dsalvia <[email protected]> Co-authored-by: damiansalvia <[email protected]>
1 parent 065e57b commit b831958

13 files changed

+1260
-420
lines changed

src/Common/Font.cs

+216-102
Large diffs are not rendered by default.

src/Common/FontConverter.cs

+524
Large diffs are not rendered by default.

src/Common/FontFamily.cs

+234-165
Large diffs are not rendered by default.

src/Common/GeneXus.Drawing.Common.csproj

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
</PropertyGroup>
99

1010
<ItemGroup>
11-
<PackageReference Include="SkiaSharp" Version="2.88.7" />
11+
<PackageReference Include="SkiaSharp" Version="2.88.8" />
1212
<PackageReference Include="SkiaSharp.Extended" Version="2.0.0" />
1313
<PackageReference Include="SkiaSharp.NativeAssets.Linux.NoDependencies" Version="2.88.7" />
1414
<PackageReference Include="SkiaSharp.Svg" Version="1.60.0" />

src/Common/GraphicsUnit.cs

+9-8
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,43 @@
11
namespace GeneXus.Drawing;
22

33
/// <summary>
4-
/// Specifies the unit of measure for the given data.
4+
/// Specifies the unit of measure for the given data
55
/// </summary>
66
public enum GraphicsUnit
77
{
88
/// <summary>
9-
/// Specifies the world unit as the unit of measure.
9+
/// Specifies the world coordinate system unit as the unit of measure.
1010
/// </summary>
1111
World = 0,
1212

1313
/// <summary>
14-
/// Specifies 1/75 inch as the unit of measure.
14+
/// Specifies the unit of measure of the display device. Typically pixels for video
15+
/// displays, and 1/100 inch for printers.
1516
/// </summary>
1617
Display = 1,
1718

1819
/// <summary>
19-
/// Specifies a device pixel as the unit of measure.
20+
/// Specifies a device pixel as the unit of measure.
2021
/// </summary>
2122
Pixel = 2,
2223

2324
/// <summary>
24-
/// Specifies a printer's point (1/72 inch) as the unit of measure.
25+
/// Specifies a printer's point (1/72 inch) as the unit of measure.
2526
/// </summary>
2627
Point = 3,
2728

2829
/// <summary>
29-
/// Specifies the inch as the unit of measure.
30+
/// Specifies the inch as the unit of measure.
3031
/// </summary>
3132
Inch = 4,
3233

3334
/// <summary>
34-
/// Specifies the document unit (1/300 inch) as the unit of measure.
35+
/// Specifies the document unit (1/300 inch) as the unit of measure.
3536
/// </summary>
3637
Document = 5,
3738

3839
/// <summary>
39-
/// Specifies the millimeter as the unit of measure.
40+
/// Specifies the millimeter as the unit of measure.
4041
/// </summary>
4142
Millimeter = 6
4243
}

src/Common/Text/FontCollection.cs

+4-4
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,14 @@ namespace GeneXus.Drawing.Text;
55

66
public abstract class FontCollection : IDisposable
77
{
8-
protected List<FontFamily> m_families = new List<FontFamily>();
8+
protected readonly List<FontFamily> m_families = new();
99

1010
/// <summary>
1111
/// Cleans up resources for this <see cref='FontCollection'/>.
1212
/// </summary>
1313
~FontCollection() => Dispose(false);
1414

15-
15+
1616
#region IDisposable
1717

1818
/// <summary>
@@ -24,7 +24,7 @@ public void Dispose()
2424
Dispose(true);
2525
}
2626

27-
protected virtual void Dispose(bool disposing)
27+
private void Dispose(bool disposing)
2828
{
2929
foreach (FontFamily ff in m_families)
3030
ff.Dispose();
@@ -34,7 +34,7 @@ protected virtual void Dispose(bool disposing)
3434
#endregion
3535

3636

37-
#region Propertie
37+
#region Properties
3838

3939
/// <summary>
4040
/// Gets the array of <see cref='FontFamily'/> objects associated with this <see cref='FontCollection'/>.
+22-6
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,29 @@
1-
namespace GeneXus.Drawing.Text;
1+
using SkiaSharp;
2+
using System.Collections.Generic;
23

3-
public class InstalledFontCollection : FontCollection
4+
namespace GeneXus.Drawing.Text;
5+
6+
public sealed class InstalledFontCollection : FontCollection
47
{
58
/// <summary>
6-
/// Initializes a new instance of the <see cref='InstalledFontCollection'/> class.
9+
/// Initializes a new instance of the <see cref='InstalledFontCollection'/> class.
710
/// </summary>
8-
public InstalledFontCollection() : base()
11+
public InstalledFontCollection()
912
{
10-
foreach (var font in Font.SystemFonts)
11-
m_families.Add(font.FontFamily);
13+
foreach (string name in SKFontManager.Default.FontFamilies)
14+
{
15+
List<SKTypeface> typefaces = new();
16+
foreach (SKFontStyle style in SKFontManager.Default.GetFontStyles(name))
17+
{
18+
var typeface = SKFontManager.Default.MatchFamily(name, style);
19+
typefaces.Add(typeface);
20+
}
21+
22+
if (typefaces.Count == 0)
23+
continue;
24+
25+
FontFamily fontFamily = new(name, typefaces.ToArray(), true);
26+
m_families.Add(fontFamily);
27+
}
1228
}
1329
}

src/Common/Text/PrivateFontCollection.cs

+14-12
Original file line numberDiff line numberDiff line change
@@ -4,34 +4,36 @@
44

55
namespace GeneXus.Drawing.Text;
66

7-
public class PrivateFontCollection : FontCollection
7+
public sealed class PrivateFontCollection : FontCollection
88
{
99
/// <summary>
10-
/// Initializes a new instance of the <see cref='PrivateFontCollection'/> class.
10+
/// Initializes a new instance of the <see cref='PrivateFontCollection'/> class.
1111
/// </summary>
12-
public PrivateFontCollection() : base()
13-
{ }
12+
public PrivateFontCollection() { }
13+
14+
15+
#region Methods
1416

1517
/// <summary>
16-
/// Adds a font from the specified file to this <see cref='PrivateFontCollection'/>.
18+
/// Adds a font from the specified file to this <see cref='PrivateFontCollection'/>.
1719
/// </summary>
18-
public void AddFontFile(string filename)
20+
public void AddFontFile(string filePath)
1921
{
20-
var fontFamily = new FontFamily(filename);
21-
m_families.Add(fontFamily);
22+
m_families.Add(FontFamily.FromFile(filePath));
2223
}
2324

2425
/// <summary>
25-
/// Adds a font contained in system memory to this <see cref='PrivateFontCollection'/>.
26+
/// Adds a font contained in system memory to this <see cref='PrivateFontCollection'/>.
2627
/// </summary>
2728
public void AddMemoryFont(IntPtr memory, int length)
2829
{
2930
byte[] fontData = new byte[length];
3031
Marshal.Copy(memory, fontData, 0, length);
3132

32-
using var stream = new MemoryStream(fontData);
33-
34-
var fontFamily = new FontFamily(stream);
33+
using MemoryStream stream = new(fontData);
34+
FontFamily fontFamily = FontFamily.FromStream(stream);
3535
m_families.Add(fontFamily);
3636
}
37+
38+
#endregion
3739
}

test/Common/FontConverterTest.cs

+49
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
using System;
2+
using System.ComponentModel;
3+
4+
namespace GeneXus.Drawing.Test;
5+
6+
internal class FontConverterTest
7+
{
8+
[Test]
9+
public void Method_ConvertFromString()
10+
{
11+
Font font = TypeDescriptor.GetConverter(typeof(Font)).ConvertFromString("Verdana,12") as Font;
12+
Assert.Multiple(() =>
13+
{
14+
Assert.That(font?.Name, Is.EqualTo("Verdana"));
15+
Assert.That(Math.Abs(font.Size - 12f), Is.LessThan(0.01));
16+
Assert.That(font.Unit, Is.EqualTo(GraphicsUnit.Point));
17+
Assert.That(font.Style, Is.EqualTo(FontStyle.Regular));
18+
});
19+
}
20+
21+
[Test]
22+
public void Method_ConvertToString()
23+
{
24+
Font font = new("Verdana"); // default size is 12
25+
string text = TypeDescriptor.GetConverter(typeof(Font)).ConvertToString(font);
26+
Assert.That(text, Is.EqualTo("Verdana, 12pt"));
27+
}
28+
29+
[Test]
30+
public void Method_ConvertFromString_Unknown()
31+
{
32+
Font font = TypeDescriptor.GetConverter(typeof(Font)).ConvertFromString("UnknownFont,14") as Font;
33+
Assert.Multiple(() =>
34+
{
35+
Assert.That(font?.Name, Is.EqualTo("UnknownFont"));
36+
Assert.That(Math.Abs(font.Size - 14f), Is.LessThan(0.01));
37+
Assert.That(font.Unit, Is.EqualTo(GraphicsUnit.Point));
38+
Assert.That(font.Style, Is.EqualTo(FontStyle.Regular));
39+
});
40+
}
41+
42+
[Test]
43+
public void Method_ConvertToString_Unknown()
44+
{
45+
Font font = new("UnknownFont"); // default size is 12
46+
string text = TypeDescriptor.GetConverter(typeof(Font)).ConvertToString(font);
47+
Assert.That(text, Is.EqualTo("UnknownFont, 12pt"));
48+
}
49+
}

test/Common/FontFamilyUnitTest.cs

+37-59
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
using GeneXus.Drawing.Text;
2-
using NUnit.Framework;
32
using System;
3+
using System.Collections.Generic;
44
using System.IO;
5+
using System.Runtime.InteropServices;
56

67
namespace GeneXus.Drawing.Test;
78

@@ -17,90 +18,67 @@ public void Setup()
1718
}
1819

1920
[Test]
20-
[TestCase("Montserrat-Regular.ttf", "Montserrat", 968, 251, 1219, 1000 )]
21-
[TestCase("Montserrat-Italic.ttf", "Montserrat", 968, 251, 1219, 1000 )]
22-
[TestCase("Montserrat-Bold.ttf", "Montserrat", 968, 251, 1219, 1000 )]
23-
[TestCase("Graphik-Regular.otf", "Graphik", 818, 182, 1100, 1000 )]
24-
[TestCase("Graphik-Italic.otf", "Graphik", 818, 182, 1100, 1000 )]
25-
[TestCase("Graphik-Bold.otf", "Graphik", 825, 175, 1100, 1000 )]
26-
[TestCase("EncodeSans-Regular.ttf", "Encode Sans", 2060, 440, 2500, 2000)]
27-
[TestCase("EncodeSans-Condensed.ttf", "Encode Sans", 2060, 440, 2500, 2000)]
28-
[TestCase("EncodeSans-Expanded.ttf", "Encode Sans", 2060, 440, 2500, 2000)]
29-
[TestCase("AvenirNext-Collection.ttc", "Avenir Next", 1000, 366, 1366, 1000 , 7)]
30-
[TestCase("AvenirNext-Collection.ttc", "Avenir Next", 1000, 366, 1366, 1000 , 4)]
31-
[TestCase("AvenirNext-Collection.ttc", "Avenir Next", 1000, 366, 1366, 1000 , 0)]
32-
public void Constructor_FileName(string fileName, string familyName, int ascent, int descent, int lineSpacing, int emHeight, int fontIndex = 0)
21+
[TestCase("Montserrat-Regular.ttf", "Montserrat", FontStyle.Regular, 968, 251, 1219, 1000 )]
22+
[TestCase("Montserrat-Italic.ttf", "Montserrat", FontStyle.Italic, 968, 251, 1219, 1000 )]
23+
[TestCase("Montserrat-Bold.ttf", "Montserrat", FontStyle.Bold, 968, 251, 1219, 1000 )]
24+
[TestCase("Graphik-Regular.otf", "Graphik", FontStyle.Regular, 818, 182, 1100, 1000 )]
25+
[TestCase("Graphik-Italic.otf", "Graphik", FontStyle.Italic, 818, 182, 1100, 1000 )]
26+
[TestCase("Graphik-Bold.otf", "Graphik", FontStyle.Bold, 825, 175, 1100, 1000 )]
27+
[TestCase("EncodeSans-Regular.ttf", "Encode Sans", FontStyle.Regular, 2060, 440, 2500, 2000)]
28+
[TestCase("EncodeSans-Condensed.ttf", "Encode Sans Condensed", FontStyle.Regular, 2060, 440, 2500, 2000)]
29+
[TestCase("EncodeSans-Expanded.ttf", "Encode Sans Expanded", FontStyle.Regular, 2060, 440, 2500, 2000)]
30+
[TestCase("AvenirNext-Collection.ttc", "Avenir Next", FontStyle.Regular, 1000, 366, 1366, 1000)]
31+
[TestCase("AvenirNext-Collection.ttc", "Avenir Next", FontStyle.Italic, 1000, 366, 1366, 1000)]
32+
[TestCase("AvenirNext-Collection.ttc", "Avenir Next", FontStyle.Bold, 1000, 366, 1366, 1000)]
33+
public void Constructor_FileName(string fileName, string familyName, FontStyle style, int ascent, int descent, int lineSpacing, int emHeight)
3334
{
34-
var fontPath = Path.Combine(FONT_PATH, fileName);
35-
using var family = new FontFamily(fontPath, fontIndex);
36-
Assert.Multiple(() =>
37-
{
38-
Assert.That(family.Name, Is.EqualTo(familyName));
39-
Assert.That(family.GetCellAscent(), Is.EqualTo(ascent));
40-
Assert.That(family.GetCellDescent(), Is.EqualTo(descent));
41-
Assert.That(family.GetLineSpacing(), Is.EqualTo(lineSpacing));
42-
Assert.That(family.GetEmHeight(), Is.EqualTo(emHeight));
43-
});
44-
}
35+
if (fileName.StartsWith("EncodeSans-") && RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
36+
familyName = "Encode Sans"; // in Windows it doesn't read the correct family name for this font with skia
4537

46-
[Test]
47-
[TestCase("Montserrat-Regular.ttf", "Montserrat", 968, 251, 1219, 1000)]
48-
[TestCase("Montserrat-Italic.ttf", "Montserrat", 968, 251, 1219, 1000)]
49-
[TestCase("Montserrat-Bold.ttf", "Montserrat", 968, 251, 1219, 1000)]
50-
[TestCase("Graphik-Regular.otf", "Graphik", 818, 182, 1100, 1000)]
51-
[TestCase("Graphik-Italic.otf", "Graphik", 818, 182, 1100, 1000)]
52-
[TestCase("Graphik-Bold.otf", "Graphik", 825, 175, 1100, 1000)]
53-
[TestCase("EncodeSans-Regular.ttf", "Encode Sans", 2060, 440, 2500, 2000)]
54-
[TestCase("EncodeSans-Condensed.ttf", "Encode Sans", 2060, 440, 2500, 2000)]
55-
[TestCase("EncodeSans-Expanded.ttf", "Encode Sans", 2060, 440, 2500, 2000)]
56-
[TestCase("AvenirNext-Collection.ttc", "Avenir Next", 1000, 366, 1366, 1000, 7)]
57-
[TestCase("AvenirNext-Collection.ttc", "Avenir Next", 1000, 366, 1366, 1000, 4)]
58-
[TestCase("AvenirNext-Collection.ttc", "Avenir Next", 1000, 366, 1366, 1000, 0)]
59-
public void Constructor_Stream(string fileName, string familyName, int ascent, int descent, int lineSpacing, int emHeight, int fontIndex = 0)
60-
{
61-
var fontPath = Path.Combine(FONT_PATH, fileName);
62-
using var fontStream = File.OpenRead(fontPath);
63-
using var family = new FontFamily(fontStream, fontIndex);
38+
string fontPath = Path.Combine(FONT_PATH, fileName);
39+
using PrivateFontCollection fontCollection = new();
40+
fontCollection.AddFontFile(fontPath);
41+
42+
FontFamily family = fontCollection.Families[0];
6443
Assert.Multiple(() =>
6544
{
6645
Assert.That(family.Name, Is.EqualTo(familyName));
67-
Assert.That(family.GetCellAscent(), Is.EqualTo(ascent));
68-
Assert.That(family.GetCellDescent(), Is.EqualTo(descent));
69-
Assert.That(family.GetLineSpacing(), Is.EqualTo(lineSpacing));
70-
Assert.That(family.GetEmHeight(), Is.EqualTo(emHeight));
46+
Assert.That(family.GetCellAscent(style), Is.EqualTo(ascent));
47+
Assert.That(family.GetCellDescent(style), Is.EqualTo(descent));
48+
Assert.That(family.GetLineSpacing(style), Is.EqualTo(lineSpacing));
49+
Assert.That(family.GetEmHeight(style), Is.EqualTo(emHeight));
7150
});
7251
}
7352

7453
[Test]
7554
public void Constructor_FontCollection()
7655
{
77-
using var pfc = new PrivateFontCollection();
56+
using PrivateFontCollection pfc = new();
7857
pfc.AddFontFile(Path.Combine(FONT_PATH, "Montserrat-Regular.ttf"));
7958
pfc.AddFontFile(Path.Combine(FONT_PATH, "Montserrat-Italic.ttf"));
8059
pfc.AddFontFile(Path.Combine(FONT_PATH, "Montserrat-Bold.ttf"));
8160
Assert.That(pfc.Families, Has.Length.EqualTo(3));
8261

83-
using var font = new Font(pfc.Families[1]);
84-
using var family = new FontFamily(font.Name, pfc);
62+
using FontFamily family = new("Montserrat", pfc);
8563
Assert.Multiple(() =>
8664
{
8765
Assert.That(family.Name, Is.EqualTo("Montserrat"));
8866
Assert.That(family.IsStyleAvailable(FontStyle.Regular), Is.True);
89-
Assert.That(family.IsStyleAvailable(FontStyle.Italic), Is.EqualTo((font.Style & FontStyle.Italic) == FontStyle.Italic));
90-
Assert.That(family.IsStyleAvailable(FontStyle.Bold), Is.EqualTo((font.Style & FontStyle.Bold) == FontStyle.Bold));
67+
Assert.That(family.IsStyleAvailable(FontStyle.Italic), Is.True);
68+
Assert.That(family.IsStyleAvailable(FontStyle.Bold), Is.True);
9169
});
9270
}
93-
71+
9472
[Test]
9573
public void Constructor_GenericFont()
9674
{
97-
using var monospace = new FontFamily(GenericFontFamilies.Monospace);
98-
Assert.That(monospace.Name, Is.AnyOf("Courier New", "Consolas", "Courier", "Menlo", "Monaco", "Lucida Console").IgnoreCase);
75+
using FontFamily monospace = new(GenericFontFamilies.Monospace);
76+
Assert.That(monospace.Name, Is.AnyOf("Courier New", "Consolas", "Courier", "Menlo", "Monaco", "Lucida Console", "DejaVu Sans Mono").IgnoreCase);
9977

100-
using var sanserif = new FontFamily(GenericFontFamilies.SansSerif);
101-
Assert.That(sanserif.Name, Is.AnyOf("Arial", "Helvetica", "Verdana", "Tahoma", "Trebuchet MS", "Gill Sans").IgnoreCase);
78+
using FontFamily sanserif = new(GenericFontFamilies.SansSerif);
79+
Assert.That(sanserif.Name, Is.AnyOf("Arial", "Helvetica", "Verdana", "Tahoma", "Trebuchet MS", "Gill Sans", "DejaVu Sans").IgnoreCase);
10280

103-
using var justserif = new FontFamily(GenericFontFamilies.Serif);
104-
Assert.That(justserif.Name, Is.AnyOf("Times New Roman", "Georgia", "Garamond", "Palatino", "Book Antiqua", "Baskerville").IgnoreCase);
81+
using FontFamily serif = new(GenericFontFamilies.Serif);
82+
Assert.That(serif.Name, Is.AnyOf("Times New Roman", "Georgia", "Garamond", "Palatino", "Book Antiqua", "Baskerville", "DejaVu Serif").IgnoreCase);
10583
}
10684
}

0 commit comments

Comments
 (0)