diff --git a/src/UglyToad.PdfPig/Fonts/TrueType/Glyphs/CompositeGlyphDescription.cs b/src/UglyToad.PdfPig/Fonts/TrueType/Glyphs/CompositeGlyphDescription.cs
new file mode 100644
index 00000000..cb64005a
--- /dev/null
+++ b/src/UglyToad.PdfPig/Fonts/TrueType/Glyphs/CompositeGlyphDescription.cs
@@ -0,0 +1,20 @@
+namespace UglyToad.PdfPig.Fonts.TrueType.Glyphs
+{
+ using Geometry;
+
+ internal class CompositeGlyphDescription : IGlyphDescription
+ {
+ public bool IsSimple { get; } = false;
+
+ public SimpleGlyphDescription SimpleGlyph { get; } = null;
+
+ public CompositeGlyphDescription CompositeGlyph => this;
+
+ public PdfRectangle GlyphBounds { get; }
+
+ public IGlyphDescription DeepClone()
+ {
+ return new CompositeGlyphDescription();
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/UglyToad.PdfPig/Fonts/TrueType/Tables/CompositeGlyphFlags.cs b/src/UglyToad.PdfPig/Fonts/TrueType/Glyphs/CompositeGlyphFlags.cs
similarity index 97%
rename from src/UglyToad.PdfPig/Fonts/TrueType/Tables/CompositeGlyphFlags.cs
rename to src/UglyToad.PdfPig/Fonts/TrueType/Glyphs/CompositeGlyphFlags.cs
index 0561293d..3692c4f7 100644
--- a/src/UglyToad.PdfPig/Fonts/TrueType/Tables/CompositeGlyphFlags.cs
+++ b/src/UglyToad.PdfPig/Fonts/TrueType/Glyphs/CompositeGlyphFlags.cs
@@ -1,4 +1,4 @@
-namespace UglyToad.PdfPig.Fonts.TrueType.Tables
+namespace UglyToad.PdfPig.Fonts.TrueType.Glyphs
{
using System;
diff --git a/src/UglyToad.PdfPig/Fonts/TrueType/Glyphs/EmptyGlyph.cs b/src/UglyToad.PdfPig/Fonts/TrueType/Glyphs/EmptyGlyph.cs
new file mode 100644
index 00000000..9594745d
--- /dev/null
+++ b/src/UglyToad.PdfPig/Fonts/TrueType/Glyphs/EmptyGlyph.cs
@@ -0,0 +1,25 @@
+namespace UglyToad.PdfPig.Fonts.TrueType.Glyphs
+{
+ using Geometry;
+
+ internal class EmptyGlyph : IGlyphDescription
+ {
+ public bool IsSimple { get; } = true;
+
+ public SimpleGlyphDescription SimpleGlyph => new SimpleGlyphDescription(new byte[0], new int[0], new GlyphPoint[0], GlyphBounds);
+
+ public CompositeGlyphDescription CompositeGlyph { get; } = null;
+
+ public PdfRectangle GlyphBounds { get; }
+
+ public EmptyGlyph(PdfRectangle glyphBounds)
+ {
+ GlyphBounds = glyphBounds;
+ }
+
+ public IGlyphDescription DeepClone()
+ {
+ return new EmptyGlyph(GlyphBounds);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/UglyToad.PdfPig/Fonts/TrueType/Glyphs/GlyphPoint.cs b/src/UglyToad.PdfPig/Fonts/TrueType/Glyphs/GlyphPoint.cs
new file mode 100644
index 00000000..c393239b
--- /dev/null
+++ b/src/UglyToad.PdfPig/Fonts/TrueType/Glyphs/GlyphPoint.cs
@@ -0,0 +1,23 @@
+namespace UglyToad.PdfPig.Fonts.TrueType.Glyphs
+{
+ using Geometry;
+
+ internal struct GlyphPoint
+ {
+ public PdfPoint Point { get; }
+
+ public bool IsOnCurve { get; }
+
+ public GlyphPoint(decimal x, decimal y, bool isOnCurve) : this(new PdfPoint(x, y), isOnCurve) { }
+ public GlyphPoint(PdfPoint point, bool isOnCurve)
+ {
+ Point = point;
+ IsOnCurve = isOnCurve;
+ }
+
+ public override string ToString()
+ {
+ return $"{Point} | {IsOnCurve}";
+ }
+ }
+}
diff --git a/src/UglyToad.PdfPig/Fonts/TrueType/Tables/IGlyphDescription.cs b/src/UglyToad.PdfPig/Fonts/TrueType/Glyphs/IGlyphDescription.cs
similarity index 81%
rename from src/UglyToad.PdfPig/Fonts/TrueType/Tables/IGlyphDescription.cs
rename to src/UglyToad.PdfPig/Fonts/TrueType/Glyphs/IGlyphDescription.cs
index f2449935..095e27ff 100644
--- a/src/UglyToad.PdfPig/Fonts/TrueType/Tables/IGlyphDescription.cs
+++ b/src/UglyToad.PdfPig/Fonts/TrueType/Glyphs/IGlyphDescription.cs
@@ -1,6 +1,5 @@
-namespace UglyToad.PdfPig.Fonts.TrueType.Tables
+namespace UglyToad.PdfPig.Fonts.TrueType.Glyphs
{
- using System;
using Geometry;
internal interface IGlyphDescription
diff --git a/src/UglyToad.PdfPig/Fonts/TrueType/Glyphs/SimpleGlyphDescription.cs b/src/UglyToad.PdfPig/Fonts/TrueType/Glyphs/SimpleGlyphDescription.cs
new file mode 100644
index 00000000..3c8417e4
--- /dev/null
+++ b/src/UglyToad.PdfPig/Fonts/TrueType/Glyphs/SimpleGlyphDescription.cs
@@ -0,0 +1,54 @@
+namespace UglyToad.PdfPig.Fonts.TrueType.Glyphs
+{
+ using System;
+ using Geometry;
+
+ internal class SimpleGlyphDescription : IGlyphDescription
+ {
+ ///
+ /// The bounding rectangle for the character.
+ ///
+ public PdfRectangle GlyphBounds { get; }
+
+ ///
+ /// The total number of bytes for instructions.
+ ///
+ public byte[] Instructions { get; }
+
+ ///
+ /// An array of the last points of each contour.
+ ///
+ public int[] EndPointsOfContours { get; }
+
+ public GlyphPoint[] Points { get; set; }
+
+ public SimpleGlyphDescription(byte[] instructions, int[] endPointsOfContours, GlyphPoint[] points,
+ PdfRectangle bounds)
+ {
+ Instructions = instructions;
+ EndPointsOfContours = endPointsOfContours;
+ Points = points;
+ GlyphBounds = bounds;
+ }
+
+ public bool IsSimple { get; } = true;
+
+ public SimpleGlyphDescription SimpleGlyph => this;
+
+ public CompositeGlyphDescription CompositeGlyph { get; } = null;
+
+ public IGlyphDescription DeepClone()
+ {
+ var clonedInstructions = new byte[Instructions.Length];
+ Array.Copy(Instructions, clonedInstructions, Instructions.Length);
+
+ var clonedEndPoints = new int[EndPointsOfContours.Length];
+ Array.Copy(EndPointsOfContours, clonedEndPoints, EndPointsOfContours.Length);
+
+ var clonedPoints = new GlyphPoint[Points.Length];
+ Array.Copy(Points, clonedPoints, Points.Length);
+
+ return new SimpleGlyphDescription(clonedInstructions, clonedEndPoints, clonedPoints, GlyphBounds);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/UglyToad.PdfPig/Fonts/TrueType/Tables/SimpleGlyphFlags.cs b/src/UglyToad.PdfPig/Fonts/TrueType/Glyphs/SimpleGlyphFlags.cs
similarity index 95%
rename from src/UglyToad.PdfPig/Fonts/TrueType/Tables/SimpleGlyphFlags.cs
rename to src/UglyToad.PdfPig/Fonts/TrueType/Glyphs/SimpleGlyphFlags.cs
index b7c772b9..269f4141 100644
--- a/src/UglyToad.PdfPig/Fonts/TrueType/Tables/SimpleGlyphFlags.cs
+++ b/src/UglyToad.PdfPig/Fonts/TrueType/Glyphs/SimpleGlyphFlags.cs
@@ -1,4 +1,4 @@
-namespace UglyToad.PdfPig.Fonts.TrueType.Tables
+namespace UglyToad.PdfPig.Fonts.TrueType.Glyphs
{
using System;
diff --git a/src/UglyToad.PdfPig/Fonts/TrueType/Tables/GlyphDataTable.cs b/src/UglyToad.PdfPig/Fonts/TrueType/Tables/GlyphDataTable.cs
index ebd4a80f..8cd6bf92 100644
--- a/src/UglyToad.PdfPig/Fonts/TrueType/Tables/GlyphDataTable.cs
+++ b/src/UglyToad.PdfPig/Fonts/TrueType/Tables/GlyphDataTable.cs
@@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
using Geometry;
+ using Glyphs;
using Parser;
using Util.JetBrains.Annotations;
@@ -88,7 +89,7 @@
var instructionLength = data.ReadUnsignedShort();
- data.ReadByteArray(instructionLength);
+ var instructions = data.ReadByteArray(instructionLength);
var pointCount = 0;
if (contourCount > 0)
@@ -104,7 +105,14 @@
var yCoordinates = ReadCoordinates(data, pointCount, flags, SimpleGlyphFlags.YShortVector,
SimpleGlyphFlags.YSignOrSame);
- return new SimpleGlyphDescription(instructionLength, endPointsOfContours, flags, xCoordinates, yCoordinates, bounds);
+ var points = new GlyphPoint[xCoordinates.Length];
+ for (var i = xCoordinates.Length - 1; i >= 0; i--)
+ {
+ var isOnCurve = (flags[i] & SimpleGlyphFlags.OnCurve) == SimpleGlyphFlags.OnCurve;
+ points[i] = new GlyphPoint(xCoordinates[i], yCoordinates[i], isOnCurve);
+ }
+
+ return new SimpleGlyphDescription(instructions, endPointsOfContours, points, bounds);
}
private static CompositeGlyphDescription ReadCompositeGlyph(TrueTypeDataBytes data, TemporaryCompositeLocation compositeLocation, Dictionary compositeLocations, IGlyphDescription[] glyphs)
@@ -116,6 +124,8 @@
data.Seek(compositeLocation.Position);
+ var components = new List();
+
CompositeGlyphFlags flags;
do
{
@@ -150,42 +160,32 @@
arg2 = data.ReadByte();
}
- float xscale = 1;
- float scale01 = 0;
- float scale10 = 0;
- float yscale = 1;
+ decimal xscale = 1;
+ decimal scale01 = 0;
+ decimal scale10 = 0;
+ decimal yscale = 1;
- bool hasScale, hasMatrix = false;
if (HasFlag(flags, CompositeGlyphFlags.WeHaveAScale))
{
xscale = ReadTwoFourteenFormat(data);
yscale = xscale;
- hasScale = true;
}
else if (HasFlag(flags, CompositeGlyphFlags.WeHaveAnXAndYScale))
{
xscale = ReadTwoFourteenFormat(data);
yscale = ReadTwoFourteenFormat(data);
- hasScale = true;
}
else if (HasFlag(flags, CompositeGlyphFlags.WeHaveATwoByTwo))
{
- /*
- * We build the 2 by 2 matrix:
- * x 0
- * 0 y
- */
xscale = ReadTwoFourteenFormat(data);
scale01 = ReadTwoFourteenFormat(data);
scale10 = ReadTwoFourteenFormat(data);
yscale = ReadTwoFourteenFormat(data);
- hasScale = true;
- hasMatrix = true;
}
if (HasFlag(flags, CompositeGlyphFlags.ArgsAreXAndYValues))
{
-
+ components.Add(new CompositeComponent(glyphIndex, new PdfMatrix3By2(xscale, scale01, scale10, yscale, arg1, arg2)));
}
else
{
@@ -197,9 +197,9 @@
return new CompositeGlyphDescription();
}
- private static float ReadTwoFourteenFormat(TrueTypeDataBytes data)
+ private static decimal ReadTwoFourteenFormat(TrueTypeDataBytes data)
{
- const float divisor = 1 << 14;
+ const decimal divisor = 1 << 14;
return data.ReadSignedShort() / divisor;
}
@@ -281,5 +281,18 @@
}
}
}
+
+ private class CompositeComponent
+ {
+ public int Index { get; }
+
+ public PdfMatrix3By2 Transformation { get; }
+
+ public CompositeComponent(int index, PdfMatrix3By2 transformation)
+ {
+ Index = index;
+ Transformation = transformation;
+ }
+ }
}
}
diff --git a/src/UglyToad.PdfPig/Fonts/TrueType/Tables/SimpleGlyphDescription.cs b/src/UglyToad.PdfPig/Fonts/TrueType/Tables/SimpleGlyphDescription.cs
deleted file mode 100644
index 2a06a386..00000000
--- a/src/UglyToad.PdfPig/Fonts/TrueType/Tables/SimpleGlyphDescription.cs
+++ /dev/null
@@ -1,110 +0,0 @@
-namespace UglyToad.PdfPig.Fonts.TrueType.Tables
-{
- using System;
- using Geometry;
-
- internal class SimpleGlyphDescription : IGlyphDescription
- {
- ///
- /// The bounding rectangle for the character.
- ///
- public PdfRectangle GlyphBounds { get; }
-
- ///
- /// The total number of bytes for instructions.
- ///
- public int InstructionLength { get; }
-
- ///
- /// An array of the last points of each contour.
- ///
- public int[] EndPointsOfContours { get; }
-
- ///
- /// Array of flags for each coordinate in the outline.
- ///
- public SimpleGlyphFlags[] Flags { get; }
-
- ///
- /// The x-coordinates of the points in this glyph. The first coordinates are relative to the origin (0, 0)
- /// the rest are relative to the previous point.
- ///
- public short[] XCoordinates { get; }
-
- ///
- /// The y-coordinates of the points in this glyph. The first coordinates are relative to the origin (0, 0)
- /// the rest are relative to the previous point.
- ///
- public short[] YCoordinates { get; }
-
- public SimpleGlyphDescription(int instructionLength, int[] endPointsOfContours, SimpleGlyphFlags[] flags, short[] xCoordinates, short[] yCoordinates,
- PdfRectangle bounds)
- {
- InstructionLength = instructionLength;
- EndPointsOfContours = endPointsOfContours;
- Flags = flags;
- XCoordinates = xCoordinates;
- YCoordinates = yCoordinates;
- GlyphBounds = bounds;
- }
-
- public bool IsSimple { get; } = true;
-
- public SimpleGlyphDescription SimpleGlyph => this;
-
- public CompositeGlyphDescription CompositeGlyph { get; } = null;
-
- public IGlyphDescription DeepClone()
- {
- var clonedEndPoints = new int[EndPointsOfContours.Length];
- Array.Copy(EndPointsOfContours, clonedEndPoints, EndPointsOfContours.Length);
-
- var clonedFlags = new SimpleGlyphFlags[Flags.Length];
- Array.Copy(Flags, clonedFlags, Flags.Length);
-
- var clonedXCoordinates = new short[XCoordinates.Length];
- Array.Copy(XCoordinates, clonedXCoordinates, XCoordinates.Length);
-
- var clonedYCoordinates = new short[YCoordinates.Length];
- Array.Copy(YCoordinates, clonedYCoordinates, YCoordinates.Length);
-
- return new SimpleGlyphDescription(InstructionLength, clonedEndPoints, clonedFlags, clonedXCoordinates, clonedYCoordinates, GlyphBounds);
- }
- }
-
- internal class CompositeGlyphDescription : IGlyphDescription
- {
- public bool IsSimple { get; } = false;
-
- public SimpleGlyphDescription SimpleGlyph { get; } = null;
-
- public CompositeGlyphDescription CompositeGlyph => this;
-
- public PdfRectangle GlyphBounds { get; }
- public IGlyphDescription DeepClone()
- {
- return new CompositeGlyphDescription();
- }
- }
-
- internal class EmptyGlyph : IGlyphDescription
- {
- public bool IsSimple { get; } = true;
-
- public SimpleGlyphDescription SimpleGlyph => new SimpleGlyphDescription(0, new int[0], new SimpleGlyphFlags[0], new short[0], new short[0], GlyphBounds);
-
- public CompositeGlyphDescription CompositeGlyph { get; } = null;
-
- public PdfRectangle GlyphBounds { get; }
-
- public EmptyGlyph(PdfRectangle glyphBounds)
- {
- GlyphBounds = glyphBounds;
- }
-
- public IGlyphDescription DeepClone()
- {
- return new EmptyGlyph(GlyphBounds);
- }
- }
-}
\ No newline at end of file
diff --git a/src/UglyToad.PdfPig/Geometry/PdfMatrix3By2.cs b/src/UglyToad.PdfPig/Geometry/PdfMatrix3By2.cs
new file mode 100644
index 00000000..959aff78
--- /dev/null
+++ b/src/UglyToad.PdfPig/Geometry/PdfMatrix3By2.cs
@@ -0,0 +1,31 @@
+namespace UglyToad.PdfPig.Geometry
+{
+ internal struct PdfMatrix3By2
+ {
+ private readonly decimal r0c0;
+ private readonly decimal r0c1;
+ private readonly decimal r1c0;
+ private readonly decimal r1c1;
+ private readonly decimal r2c0;
+ private readonly decimal r2c1;
+
+ public PdfMatrix3By2(decimal r0C0, decimal r0C1, decimal r1C0, decimal r1C1, decimal r2C0, decimal r2C1)
+ {
+ r0c0 = r0C0;
+ r0c1 = r0C1;
+ r1c0 = r1C0;
+ r1c1 = r1C1;
+ r2c0 = r2C0;
+ r2c1 = r2C1;
+ }
+
+ public static PdfMatrix3By2 Identity { get; } = new PdfMatrix3By2(1, 0, 0, 1, 0, 0);
+ public static PdfMatrix3By2 CreateTranslation(PdfVector vector) => new PdfMatrix3By2(1, 0, 0, 1, vector.X, vector.Y);
+ public static PdfMatrix3By2 CreateTranslation(decimal x, decimal y) => new PdfMatrix3By2(1, 0, 0, 1, x, y);
+
+ public PdfMatrix3By2 WithTranslation(decimal x, decimal y)
+ {
+ return new PdfMatrix3By2(r0c0, r0c1, r1c0, r1c1, x, y);
+ }
+ }
+}