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

WIP: Compact Font Format (CFF) Version 2 #342

Open
wants to merge 41 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
b360105
Start implementing CFF2 support
brianpopow Jun 23, 2022
a1917ff
Fix mistake loading VariationAxisRecord
brianpopow Jun 27, 2022
29317e4
- Use Epsilon defined in fontkit rather the float.Epsilon
brianpopow Jun 27, 2022
c541f52
Cache calculated blend vectors
brianpopow Jun 27, 2022
dfc08fc
Make ItemVariationStore a property of ICffTable
brianpopow Jun 28, 2022
359d270
Make gvar, avar and fvar optional
brianpopow Jun 28, 2022
646ddba
Start parsing gvar table
brianpopow Jun 29, 2022
5f6030b
Merge remote-tracking branch 'origin/main' into bp/cff2
brianpopow Jul 3, 2022
0d7e743
Add FDSelect format 4
brianpopow Jul 3, 2022
71cc71a
Move ReadFdArray to base class
brianpopow Jul 3, 2022
a06eeef
Move ReadFdSelect to base class
brianpopow Jul 3, 2022
7c408d6
Better ascii art
brianpopow Jul 4, 2022
d2fa25d
Add flag descriptions
brianpopow Jul 4, 2022
3064aa4
Parse hvar table
brianpopow Jul 5, 2022
6840b84
Fix issues parsing ItemvariationStore
brianpopow Jul 5, 2022
f43a240
Use font name from NameTable for CFF2 font
brianpopow Jul 5, 2022
3d7b12c
Implement loading DeltaSetIndexMap
brianpopow Jul 6, 2022
fad1a0b
Parse deltaSets
brianpopow Jul 9, 2022
7805dca
Merge branch 'main' into bp/cff2
JimBobSquarePants Jul 11, 2023
dedd968
Add AdvanceAdjustment() to VariationProcessor
brianpopow Jul 12, 2023
e2d1416
Add Variations table properties to TrueTypeFontTables
brianpopow Jul 13, 2023
9b6a92c
Add TryGetVariationAxes method to FontMetrics
brianpopow Jul 13, 2023
31870b3
Fix stylecop warnings
brianpopow Jul 16, 2023
63192f9
Merge branch 'main' into bp/cff2
JimBobSquarePants Aug 9, 2023
b361fc6
Fix longwords mask check.
JimBobSquarePants Aug 9, 2023
8a60922
Make long delta int instead of uint and short delta short instead of …
brianpopow Aug 10, 2023
5c4e888
Use ReadSByte() instead of ReadByte()
brianpopow Aug 10, 2023
34e2c4a
Rename longDeltas to regionDeltas
brianpopow Aug 10, 2023
5e2b1e3
ItemVariationData has now a DeltaSet array with longDeltas and region…
brianpopow Aug 10, 2023
7f0e2c5
Fix delta concat order
JimBobSquarePants Aug 11, 2023
3c6cf40
Merge branch 'main' into bp/cff2
JimBobSquarePants Sep 5, 2023
a18e567
Fix build
JimBobSquarePants Sep 5, 2023
d3d5910
Update StreamFontMetrics.TrueType.cs
JimBobSquarePants Sep 5, 2023
cf17d52
Update GlyphVariationProcessor.cs
JimBobSquarePants Sep 5, 2023
05eaf59
Start figuring out missing implementation.
JimBobSquarePants Sep 12, 2023
4538e20
Merge branch 'main' into bp/cff2
JimBobSquarePants Sep 15, 2023
efd34aa
Merge branch 'main' into bp/cff2
JimBobSquarePants Oct 3, 2023
4720953
Merge branch 'main' into bp/cff2
JimBobSquarePants Dec 1, 2023
606f16b
Merge branch 'main' into bp/cff2
JimBobSquarePants Dec 6, 2023
a22a3b0
Merge branch 'main' into bp/cff2
JimBobSquarePants Feb 9, 2024
ff606eb
Merge branch 'main' into bp/cff2
JimBobSquarePants Feb 15, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/SixLabors.Fonts/BigEndianBinaryReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -356,7 +356,7 @@ public string ReadTag()
}

