mirror of
https://github.com/UglyToad/PdfPig.git
synced 2025-09-19 19:07:56 +08:00
#8 tidy up truetype font internally. some more work on a potential document creation api
This commit is contained in:
@@ -35,43 +35,43 @@
|
|||||||
|
|
||||||
Assert.Equal(1, font.Version);
|
Assert.Equal(1, font.Version);
|
||||||
|
|
||||||
Assert.Equal(1, font.HeaderTable.Version);
|
Assert.Equal(1, font.TableRegister.HeaderTable.Version);
|
||||||
Assert.Equal(1, font.HeaderTable.Revision);
|
Assert.Equal(1, font.TableRegister.HeaderTable.Revision);
|
||||||
|
|
||||||
Assert.Equal(1142661421, font.HeaderTable.CheckSumAdjustment);
|
Assert.Equal(1142661421, font.TableRegister.HeaderTable.CheckSumAdjustment);
|
||||||
Assert.Equal(1594834165, font.HeaderTable.MagicNumber);
|
Assert.Equal(1594834165, font.TableRegister.HeaderTable.MagicNumber);
|
||||||
|
|
||||||
Assert.Equal(9, font.HeaderTable.Flags);
|
Assert.Equal(9, font.TableRegister.HeaderTable.Flags);
|
||||||
|
|
||||||
Assert.Equal(2048, font.HeaderTable.UnitsPerEm);
|
Assert.Equal(2048, font.TableRegister.HeaderTable.UnitsPerEm);
|
||||||
|
|
||||||
Assert.Equal(2008, font.HeaderTable.Created.Year);
|
Assert.Equal(2008, font.TableRegister.HeaderTable.Created.Year);
|
||||||
Assert.Equal(10, font.HeaderTable.Created.Month);
|
Assert.Equal(10, font.TableRegister.HeaderTable.Created.Month);
|
||||||
Assert.Equal(13, font.HeaderTable.Created.Day);
|
Assert.Equal(13, font.TableRegister.HeaderTable.Created.Day);
|
||||||
Assert.Equal(12, font.HeaderTable.Created.Hour);
|
Assert.Equal(12, font.TableRegister.HeaderTable.Created.Hour);
|
||||||
Assert.Equal(29, font.HeaderTable.Created.Minute);
|
Assert.Equal(29, font.TableRegister.HeaderTable.Created.Minute);
|
||||||
Assert.Equal(34, font.HeaderTable.Created.Second);
|
Assert.Equal(34, font.TableRegister.HeaderTable.Created.Second);
|
||||||
|
|
||||||
Assert.Equal(2011, font.HeaderTable.Modified.Year);
|
Assert.Equal(2011, font.TableRegister.HeaderTable.Modified.Year);
|
||||||
Assert.Equal(12, font.HeaderTable.Modified.Month);
|
Assert.Equal(12, font.TableRegister.HeaderTable.Modified.Month);
|
||||||
Assert.Equal(31, font.HeaderTable.Modified.Day);
|
Assert.Equal(31, font.TableRegister.HeaderTable.Modified.Day);
|
||||||
Assert.Equal(5, font.HeaderTable.Modified.Hour);
|
Assert.Equal(5, font.TableRegister.HeaderTable.Modified.Hour);
|
||||||
Assert.Equal(13, font.HeaderTable.Modified.Minute);
|
Assert.Equal(13, font.TableRegister.HeaderTable.Modified.Minute);
|
||||||
Assert.Equal(10, font.HeaderTable.Modified.Second);
|
Assert.Equal(10, font.TableRegister.HeaderTable.Modified.Second);
|
||||||
|
|
||||||
Assert.Equal(-980, font.HeaderTable.Bounds.Left);
|
Assert.Equal(-980, font.TableRegister.HeaderTable.Bounds.Left);
|
||||||
Assert.Equal(-555, font.HeaderTable.Bounds.Bottom);
|
Assert.Equal(-555, font.TableRegister.HeaderTable.Bounds.Bottom);
|
||||||
|
|
||||||
Assert.Equal(2396, font.HeaderTable.Bounds.Right);
|
Assert.Equal(2396, font.TableRegister.HeaderTable.Bounds.Right);
|
||||||
Assert.Equal(2163, font.HeaderTable.Bounds.Top);
|
Assert.Equal(2163, font.TableRegister.HeaderTable.Bounds.Top);
|
||||||
|
|
||||||
Assert.Equal(HeaderTable.HeaderMacStyle.None, font.HeaderTable.MacStyle);
|
Assert.Equal(HeaderTable.HeaderMacStyle.None, font.TableRegister.HeaderTable.MacStyle);
|
||||||
Assert.Equal(9, font.HeaderTable.LowestRecommendedPpem);
|
Assert.Equal(9, font.TableRegister.HeaderTable.LowestRecommendedPpem);
|
||||||
|
|
||||||
Assert.Equal(HeaderTable.FontDirection.StronglyLeftToRightWithNeutrals, font.HeaderTable.FontDirectionHint);
|
Assert.Equal(HeaderTable.FontDirection.StronglyLeftToRightWithNeutrals, font.TableRegister.HeaderTable.FontDirectionHint);
|
||||||
|
|
||||||
Assert.Equal(0, font.HeaderTable.IndexToLocFormat);
|
Assert.Equal(0, font.TableRegister.HeaderTable.IndexToLocFormat);
|
||||||
Assert.Equal(0, font.HeaderTable.GlyphDataFormat);
|
Assert.Equal(0, font.TableRegister.HeaderTable.GlyphDataFormat);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
@@ -139,7 +139,7 @@
|
|||||||
|
|
||||||
var font = parser.Parse(input);
|
var font = parser.Parse(input);
|
||||||
|
|
||||||
Assert.NotNull(font.GlyphTable);
|
Assert.NotNull(font.TableRegister.GlyphTable);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
@@ -151,7 +151,7 @@
|
|||||||
|
|
||||||
var font = parser.Parse(input);
|
var font = parser.Parse(input);
|
||||||
|
|
||||||
Assert.NotNull(font.GlyphTable);
|
Assert.NotNull(font.TableRegister.GlyphTable);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
@@ -175,7 +175,7 @@
|
|||||||
var height = decimal.Parse(match.Groups["height"].Value);
|
var height = decimal.Parse(match.Groups["height"].Value);
|
||||||
var points = int.Parse(match.Groups["points"].Value);
|
var points = int.Parse(match.Groups["points"].Value);
|
||||||
|
|
||||||
var glyph = font.GlyphTable.Glyphs[i];
|
var glyph = font.TableRegister.GlyphTable.Glyphs[i];
|
||||||
|
|
||||||
// Vendor data ignores the empty glyph bounds.
|
// Vendor data ignores the empty glyph bounds.
|
||||||
if (width == 0 && height == 0)
|
if (width == 0 && height == 0)
|
||||||
|
27
src/UglyToad.PdfPig.Tests/Writer/PdfDocumentBuilderTests.cs
Normal file
27
src/UglyToad.PdfPig.Tests/Writer/PdfDocumentBuilderTests.cs
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
namespace UglyToad.PdfPig.Tests.Writer
|
||||||
|
{
|
||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using Content;
|
||||||
|
using PdfPig.Geometry;
|
||||||
|
using PdfPig.Writer;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
public class PdfDocumentBuilderTests
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void CanLoadFontAndWriteText()
|
||||||
|
{
|
||||||
|
var builder = new PdfDocumentBuilder();
|
||||||
|
|
||||||
|
var page = builder.AddPage(PageSize.A4);
|
||||||
|
|
||||||
|
var path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Fonts", "TrueType");
|
||||||
|
var file = Path.Combine(path, "Andada-Regular.ttf");
|
||||||
|
|
||||||
|
var font = builder.AddTrueTypeFont(File.ReadAllBytes(file));
|
||||||
|
|
||||||
|
page.AddText("One", 12, new PdfPoint(30, 50), font);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -179,7 +179,7 @@
|
|||||||
{
|
{
|
||||||
var scale = 1000m;
|
var scale = 1000m;
|
||||||
|
|
||||||
if (fontProgram?.HeaderTable != null)
|
if (fontProgram?.TableRegister.HeaderTable != null)
|
||||||
{
|
{
|
||||||
scale = fontProgram.GetFontMatrixMultiplier();
|
scale = fontProgram.GetFontMatrixMultiplier();
|
||||||
}
|
}
|
||||||
|
@@ -1,33 +1,115 @@
|
|||||||
namespace UglyToad.PdfPig.Fonts.TrueType.Parser
|
namespace UglyToad.PdfPig.Fonts.TrueType.Parser
|
||||||
{
|
{
|
||||||
|
using System;
|
||||||
using Tables;
|
using Tables;
|
||||||
|
using Util.JetBrains.Annotations;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Holds tables while parsing a TrueType font
|
/// Holds tables while contained in a TrueType font.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal class TableRegister
|
internal class TableRegister
|
||||||
{
|
{
|
||||||
public GlyphDataTable GlyphDataTable { get; set; }
|
/// <summary>
|
||||||
|
/// This table contains global information about the font.
|
||||||
|
/// </summary>
|
||||||
|
[NotNull]
|
||||||
|
public HeaderTable HeaderTable { get; }
|
||||||
|
|
||||||
public HeaderTable HeaderTable { get; set; }
|
/// <summary>
|
||||||
|
/// This table contains the data that defines the appearance of the glyphs in the font.
|
||||||
|
/// </summary>
|
||||||
|
[NotNull]
|
||||||
|
public GlyphDataTable GlyphTable { get; }
|
||||||
|
|
||||||
public HorizontalHeaderTable HorizontalHeaderTable { get; set; }
|
/// <summary>
|
||||||
|
/// This table contains information needed to layout fonts whose characters are written horizontally.
|
||||||
|
/// </summary>
|
||||||
|
[NotNull]
|
||||||
|
public HorizontalHeaderTable HorizontalHeaderTable { get; }
|
||||||
|
|
||||||
public HorizontalMetricsTable HorizontalMetricsTable { get; set; }
|
/// <summary>
|
||||||
|
/// This table contains metric information for the horizontal layout each of the glyphs in the font.
|
||||||
|
/// </summary>
|
||||||
|
[NotNull]
|
||||||
|
public HorizontalMetricsTable HorizontalMetricsTable { get; }
|
||||||
|
|
||||||
public IndexToLocationTable IndexToLocationTable { get; set; }
|
/// <summary>
|
||||||
|
/// This table stores the offsets to the locations of the glyphs (relative to the glyph table).
|
||||||
|
/// </summary>
|
||||||
|
[NotNull]
|
||||||
|
public IndexToLocationTable IndexToLocationTable { get; }
|
||||||
|
|
||||||
public BasicMaximumProfileTable MaximumProfileTable { get; set; }
|
/// <summary>
|
||||||
|
/// This table establishes the memory requirements for the font.
|
||||||
|
/// </summary>
|
||||||
|
[NotNull]
|
||||||
|
public BasicMaximumProfileTable MaximumProfileTable { get; }
|
||||||
|
|
||||||
public PostScriptTable PostScriptTable { get; set; }
|
public PostScriptTable PostScriptTable { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Defines mapping of character codes to glyph index values in the font.
|
/// Defines mapping of character codes to glyph index values in the font.
|
||||||
/// Can contain multiple sub-tables to support multiple encoding schemes.
|
/// Can contain multiple sub-tables to support multiple encoding schemes.
|
||||||
/// Where a character code isn't found it should map to index 0.
|
/// Where a character code isn't found it should map to index 0.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public CMapTable CMapTable { get; set; }
|
public CMapTable CMapTable { get; }
|
||||||
|
|
||||||
public KerningTable KerningTable { get; set; }
|
public KerningTable KerningTable { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create a new <see cref="TableRegister"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="builder">The builder with necessary tables set.</param>
|
||||||
|
public TableRegister([NotNull] Builder builder)
|
||||||
|
{
|
||||||
|
if (builder == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(builder));
|
||||||
|
}
|
||||||
|
|
||||||
|
HeaderTable = builder.HeaderTable ?? throw new ArgumentException("The builder did not contain the header table");
|
||||||
|
GlyphTable = builder.GlyphDataTable ?? throw new ArgumentException("The builder did not contain the glyph data table.");
|
||||||
|
HorizontalHeaderTable = builder.HorizontalHeaderTable ?? throw new ArgumentException("The builder did not contain the horizontal header table.");
|
||||||
|
HorizontalMetricsTable = builder.HorizontalMetricsTable;
|
||||||
|
IndexToLocationTable = builder.IndexToLocationTable ?? throw new ArgumentException("The builder did not contain the index to location table.");
|
||||||
|
MaximumProfileTable = builder.MaximumProfileTable ?? throw new ArgumentException("The builder did not contain the maximum profile table.");
|
||||||
|
PostScriptTable = builder.PostScriptTable;
|
||||||
|
CMapTable = builder.CMapTable;
|
||||||
|
KerningTable = builder.KerningTable;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Used to gather the necessary tables for a TrueType font.
|
||||||
|
/// </summary>
|
||||||
|
public class Builder
|
||||||
|
{
|
||||||
|
public GlyphDataTable GlyphDataTable { get; set; }
|
||||||
|
|
||||||
|
public HeaderTable HeaderTable { get; set; }
|
||||||
|
|
||||||
|
public HorizontalHeaderTable HorizontalHeaderTable { get; set; }
|
||||||
|
|
||||||
|
public HorizontalMetricsTable HorizontalMetricsTable { get; set; }
|
||||||
|
|
||||||
|
public IndexToLocationTable IndexToLocationTable { get; set; }
|
||||||
|
|
||||||
|
public BasicMaximumProfileTable MaximumProfileTable { get; set; }
|
||||||
|
|
||||||
|
public PostScriptTable PostScriptTable { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Defines mapping of character codes to glyph index values in the font.
|
||||||
|
/// Can contain multiple sub-tables to support multiple encoding schemes.
|
||||||
|
/// Where a character code isn't found it should map to index 0.
|
||||||
|
/// </summary>
|
||||||
|
public CMapTable CMapTable { get; set; }
|
||||||
|
|
||||||
|
public KerningTable KerningTable { get; set; }
|
||||||
|
|
||||||
|
public TableRegister Build()
|
||||||
|
{
|
||||||
|
return new TableRegister(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -57,7 +57,7 @@
|
|||||||
{
|
{
|
||||||
var isPostScript = tables.ContainsKey(TrueTypeHeaderTable.Cff);
|
var isPostScript = tables.ContainsKey(TrueTypeHeaderTable.Cff);
|
||||||
|
|
||||||
var tableRegister = new TableRegister();
|
var builder = new TableRegister.Builder();
|
||||||
|
|
||||||
if (!tables.TryGetValue(TrueTypeHeaderTable.Head, out var table))
|
if (!tables.TryGetValue(TrueTypeHeaderTable.Head, out var table))
|
||||||
{
|
{
|
||||||
@@ -65,7 +65,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
// head
|
// head
|
||||||
tableRegister.HeaderTable = HeaderTable.Load(data, table);
|
builder.HeaderTable = HeaderTable.Load(data, table);
|
||||||
|
|
||||||
if (!tables.TryGetValue(TrueTypeHeaderTable.Hhea, out var hHead))
|
if (!tables.TryGetValue(TrueTypeHeaderTable.Hhea, out var hHead))
|
||||||
{
|
{
|
||||||
@@ -73,7 +73,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
// hhea
|
// hhea
|
||||||
tableRegister.HorizontalHeaderTable = HorizontalHeaderTable.Load(data, hHead);
|
builder.HorizontalHeaderTable = HorizontalHeaderTable.Load(data, hHead);
|
||||||
|
|
||||||
if (!tables.TryGetValue(TrueTypeHeaderTable.Maxp, out var maxHeaderTable))
|
if (!tables.TryGetValue(TrueTypeHeaderTable.Maxp, out var maxHeaderTable))
|
||||||
{
|
{
|
||||||
@@ -81,12 +81,12 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
// maxp
|
// maxp
|
||||||
tableRegister.MaximumProfileTable = BasicMaximumProfileTable.Load(data, maxHeaderTable);
|
builder.MaximumProfileTable = BasicMaximumProfileTable.Load(data, maxHeaderTable);
|
||||||
|
|
||||||
// post
|
// post
|
||||||
if (tables.TryGetValue(TrueTypeHeaderTable.Post, out var postscriptHeaderTable))
|
if (tables.TryGetValue(TrueTypeHeaderTable.Post, out var postscriptHeaderTable))
|
||||||
{
|
{
|
||||||
tableRegister.PostScriptTable = PostScriptTable.Load(data, postscriptHeaderTable, tableRegister.MaximumProfileTable);
|
builder.PostScriptTable = PostScriptTable.Load(data, postscriptHeaderTable, builder.MaximumProfileTable);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isPostScript)
|
if (!isPostScript)
|
||||||
@@ -97,8 +97,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
// loca
|
// loca
|
||||||
tableRegister.IndexToLocationTable =
|
builder.IndexToLocationTable =
|
||||||
IndexToLocationTable.Load(data, indexToLocationHeaderTable, tableRegister);
|
IndexToLocationTable.Load(data, indexToLocationHeaderTable, builder);
|
||||||
|
|
||||||
if (!tables.TryGetValue(TrueTypeHeaderTable.Glyf, out var glyphHeaderTable))
|
if (!tables.TryGetValue(TrueTypeHeaderTable.Glyf, out var glyphHeaderTable))
|
||||||
{
|
{
|
||||||
@@ -106,15 +106,15 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
// glyf
|
// glyf
|
||||||
tableRegister.GlyphDataTable = GlyphDataTable.Load(data, glyphHeaderTable, tableRegister);
|
builder.GlyphDataTable = GlyphDataTable.Load(data, glyphHeaderTable, builder);
|
||||||
|
|
||||||
OptionallyParseTables(tables, data, tableRegister);
|
OptionallyParseTables(tables, data, builder);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new TrueTypeFontProgram(version, tables, tableRegister);
|
return new TrueTypeFontProgram(version, tables, builder.Build());
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void OptionallyParseTables(IReadOnlyDictionary<string, TrueTypeHeaderTable> tables, TrueTypeDataBytes data, TableRegister tableRegister)
|
private static void OptionallyParseTables(IReadOnlyDictionary<string, TrueTypeHeaderTable> tables, TrueTypeDataBytes data, TableRegister.Builder tableRegister)
|
||||||
{
|
{
|
||||||
// cmap
|
// cmap
|
||||||
if (tables.TryGetValue(TrueTypeHeaderTable.Cmap, out var cmap))
|
if (tables.TryGetValue(TrueTypeHeaderTable.Cmap, out var cmap))
|
||||||
|
@@ -76,7 +76,7 @@
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static CMapTable Load(TrueTypeDataBytes data, TrueTypeHeaderTable table, TableRegister tableRegister)
|
public static CMapTable Load(TrueTypeDataBytes data, TrueTypeHeaderTable table, TableRegister.Builder tableRegister)
|
||||||
{
|
{
|
||||||
data.Seek(table.Offset);
|
data.Seek(table.Offset);
|
||||||
|
|
||||||
|
@@ -8,7 +8,8 @@
|
|||||||
using Util.JetBrains.Annotations;
|
using Util.JetBrains.Annotations;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Describes the glyphs in the font.
|
/// The 'glyf' table contains the data that defines the appearance of the glyphs in the font.
|
||||||
|
/// This includes specification of the points that describe the contours that make up a glyph outline and the instructions that grid-fit that glyph.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal class GlyphDataTable : ITable
|
internal class GlyphDataTable : ITable
|
||||||
{
|
{
|
||||||
@@ -25,7 +26,7 @@
|
|||||||
Glyphs = glyphs ?? throw new ArgumentNullException(nameof(glyphs));
|
Glyphs = glyphs ?? throw new ArgumentNullException(nameof(glyphs));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static GlyphDataTable Load(TrueTypeDataBytes data, TrueTypeHeaderTable table, TableRegister tableRegister)
|
public static GlyphDataTable Load(TrueTypeDataBytes data, TrueTypeHeaderTable table, TableRegister.Builder tableRegister)
|
||||||
{
|
{
|
||||||
data.Seek(table.Offset);
|
data.Seek(table.Offset);
|
||||||
|
|
||||||
|
@@ -4,7 +4,8 @@
|
|||||||
using Geometry;
|
using Geometry;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gives global information about the font.
|
/// The 'head' table contains global information about the font.
|
||||||
|
/// It contains things like as the font version number, the creation and modification dates, revision number and basic typographic data that applies to the font as a whole.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal class HeaderTable : ITable
|
internal class HeaderTable : ITable
|
||||||
{
|
{
|
||||||
|
@@ -2,6 +2,10 @@
|
|||||||
{
|
{
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The 'hhea' table contains information needed to layout fonts whose characters are written horizontally, that is, either left to right or right to left.
|
||||||
|
/// This table contains information that is general to the font as a whole.
|
||||||
|
/// </summary>
|
||||||
internal class HorizontalHeaderTable : ITable
|
internal class HorizontalHeaderTable : ITable
|
||||||
{
|
{
|
||||||
public string Tag => TrueTypeHeaderTable.Hhea;
|
public string Tag => TrueTypeHeaderTable.Hhea;
|
||||||
|
@@ -2,6 +2,9 @@
|
|||||||
{
|
{
|
||||||
using Parser;
|
using Parser;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The 'hmtx' table contains metric information for the horizontal layout each of the glyphs in the font.
|
||||||
|
/// </summary>
|
||||||
internal class HorizontalMetricsTable : ITable
|
internal class HorizontalMetricsTable : ITable
|
||||||
{
|
{
|
||||||
private readonly int[] advancedWidths;
|
private readonly int[] advancedWidths;
|
||||||
@@ -22,7 +25,7 @@
|
|||||||
DirectoryTable = directoryTable;
|
DirectoryTable = directoryTable;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static HorizontalMetricsTable Load(TrueTypeDataBytes data, TrueTypeHeaderTable table, TableRegister tableRegister)
|
public static HorizontalMetricsTable Load(TrueTypeDataBytes data, TrueTypeHeaderTable table, TableRegister.Builder tableRegister)
|
||||||
{
|
{
|
||||||
var glyphCount = tableRegister.MaximumProfileTable.NumberOfGlyphs;
|
var glyphCount = tableRegister.MaximumProfileTable.NumberOfGlyphs;
|
||||||
var metricCount = tableRegister.HorizontalHeaderTable.NumberOfHeaderMetrics;
|
var metricCount = tableRegister.HorizontalHeaderTable.NumberOfHeaderMetrics;
|
||||||
|
@@ -25,7 +25,7 @@
|
|||||||
GlyphOffsets = glyphOffsets;
|
GlyphOffsets = glyphOffsets;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IndexToLocationTable Load(TrueTypeDataBytes data, TrueTypeHeaderTable table, TableRegister tableRegister)
|
public static IndexToLocationTable Load(TrueTypeDataBytes data, TrueTypeHeaderTable table, TableRegister.Builder tableRegister)
|
||||||
{
|
{
|
||||||
const short shortFormat = 0;
|
const short shortFormat = 0;
|
||||||
const short longFormat = 1;
|
const short longFormat = 1;
|
||||||
|
@@ -64,6 +64,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
internal class MaximumProfileTable : BasicMaximumProfileTable
|
internal class MaximumProfileTable : BasicMaximumProfileTable
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -131,6 +132,9 @@
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public int MaximumComponentDepth { get; }
|
public int MaximumComponentDepth { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create a new <see cref="MaximumProfileTable"/>.
|
||||||
|
/// </summary>
|
||||||
public MaximumProfileTable(TrueTypeHeaderTable directoryTable, float version, int numberOfGlyphs, int maximumPoints, int maximumContours, int maximumCompositePoints, int maximumCompositeContours, int maximumZones, int maximumTwilightPoints, int maximumStorage, int maximumFunctionDefinitions, int maximumInstructionDefinitions, int maximumStackElements, int maximumSizeOfInstructions, int maximumComponentElements, int maximumComponentDepth) : base(directoryTable, version, numberOfGlyphs)
|
public MaximumProfileTable(TrueTypeHeaderTable directoryTable, float version, int numberOfGlyphs, int maximumPoints, int maximumContours, int maximumCompositePoints, int maximumCompositeContours, int maximumZones, int maximumTwilightPoints, int maximumStorage, int maximumFunctionDefinitions, int maximumInstructionDefinitions, int maximumStackElements, int maximumSizeOfInstructions, int maximumComponentElements, int maximumComponentDepth) : base(directoryTable, version, numberOfGlyphs)
|
||||||
{
|
{
|
||||||
MaximumPoints = maximumPoints;
|
MaximumPoints = maximumPoints;
|
||||||
|
@@ -5,7 +5,6 @@
|
|||||||
using CidFonts;
|
using CidFonts;
|
||||||
using Geometry;
|
using Geometry;
|
||||||
using Parser;
|
using Parser;
|
||||||
using Tables;
|
|
||||||
|
|
||||||
internal class TrueTypeFontProgram : ICidFontProgram
|
internal class TrueTypeFontProgram : ICidFontProgram
|
||||||
{
|
{
|
||||||
@@ -13,9 +12,6 @@
|
|||||||
|
|
||||||
public IReadOnlyDictionary<string, TrueTypeHeaderTable> TableHeaders { get; }
|
public IReadOnlyDictionary<string, TrueTypeHeaderTable> TableHeaders { get; }
|
||||||
|
|
||||||
public HeaderTable HeaderTable { get; }
|
|
||||||
public CMapTable CMapTable { get; }
|
|
||||||
public GlyphDataTable GlyphTable { get; }
|
|
||||||
public TableRegister TableRegister { get; }
|
public TableRegister TableRegister { get; }
|
||||||
|
|
||||||
public TrueTypeFontProgram(decimal version, IReadOnlyDictionary<string, TrueTypeHeaderTable> tableHeaders, TableRegister tableRegister)
|
public TrueTypeFontProgram(decimal version, IReadOnlyDictionary<string, TrueTypeHeaderTable> tableHeaders, TableRegister tableRegister)
|
||||||
@@ -23,9 +19,6 @@
|
|||||||
Version = version;
|
Version = version;
|
||||||
TableHeaders = tableHeaders;
|
TableHeaders = tableHeaders;
|
||||||
TableRegister = tableRegister ?? throw new ArgumentNullException(nameof(tableRegister));
|
TableRegister = tableRegister ?? throw new ArgumentNullException(nameof(tableRegister));
|
||||||
HeaderTable = tableRegister.HeaderTable;
|
|
||||||
CMapTable = tableRegister.CMapTable;
|
|
||||||
GlyphTable = tableRegister.GlyphDataTable;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool TryGetBoundingBox(int characterIdentifier, out PdfRectangle boundingBox) => TryGetBoundingBox(characterIdentifier, null, out boundingBox);
|
public bool TryGetBoundingBox(int characterIdentifier, out PdfRectangle boundingBox) => TryGetBoundingBox(characterIdentifier, null, out boundingBox);
|
||||||
@@ -38,7 +31,7 @@
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
var glyph = GlyphTable.Glyphs[index];
|
var glyph = TableRegister.GlyphTable.Glyphs[index];
|
||||||
|
|
||||||
if (glyph?.Bounds == null)
|
if (glyph?.Bounds == null)
|
||||||
{
|
{
|
||||||
@@ -72,7 +65,7 @@
|
|||||||
|
|
||||||
public int GetFontMatrixMultiplier()
|
public int GetFontMatrixMultiplier()
|
||||||
{
|
{
|
||||||
return HeaderTable?.UnitsPerEm ?? 1000;
|
return TableRegister.HeaderTable.UnitsPerEm;
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool TryGetBoundingAdvancedWidthByIndex(int index, out decimal width)
|
private bool TryGetBoundingAdvancedWidthByIndex(int index, out decimal width)
|
||||||
@@ -93,17 +86,12 @@
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (CMapTable == null)
|
if (TableRegister.CMapTable == null)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!CMapTable.TryGetGlyphIndex(characterIdentifier, out glyphIndex))
|
return TableRegister.CMapTable.TryGetGlyphIndex(characterIdentifier, out glyphIndex);
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -1,5 +1,8 @@
|
|||||||
namespace UglyToad.PdfPig.Fonts.TrueType
|
namespace UglyToad.PdfPig.Fonts.TrueType
|
||||||
{
|
{
|
||||||
|
using System;
|
||||||
|
using Util.JetBrains.Annotations;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A table directory entry from the TrueType font file.
|
/// A table directory entry from the TrueType font file.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -161,6 +164,7 @@
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The 4 byte tag identifying the table.
|
/// The 4 byte tag identifying the table.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[NotNull]
|
||||||
public string Tag { get; }
|
public string Tag { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -180,7 +184,7 @@
|
|||||||
|
|
||||||
public TrueTypeHeaderTable(string tag, long checkSum, long offset, long length)
|
public TrueTypeHeaderTable(string tag, long checkSum, long offset, long length)
|
||||||
{
|
{
|
||||||
Tag = tag;
|
Tag = tag ?? throw new ArgumentNullException(nameof(tag));
|
||||||
CheckSum = checkSum;
|
CheckSum = checkSum;
|
||||||
Offset = offset;
|
Offset = offset;
|
||||||
Length = length;
|
Length = length;
|
||||||
|
@@ -1,5 +1,7 @@
|
|||||||
namespace UglyToad.PdfPig.Geometry
|
namespace UglyToad.PdfPig.Geometry
|
||||||
{
|
{
|
||||||
|
using System.Diagnostics;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A point in a PDF file.
|
/// A point in a PDF file.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -28,6 +30,7 @@
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Create a new <see cref="PdfPoint"/> at this position.
|
/// Create a new <see cref="PdfPoint"/> at this position.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[DebuggerStepThrough]
|
||||||
public PdfPoint(decimal x, decimal y)
|
public PdfPoint(decimal x, decimal y)
|
||||||
{
|
{
|
||||||
X = x;
|
X = x;
|
||||||
@@ -37,6 +40,7 @@
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Create a new <see cref="PdfPoint"/> at this position.
|
/// Create a new <see cref="PdfPoint"/> at this position.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[DebuggerStepThrough]
|
||||||
public PdfPoint(int x, int y)
|
public PdfPoint(int x, int y)
|
||||||
{
|
{
|
||||||
X = x;
|
X = x;
|
||||||
@@ -46,6 +50,7 @@
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Create a new <see cref="PdfPoint"/> at this position.
|
/// Create a new <see cref="PdfPoint"/> at this position.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[DebuggerStepThrough]
|
||||||
public PdfPoint(double x, double y)
|
public PdfPoint(double x, double y)
|
||||||
{
|
{
|
||||||
X = (decimal)x;
|
X = (decimal)x;
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
namespace UglyToad.PdfPig.Writer
|
namespace UglyToad.PdfPig.Writer
|
||||||
{
|
{
|
||||||
using System;
|
using System;
|
||||||
|
using Fonts.TrueType;
|
||||||
using Geometry;
|
using Geometry;
|
||||||
|
|
||||||
internal class PdfPageBuilder
|
internal class PdfPageBuilder
|
||||||
@@ -16,5 +17,53 @@
|
|||||||
this.documentBuilder = documentBuilder ?? throw new ArgumentNullException(nameof(documentBuilder));
|
this.documentBuilder = documentBuilder ?? throw new ArgumentNullException(nameof(documentBuilder));
|
||||||
PageNumber = number;
|
PageNumber = number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public PdfPageBuilder AddText(string text, decimal fontSize, PdfPoint position, PdfDocumentBuilder.AddedFont font)
|
||||||
|
{
|
||||||
|
if (font == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(font));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (text == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(text));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!documentBuilder.Fonts.TryGetValue(font.Id, out var fontProgram))
|
||||||
|
{
|
||||||
|
throw new ArgumentException($"No font has been added to the PdfDocumentBuilder with Id: {font.Id}. " +
|
||||||
|
$"Use {nameof(documentBuilder.AddTrueTypeFont)} to register a font.", nameof(font));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fontSize <= 0)
|
||||||
|
{
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(fontSize), "Font size must be greater than 0");
|
||||||
|
}
|
||||||
|
|
||||||
|
var width = CalculateGlyphSpaceTextWidth(text, fontProgram);
|
||||||
|
|
||||||
|
Console.WriteLine(width);
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static decimal CalculateGlyphSpaceTextWidth(string text, TrueTypeFontProgram font)
|
||||||
|
{
|
||||||
|
var width = 0m;
|
||||||
|
for (var i = 0; i < text.Length; i++)
|
||||||
|
{
|
||||||
|
var c = text[i];
|
||||||
|
|
||||||
|
if(!font.TryGetBoundingBox(c, out var rect))
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException($"The font does not contain a character: {c}.");
|
||||||
|
}
|
||||||
|
|
||||||
|
width += rect.Width;
|
||||||
|
}
|
||||||
|
|
||||||
|
return width;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -4,15 +4,38 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Runtime.InteropServices.ComTypes;
|
using System.Runtime.InteropServices.ComTypes;
|
||||||
using Content;
|
using Content;
|
||||||
|
using Fonts.TrueType;
|
||||||
|
using Fonts.TrueType.Parser;
|
||||||
using Geometry;
|
using Geometry;
|
||||||
|
using IO;
|
||||||
|
|
||||||
internal class PdfDocumentBuilder
|
internal class PdfDocumentBuilder
|
||||||
{
|
{
|
||||||
|
private static readonly TrueTypeFontParser Parser = new TrueTypeFontParser();
|
||||||
|
|
||||||
private readonly Dictionary<int, PdfPageBuilder> pages = new Dictionary<int, PdfPageBuilder>();
|
private readonly Dictionary<int, PdfPageBuilder> pages = new Dictionary<int, PdfPageBuilder>();
|
||||||
|
private readonly Dictionary<Guid, TrueTypeFontProgram> fonts = new Dictionary<Guid, TrueTypeFontProgram>();
|
||||||
|
|
||||||
public IReadOnlyDictionary<int, PdfPageBuilder> Pages => pages;
|
public IReadOnlyDictionary<int, PdfPageBuilder> Pages => pages;
|
||||||
|
public IReadOnlyDictionary<Guid, TrueTypeFontProgram> Fonts => fonts;
|
||||||
|
|
||||||
public PdfPageBuilder AddPage(PageSize size, bool isPortrait)
|
public AddedFont AddTrueTypeFont(IReadOnlyList<byte> fontFileBytes)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var font = Parser.Parse(new TrueTypeDataBytes(new ByteArrayInputBytes(fontFileBytes)));
|
||||||
|
var id = Guid.NewGuid();
|
||||||
|
fonts[id] = font;
|
||||||
|
|
||||||
|
return new AddedFont(id);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Writing only supports TrueType fonts, please provide a valid TrueType font.", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public PdfPageBuilder AddPage(PageSize size, bool isPortrait = true)
|
||||||
{
|
{
|
||||||
if (!size.TryGetPdfRectangle(out var rectangle))
|
if (!size.TryGetPdfRectangle(out var rectangle))
|
||||||
{
|
{
|
||||||
@@ -54,5 +77,16 @@
|
|||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class AddedFont
|
||||||
|
{
|
||||||
|
public Guid Id { get; }
|
||||||
|
|
||||||
|
internal AddedFont(Guid id)
|
||||||
|
{
|
||||||
|
Id = id;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user