Add initial support to process CFF fonts contained inside a TrueType font

This commit is contained in:
BobLd
2025-09-14 11:17:33 +01:00
parent 304d7dde5a
commit 44e638ee4d
2 changed files with 110 additions and 13 deletions

View File

@@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
using Tables;
using UglyToad.PdfPig.Fonts.CompactFontFormat;
/// <summary>
/// Parses TrueType fonts.
@@ -59,7 +60,36 @@
private static TrueTypeFont ParseTables(float version, IReadOnlyDictionary<string, TrueTypeHeaderTable> tables, TrueTypeDataBytes data)
{
var isPostScript = tables.ContainsKey(TrueTypeHeaderTable.Cff);
bool isPostScript = false;
CompactFontFormatFontCollection? cffFontCollection = null;
if (tables.TryGetValue(TrueTypeHeaderTable.Cff, out var cffTable))
{
isPostScript = true;
try
{
/*
* The presence of a CFF (Compact Font Format) table in a TrueType font creates a hybrid situation where the font
* container uses TrueType structure but contains PostScript-based glyph descriptions. According to the OpenType
* specification, when a TrueType font contains a CFF table instead of a traditional glyf table, it indicates
* "an OpenType font with PostScript outlines". This creates what's known as an OpenType CFF font, which uses
* PostScript Type 2 charstrings for glyph descriptions rather than TrueType quadratic curves.
*
* This is to fix P2P-33713919.pdf
* See https://github.com/BobLd/PdfPig.Rendering.Skia/issues/46
* TODO - Add test coverage and need to review if the logic belongs here
*/
data.Seek(cffTable.Offset);
var buffer = data.ReadByteArray((int)cffTable.Length);
cffFontCollection = CompactFontFormatParser.Parse(new CompactFontFormatData(buffer));
}
catch (Exception e)
{
System.Diagnostics.Debug.WriteLine(e);
// Ignore
}
}
var builder = new TableRegister.Builder();
@@ -102,7 +132,7 @@
{
builder.Os2Table = TableParser.Parse<Os2Table>(os2Table, data, builder);
}
if (!isPostScript)
{
if (!tables.TryGetValue(TrueTypeHeaderTable.Loca, out var indexToLocationHeaderTable))
@@ -125,7 +155,7 @@
OptionallyParseTables(tables, data, builder);
return new TrueTypeFont(version, tables, builder.Build());
return new TrueTypeFont(version, tables, builder.Build(), cffFontCollection);
}
internal static NameTable GetNameTable(TrueTypeDataBytes data)
@@ -134,7 +164,7 @@
{
throw new ArgumentNullException(nameof(data));
}
// Read these data points to move to the correct data location.
data.Read32Fixed();
int numberOfTables = data.ReadUnsignedShort();

View File

@@ -5,6 +5,7 @@
using Core;
using Parser;
using Tables.CMapSubTables;
using UglyToad.PdfPig.Fonts.CompactFontFormat;
/// <summary>
/// A TrueType font.
@@ -54,17 +55,33 @@
/// </summary>
public int NumberOfTables { get; }
// TODO - It would be better to use 'PdfCidCompactFontFormatFont' but the class is not accessible from here.
private readonly CompactFontFormatFontCollection? cffFontCollection;
/// <summary>
/// Create a new <see cref="TrueTypeFont"/>.
/// </summary>
internal TrueTypeFont(float version, IReadOnlyDictionary<string, TrueTypeHeaderTable> tableHeaders, TableRegister tableRegister)
internal TrueTypeFont(float version, IReadOnlyDictionary<string, TrueTypeHeaderTable> tableHeaders, TableRegister tableRegister, CompactFontFormatFontCollection? cffFontCollection)
{
Version = version;
TableHeaders = tableHeaders ?? throw new ArgumentNullException(nameof(tableHeaders));
TableRegister = tableRegister ?? throw new ArgumentNullException(nameof(tableRegister));
NumberOfTables = tableHeaders.Count;
if (TableRegister.CMapTable != null)
/*
* The presence of a CFF (Compact Font Format) table in a TrueType font creates a hybrid situation where the font
* container uses TrueType structure but contains PostScript-based glyph descriptions. According to the OpenType
* specification, when a TrueType font contains a CFF table instead of a traditional glyf table, it indicates
* "an OpenType font with PostScript outlines". This creates what's known as an OpenType CFF font, which uses
* PostScript Type 2 charstrings for glyph descriptions rather than TrueType quadratic curves.
*
* This is to fix P2P-33713919.pdf
* See https://github.com/BobLd/PdfPig.Rendering.Skia/issues/46
* TODO - Add test coverage and need to review if the logic belongs here
*/
this.cffFontCollection = cffFontCollection;
if (TableRegister.CMapTable is not null)
{
const int encodingSymbol = 0;
const int encodingUnicode = 1;
@@ -72,19 +89,19 @@
foreach (var subTable in TableRegister.CMapTable.SubTables)
{
if (WindowsSymbolCMap == null
if (WindowsSymbolCMap is null
&& subTable.PlatformId == TrueTypeCMapPlatform.Windows
&& subTable.EncodingId == encodingSymbol)
{
WindowsSymbolCMap = subTable;
}
else if (WindowsUnicodeCMap == null
else if (WindowsUnicodeCMap is null
&& subTable.PlatformId == TrueTypeCMapPlatform.Windows
&& subTable.EncodingId == encodingUnicode)
{
WindowsUnicodeCMap = subTable;
}
else if (MacRomanCMap == null
else if (MacRomanCMap is null
&& subTable.PlatformId == TrueTypeCMapPlatform.Macintosh
&& subTable.EncodingId == encodingMacRoman)
{
@@ -107,8 +124,36 @@
{
boundingBox = default(PdfRectangle);
if (TableRegister.GlyphTable == null)
if (TableRegister.GlyphTable is null)
{
if (cffFontCollection is not null)
{
/*
* The presence of a CFF (Compact Font Format) table in a TrueType font creates a hybrid situation where the font
* container uses TrueType structure but contains PostScript-based glyph descriptions. According to the OpenType
* specification, when a TrueType font contains a CFF table instead of a traditional glyf table, it indicates
* "an OpenType font with PostScript outlines". This creates what's known as an OpenType CFF font, which uses
* PostScript Type 2 charstrings for glyph descriptions rather than TrueType quadratic curves.
*
* This is to fix P2P-33713919.pdf
* See https://github.com/BobLd/PdfPig.Rendering.Skia/issues/46
* TODO - Add test coverage and need to review if the logic belongs here
*/
var name = cffFontCollection.FirstFont.GetCharacterName(characterCode, true); // TODO cid?
if (string.IsNullOrEmpty(name))
{
return false;
}
var bbox = cffFontCollection.FirstFont.GetCharacterBoundingBox(name);
if (bbox.HasValue)
{
boundingBox = bbox.Value;
return true;
}
}
return false;
}
@@ -143,8 +188,30 @@
{
path = null;
if (TableRegister.GlyphTable == null)
if (TableRegister.GlyphTable is null)
{
if (cffFontCollection is not null)
{
/*
* The presence of a CFF (Compact Font Format) table in a TrueType font creates a hybrid situation where the font
* container uses TrueType structure but contains PostScript-based glyph descriptions. According to the OpenType
* specification, when a TrueType font contains a CFF table instead of a traditional glyf table, it indicates
* "an OpenType font with PostScript outlines". This creates what's known as an OpenType CFF font, which uses
* PostScript Type 2 charstrings for glyph descriptions rather than TrueType quadratic curves.
*
* This is to fix P2P-33713919.pdf
* See https://github.com/BobLd/PdfPig.Rendering.Skia/issues/46
* TODO - Add test coverage and need to review if the logic belongs here
*/
var name = cffFontCollection.FirstFont.GetCharacterName(characterCode, true);
if (string.IsNullOrEmpty(name))
{
return false;
}
return cffFontCollection.FirstFont.TryGetPath(name, out path);
}
return false;
}
@@ -188,7 +255,7 @@
{
width = 0;
if (TableRegister.HorizontalMetricsTable == null)
if (TableRegister.HorizontalMetricsTable is null)
{
return false;
}
@@ -210,7 +277,7 @@
return true;
}
if (TableRegister.CMapTable == null)
if (TableRegister.CMapTable is null)
{
return false;
}