/// <summary>
/// Reads an offset consuming the given nuber of bytes.
/// Reads an offset consuming the given number of bytes.
/// </summary>
/// <param name="size">The offset size in bytes.</param>
/// <returns>The 32-bit signed integer representing the offset.</returns>
Expand Down Expand Up @@ -393,6 +393,7 @@ private void ReadInternal(byte[] data, int size)
}
}

/// <inheritdoc />
public void Dispose()
{
if (!this.leaveOpen)
Expand Down
5 changes: 5 additions & 0 deletions src/SixLabors.Fonts/FileFontMetrics.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.Numerics;
using SixLabors.Fonts.Tables;
using SixLabors.Fonts.Tables.AdvancedTypographic;
using SixLabors.Fonts.Tables.AdvancedTypographic.Variations;
using SixLabors.Fonts.Unicode;

namespace SixLabors.Fonts;
Expand Down Expand Up @@ -115,6 +116,10 @@ internal override bool TryGetGlyphClass(ushort glyphId, [NotNullWhen(true)] out
internal override bool TryGetMarkAttachmentClass(ushort glyphId, [NotNullWhen(true)] out GlyphClassDef? markAttachmentClass)
=> this.fontMetrics.Value.TryGetMarkAttachmentClass(glyphId, out markAttachmentClass);

/// <inheritdoc/>
public override bool TryGetVariationAxes(out VariationAxis[]? variationAxes)
=> this.fontMetrics.Value.TryGetVariationAxes(out variationAxes);

/// <inheritdoc />
public override bool TryGetGlyphMetrics(
CodePoint codePoint,
Expand Down
9 changes: 9 additions & 0 deletions src/SixLabors.Fonts/FontMetrics.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System.Diagnostics.CodeAnalysis;
using System.Numerics;
using SixLabors.Fonts.Tables.AdvancedTypographic;
using SixLabors.Fonts.Tables.AdvancedTypographic.Variations;
using SixLabors.Fonts.Unicode;

namespace SixLabors.Fonts;
Expand Down Expand Up @@ -159,6 +160,14 @@ internal FontMetrics()
/// <returns>true, if the mark attachment class could be retrieved.</returns>
internal abstract bool TryGetMarkAttachmentClass(ushort glyphId, [NotNullWhen(true)] out GlyphClassDef? markAttachmentClass);

/// <summary>
/// Tries to get the variation axes that this font supports.
/// The font needs to have a fvar table.
/// </summary>
/// <param name="variationAxes">An array with Variation axes.</param>
/// <returns>True, if fvar table is present.</returns>
public abstract bool TryGetVariationAxes(out VariationAxis[]? variationAxes);

/// <summary>
/// Gets the glyph metrics for a given code point.
/// </summary>
Expand Down
16 changes: 7 additions & 9 deletions src/SixLabors.Fonts/FontReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -138,18 +138,16 @@ public FontReader(Stream stream)
{
return (TTableType)table;
}
else

TTableType? loadedTable = this.loader.Load<TTableType>(this);
if (loadedTable is null)
{
TTableType? loadedTable = this.loader.Load<TTableType>(this);
if (loadedTable is null)
{
return null;
}

table = loadedTable;
this.loadedTables.Add(typeof(TTableType), loadedTable);
return null;
}

table = loadedTable;
this.loadedTables.Add(typeof(TTableType), loadedTable);

return (TTableType)table;
}

Expand Down
30 changes: 27 additions & 3 deletions src/SixLabors.Fonts/StreamFontMetrics.Cff.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Licensed under the Six Labors Split License.

using SixLabors.Fonts.Tables.AdvancedTypographic;
using SixLabors.Fonts.Tables.AdvancedTypographic.Variations;
using SixLabors.Fonts.Tables.Cff;
using SixLabors.Fonts.Tables.General;
using SixLabors.Fonts.Tables.General.Colr;
Expand All @@ -21,15 +22,14 @@ private static StreamFontMetrics LoadCompactFont(FontReader reader)
{
// Load using recommended order for best performance.
// https://www.microsoft.com/typography/otspec/recom.htm#TableOrdering
// 'head', 'hhea', 'maxp', OS/2, 'name', 'cmap', 'post', 'CFF '
// 'head', 'hhea', 'maxp', OS/2, 'name', 'cmap', 'post', 'CFF ' / 'CFF2'
HeadTable head = reader.GetTable<HeadTable>();
HorizontalHeadTable hhea = reader.GetTable<HorizontalHeadTable>();
MaximumProfileTable maxp = reader.GetTable<MaximumProfileTable>();
OS2Table os2 = reader.GetTable<OS2Table>();
NameTable name = reader.GetTable<NameTable>();
CMapTable cmap = reader.GetTable<CMapTable>();
PostTable post = reader.GetTable<PostTable>();

ICffTable? cff = reader.TryGetTable<Cff1Table>() ?? (ICffTable?)reader.TryGetTable<Cff2Table>();

// TODO: VORG
Expand All @@ -50,6 +50,21 @@ private static StreamFontMetrics LoadCompactFont(FontReader reader)
ColrTable? colr = reader.TryGetTable<ColrTable>();
CpalTable? cpal = reader.TryGetTable<CpalTable>();

// Variations related tables.
FVarTable? fVar = reader.TryGetTable<FVarTable>();
AVarTable? aVar = reader.TryGetTable<AVarTable>();
GVarTable? gVar = reader.TryGetTable<GVarTable>();
GlyphVariationProcessor? glyphVariationProcessor = null;
if (cff?.ItemVariationStore != null)
{
if (fVar is null)
{
throw new InvalidFontFileException("missing fvar table required for glyph variations processing");
}

glyphVariationProcessor = new GlyphVariationProcessor(cff.ItemVariationStore, fVar, aVar, gVar);
}

CompactFontTables tables = new(cmap, head, hhea, htmx, maxp, name, os2, post, cff!)
{
Kern = kern,
Expand All @@ -60,9 +75,12 @@ private static StreamFontMetrics LoadCompactFont(FontReader reader)
GPos = gPos,
Colr = colr,
Cpal = cpal,
FVar = fVar,
AVar = aVar,
GVar = gVar,
};

return new StreamFontMetrics(tables);
return new StreamFontMetrics(tables, glyphVariationProcessor);
}

private GlyphMetrics CreateCffGlyphMetrics(
Expand All @@ -78,8 +96,14 @@ private GlyphMetrics CreateCffGlyphMetrics(
ICffTable cff = tables.Cff;
HorizontalMetricsTable htmx = tables.Htmx;
VerticalMetricsTable? vtmx = tables.Vmtx;
FVarTable? fVar = tables.FVar;
AVarTable? aVar = tables.AVar;
GVarTable? gVar = tables.GVar;

CffGlyphData vector = cff.GetGlyph(glyphId);
vector.FVar = fVar;
vector.AVar = aVar;
vector.GVar = gVar;
Bounds bounds = vector.GetBounds();
ushort advanceWidth = htmx.GetAdvancedWidth(glyphId);
short lsb = htmx.GetLeftSideBearing(glyphId);
Expand Down
11 changes: 11 additions & 0 deletions src/SixLabors.Fonts/StreamFontMetrics.TrueType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

using System.Numerics;
using SixLabors.Fonts.Tables.AdvancedTypographic;
using SixLabors.Fonts.Tables.AdvancedTypographic.Variations;
using SixLabors.Fonts.Tables.General;
using SixLabors.Fonts.Tables.General.Colr;
using SixLabors.Fonts.Tables.General.Kern;
Expand Down Expand Up @@ -64,6 +65,12 @@ internal void ApplyTrueTypeHinting(HintingMode hintingMode, GlyphMetrics metrics

private static StreamFontMetrics LoadTrueTypeFont(FontReader reader)
{
// Load glyph variations related tables first, because glyph table needs them.
FVarTable? fvar = reader.TryGetTable<FVarTable>();
AVarTable? avar = reader.TryGetTable<AVarTable>();
GVarTable? gvar = reader.TryGetTable<GVarTable>();
HVarTable? hvar = reader.TryGetTable<HVarTable>();

// Load using recommended order for best performance.
// https://www.microsoft.com/typography/otspec/recom.htm#TableOrdering
// 'head', 'hhea', 'maxp', OS/2, 'hmtx', LTSH, VDMX, 'hdmx', 'cmap', 'fpgm', 'prep', 'cvt ', 'loca', 'glyf', 'kern', 'name', 'post', 'gasp', PCLT, DSIG
Expand Down Expand Up @@ -109,6 +116,10 @@ private static StreamFontMetrics LoadTrueTypeFont(FontReader reader)
GPos = gPos,
Colr = colr,
Cpal = cpal,
Fvar = fvar,
Gvar = gvar,
Hvar = hvar,
Avar = avar
};

return new StreamFontMetrics(tables);
Expand Down
43 changes: 38 additions & 5 deletions src/SixLabors.Fonts/StreamFontMetrics.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@

using System.Collections.Concurrent;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Numerics;
using SixLabors.Fonts.Tables;
using SixLabors.Fonts.Tables.AdvancedTypographic;
using SixLabors.Fonts.Tables.AdvancedTypographic.Variations;
using SixLabors.Fonts.Tables.Cff;
using SixLabors.Fonts.Tables.General;
using SixLabors.Fonts.Tables.General.Colr;
Expand Down Expand Up @@ -76,11 +78,13 @@ internal StreamFontMetrics(TrueTypeFontTables tables)
/// Initializes a new instance of the <see cref="StreamFontMetrics"/> class.
/// </summary>
/// <param name="tables">The Compact Font tables.</param>
internal StreamFontMetrics(CompactFontTables tables)
/// <param name="glyphVariationProcessor">Processor which handles glyph variations.</param>
internal StreamFontMetrics(CompactFontTables tables, GlyphVariationProcessor? glyphVariationProcessor = null)
{
this.compactFontTables = tables;
this.outlineType = OutlineType.CFF;
this.description = new FontDescription(tables.Name, tables.Os2, tables.Head);
this.GlyphVariationProcessor = glyphVariationProcessor;
this.glyphIdCache = new();
this.glyphCache = new();
if (tables.Colr is not null)
Expand All @@ -95,6 +99,8 @@ internal StreamFontMetrics(CompactFontTables tables)

public HeadTable.HeadFlags HeadFlags { get; private set; }

public GlyphVariationProcessor? GlyphVariationProcessor { get; private set; }

/// <inheritdoc/>
public override FontDescription Description => this.description;

Expand Down Expand Up @@ -196,6 +202,35 @@ internal override bool TryGetMarkAttachmentClass(ushort glyphId, [NotNullWhen(tr
return gdef is not null && gdef.TryGetMarkAttachmentClass(glyphId, out markAttachmentClass);
}

/// <inheritdoc/>
public override bool TryGetVariationAxes(out VariationAxis[]? variationAxes)
{
if (this.trueTypeFontTables?.Fvar == null)
{
variationAxes = Array.Empty<VariationAxis>();
return false;
}

FVarTable? fvar = this.trueTypeFontTables?.Fvar;
Tables.General.Name.NameTable? names = this.trueTypeFontTables?.Name;
variationAxes = new VariationAxis[fvar!.Axes.Length];
for (int i = 0; i < fvar.Axes.Length; i++)
{
VariationAxisRecord axis = fvar.Axes[i];
string name = names != null ? names.GetNameById(CultureInfo.InvariantCulture, axis.AxisNameId) : string.Empty;
variationAxes[i] = new VariationAxis()
{
Tag = axis.Tag,
Min = axis.MinValue,
Max = axis.MaxValue,
Default = axis.DefaultValue,
Name = name
};
}

return true;
}

/// <inheritdoc/>
public override bool TryGetGlyphMetrics(
CodePoint codePoint,
Expand Down Expand Up @@ -367,10 +402,8 @@ internal static StreamFontMetrics LoadFont(FontReader reader)
{
return LoadTrueTypeFont(reader);
}
else
{
return LoadCompactFont(reader);
}

return LoadCompactFont(reader);
}

private (HorizontalMetrics HorizontalMetrics, VerticalMetrics VerticalMetrics) Initialize<T>(T tables)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.

namespace SixLabors.Fonts.Tables.AdvancedTypographic.Variations;

/// <summary>
/// Implements reading the Font Variations Table `avar`.
/// <see href="https://docs.microsoft.com/de-de/typography/opentype/spec/avar"/>
/// </summary>
internal class AVarTable : Table
{
internal const string TableName = "avar";

public AVarTable(uint axisCount, SegmentMapRecord[] segmentMaps)
{
this.AxisCount = axisCount;
this.SegmentMaps = segmentMaps;
}

public uint AxisCount { get; }

public SegmentMapRecord[] SegmentMaps { get; }

public static AVarTable? Load(FontReader reader)
{
if (!reader.TryGetReaderAtTablePosition(TableName, out BigEndianBinaryReader? binaryReader))
{
return null;
}

using (binaryReader)
{
return Load(binaryReader);
}
}

public static AVarTable Load(BigEndianBinaryReader reader)
{
// VariationsTable `avar`
// +-----------------+----------------------------------------+-------------------------------------------------------------------------+
// | Type | Name | Description |
// +=================+========================================+=========================================================================+
// | uint16 | majorVersion | Major version number of the font variations table — set to 1. |
// +-----------------+----------------------------------------+-------------------------------------------------------------------------+
// | uint16 | minorVersion | Minor version number of the font variations table — set to 0. |
// +-----------------+----------------------------------------+-------------------------------------------------------------------------+
// | uint16 | (reserved) | This field is permanently reserved. Set to zero. |
// +-----------------+----------------------------------------+-------------------------------------------------------------------------+
// | uint16 | axisCount | The number of variation axes in the font |
// | | | (the number of records in the axes array). |
// +-----------------+----------------------------------------+-------------------------------------------------------------------------+
// | SegmentMaps | axisSegmentMaps[axisCount] | The segment maps array — one segment map for each axis, in the order of |
// | | | axes specified in the 'fvar' table. |
// +-----------------+----------------------------------------+-------------------------------------------------------------------------+
ushort major = reader.ReadUInt16();
ushort minor = reader.ReadUInt16();
ushort reserved = reader.ReadUInt16();
ushort axisCount = reader.ReadUInt16();

if (major != 1)
{
throw new NotSupportedException("Only version 1 of avar table is supported");
}

var segmentMaps = new SegmentMapRecord[axisCount];
for (int i = 0; i < axisCount; i++)
{
segmentMaps[i] = SegmentMapRecord.Load(reader);
}

return new AVarTable(axisCount, segmentMaps);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.

namespace SixLabors.Fonts.Tables.AdvancedTypographic.Variations;

internal class AxisValueMapRecord
{
public AxisValueMapRecord(float fromCoordinate, float toCoordinate)
{
this.FromCoordinate = fromCoordinate;
this.ToCoordinate = toCoordinate;
}

public float FromCoordinate { get; }

public float ToCoordinate { get; }

public static AxisValueMapRecord Load(BigEndianBinaryReader reader)
{
// AxisValueMapRecord
// +-----------------+----------------------------------------+-------------------------------------------------------------------------+
// | Type | Name | Description |
// +=================+========================================+=========================================================================+
// | F2DOT14 | fromCoordinate | A normalized coordinate value obtained using default normalization. |
// +-----------------+----------------------------------------+-------------------------------------------------------------------------+
// | F2DOT14 | toCoordinate | The modified, normalized coordinate value. |
// +-----------------+----------------------------------------+-------------------------------------------------------------------------+
float fromCoordinate = reader.ReadF2dot14();
float toCoordinate = reader.ReadF2dot14();

return new AxisValueMapRecord(fromCoordinate, toCoordinate);
}
}
Loading
Loading