tidy up glyph reading code, simple glyphs now contain glyph points rather than 3 related arrays

This commit is contained in:
Eliot Jones
2018-04-14 15:44:07 +01:00
parent ebdda46098
commit 983933b6e8
10 changed files with 188 additions and 133 deletions

View File

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

View File

@@ -1,4 +1,4 @@
namespace UglyToad.PdfPig.Fonts.TrueType.Tables
namespace UglyToad.PdfPig.Fonts.TrueType.Glyphs
{
using System;

View File

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

View File

@@ -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}";
}
}
}

View File

@@ -1,6 +1,5 @@
namespace UglyToad.PdfPig.Fonts.TrueType.Tables
namespace UglyToad.PdfPig.Fonts.TrueType.Glyphs
{
using System;
using Geometry;
internal interface IGlyphDescription

View File

@@ -0,0 +1,54 @@
namespace UglyToad.PdfPig.Fonts.TrueType.Glyphs
{
using System;
using Geometry;
internal class SimpleGlyphDescription : IGlyphDescription
{
/// <summary>
/// The bounding rectangle for the character.
/// </summary>
public PdfRectangle GlyphBounds { get; }
/// <summary>
/// The total number of bytes for instructions.
/// </summary>
public byte[] Instructions { get; }
/// <summary>
/// An array of the last points of each contour.
/// </summary>
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);
}
}
}

View File

@@ -1,4 +1,4 @@
namespace UglyToad.PdfPig.Fonts.TrueType.Tables
namespace UglyToad.PdfPig.Fonts.TrueType.Glyphs
{
using System;

View File

@@ -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<int, TemporaryCompositeLocation> compositeLocations, IGlyphDescription[] glyphs)
@@ -116,6 +124,8 @@
data.Seek(compositeLocation.Position);
var components = new List<CompositeComponent>();
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;
}
}
}
}

View File

@@ -1,110 +0,0 @@
namespace UglyToad.PdfPig.Fonts.TrueType.Tables
{
using System;
using Geometry;
internal class SimpleGlyphDescription : IGlyphDescription
{
/// <summary>
/// The bounding rectangle for the character.
/// </summary>
public PdfRectangle GlyphBounds { get; }
/// <summary>
/// The total number of bytes for instructions.
/// </summary>
public int InstructionLength { get; }
/// <summary>
/// An array of the last points of each contour.
/// </summary>
public int[] EndPointsOfContours { get; }
/// <summary>
/// Array of flags for each coordinate in the outline.
/// </summary>
public SimpleGlyphFlags[] Flags { get; }
/// <summary>
/// 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.
/// </summary>
public short[] XCoordinates { get; }
/// <summary>
/// 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.
/// </summary>
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);
}
}
}

View File

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