Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
38 changes: 34 additions & 4 deletions src/UglyToad.PdfPig.Fonts/TrueType/Parser/TrueTypeFontParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
using Tables;
using UglyToad.PdfPig.Fonts.CompactFontFormat;

/// <summary>
/// Parses TrueType fonts.
Expand Down Expand Up @@ -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;

Check warning on line 64 in src/UglyToad.PdfPig.Fonts/TrueType/Parser/TrueTypeFontParser.cs

View workflow job for this annotation

GitHub Actions / build

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.

Check warning on line 64 in src/UglyToad.PdfPig.Fonts/TrueType/Parser/TrueTypeFontParser.cs

View workflow job for this annotation

GitHub Actions / build

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.

Check warning on line 64 in src/UglyToad.PdfPig.Fonts/TrueType/Parser/TrueTypeFontParser.cs

View workflow job for this annotation

GitHub Actions / build

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.

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

Expand Down Expand Up @@ -102,7 +132,7 @@
{
builder.Os2Table = TableParser.Parse<Os2Table>(os2Table, data, builder);
}

if (!isPostScript)
{
if (!tables.TryGetValue(TrueTypeHeaderTable.Loca, out var indexToLocationHeaderTable))
Expand All @@ -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)
Expand All @@ -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();
Expand Down
85 changes: 76 additions & 9 deletions src/UglyToad.PdfPig.Fonts/TrueType/TrueTypeFont.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using Core;
using Parser;
using Tables.CMapSubTables;
using UglyToad.PdfPig.Fonts.CompactFontFormat;

/// <summary>
/// A TrueType font.
Expand Down Expand Up @@ -54,37 +55,53 @@
/// </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;

Check warning on line 59 in src/UglyToad.PdfPig.Fonts/TrueType/TrueTypeFont.cs

View workflow job for this annotation

GitHub Actions / build

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.

Check warning on line 59 in src/UglyToad.PdfPig.Fonts/TrueType/TrueTypeFont.cs

View workflow job for this annotation

GitHub Actions / build

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.

Check warning on line 59 in src/UglyToad.PdfPig.Fonts/TrueType/TrueTypeFont.cs

View workflow job for this annotation

GitHub Actions / build

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.

Check warning on line 59 in src/UglyToad.PdfPig.Fonts/TrueType/TrueTypeFont.cs

View workflow job for this annotation

GitHub Actions / build

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.

/// <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)

Check warning on line 64 in src/UglyToad.PdfPig.Fonts/TrueType/TrueTypeFont.cs

View workflow job for this annotation

GitHub Actions / build

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.

Check warning on line 64 in src/UglyToad.PdfPig.Fonts/TrueType/TrueTypeFont.cs

View workflow job for this annotation

GitHub Actions / build

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.

Check warning on line 64 in src/UglyToad.PdfPig.Fonts/TrueType/TrueTypeFont.cs

View workflow job for this annotation

GitHub Actions / build

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.
{
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;
const int encodingMacRoman = 0;

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)
{
Expand All @@ -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;
}

Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -188,7 +255,7 @@
{
width = 0;

if (TableRegister.HorizontalMetricsTable == null)
if (TableRegister.HorizontalMetricsTable is null)
{
return false;
}
Expand All @@ -210,7 +277,7 @@
return true;
}

if (TableRegister.CMapTable == null)
if (TableRegister.CMapTable is null)
{
return false;
}
Expand Down
Loading