mirror of
https://github.com/UglyToad/PdfPig.git
synced 2025-10-15 19:54:52 +08:00
tidy up glyph reading code, simple glyphs now contain glyph points rather than 3 related arrays
This commit is contained in:
@@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -1,4 +1,4 @@
|
|||||||
namespace UglyToad.PdfPig.Fonts.TrueType.Tables
|
namespace UglyToad.PdfPig.Fonts.TrueType.Glyphs
|
||||||
{
|
{
|
||||||
using System;
|
using System;
|
||||||
|
|
25
src/UglyToad.PdfPig/Fonts/TrueType/Glyphs/EmptyGlyph.cs
Normal file
25
src/UglyToad.PdfPig/Fonts/TrueType/Glyphs/EmptyGlyph.cs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
23
src/UglyToad.PdfPig/Fonts/TrueType/Glyphs/GlyphPoint.cs
Normal file
23
src/UglyToad.PdfPig/Fonts/TrueType/Glyphs/GlyphPoint.cs
Normal 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}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -1,6 +1,5 @@
|
|||||||
namespace UglyToad.PdfPig.Fonts.TrueType.Tables
|
namespace UglyToad.PdfPig.Fonts.TrueType.Glyphs
|
||||||
{
|
{
|
||||||
using System;
|
|
||||||
using Geometry;
|
using Geometry;
|
||||||
|
|
||||||
internal interface IGlyphDescription
|
internal interface IGlyphDescription
|
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -1,4 +1,4 @@
|
|||||||
namespace UglyToad.PdfPig.Fonts.TrueType.Tables
|
namespace UglyToad.PdfPig.Fonts.TrueType.Glyphs
|
||||||
{
|
{
|
||||||
using System;
|
using System;
|
||||||
|
|
@@ -3,6 +3,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Geometry;
|
using Geometry;
|
||||||
|
using Glyphs;
|
||||||
using Parser;
|
using Parser;
|
||||||
using Util.JetBrains.Annotations;
|
using Util.JetBrains.Annotations;
|
||||||
|
|
||||||
@@ -88,7 +89,7 @@
|
|||||||
|
|
||||||
var instructionLength = data.ReadUnsignedShort();
|
var instructionLength = data.ReadUnsignedShort();
|
||||||
|
|
||||||
data.ReadByteArray(instructionLength);
|
var instructions = data.ReadByteArray(instructionLength);
|
||||||
|
|
||||||
var pointCount = 0;
|
var pointCount = 0;
|
||||||
if (contourCount > 0)
|
if (contourCount > 0)
|
||||||
@@ -104,7 +105,14 @@
|
|||||||
var yCoordinates = ReadCoordinates(data, pointCount, flags, SimpleGlyphFlags.YShortVector,
|
var yCoordinates = ReadCoordinates(data, pointCount, flags, SimpleGlyphFlags.YShortVector,
|
||||||
SimpleGlyphFlags.YSignOrSame);
|
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)
|
private static CompositeGlyphDescription ReadCompositeGlyph(TrueTypeDataBytes data, TemporaryCompositeLocation compositeLocation, Dictionary<int, TemporaryCompositeLocation> compositeLocations, IGlyphDescription[] glyphs)
|
||||||
@@ -116,6 +124,8 @@
|
|||||||
|
|
||||||
data.Seek(compositeLocation.Position);
|
data.Seek(compositeLocation.Position);
|
||||||
|
|
||||||
|
var components = new List<CompositeComponent>();
|
||||||
|
|
||||||
CompositeGlyphFlags flags;
|
CompositeGlyphFlags flags;
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
@@ -150,42 +160,32 @@
|
|||||||
arg2 = data.ReadByte();
|
arg2 = data.ReadByte();
|
||||||
}
|
}
|
||||||
|
|
||||||
float xscale = 1;
|
decimal xscale = 1;
|
||||||
float scale01 = 0;
|
decimal scale01 = 0;
|
||||||
float scale10 = 0;
|
decimal scale10 = 0;
|
||||||
float yscale = 1;
|
decimal yscale = 1;
|
||||||
|
|
||||||
bool hasScale, hasMatrix = false;
|
|
||||||
if (HasFlag(flags, CompositeGlyphFlags.WeHaveAScale))
|
if (HasFlag(flags, CompositeGlyphFlags.WeHaveAScale))
|
||||||
{
|
{
|
||||||
xscale = ReadTwoFourteenFormat(data);
|
xscale = ReadTwoFourteenFormat(data);
|
||||||
yscale = xscale;
|
yscale = xscale;
|
||||||
hasScale = true;
|
|
||||||
}
|
}
|
||||||
else if (HasFlag(flags, CompositeGlyphFlags.WeHaveAnXAndYScale))
|
else if (HasFlag(flags, CompositeGlyphFlags.WeHaveAnXAndYScale))
|
||||||
{
|
{
|
||||||
xscale = ReadTwoFourteenFormat(data);
|
xscale = ReadTwoFourteenFormat(data);
|
||||||
yscale = ReadTwoFourteenFormat(data);
|
yscale = ReadTwoFourteenFormat(data);
|
||||||
hasScale = true;
|
|
||||||
}
|
}
|
||||||
else if (HasFlag(flags, CompositeGlyphFlags.WeHaveATwoByTwo))
|
else if (HasFlag(flags, CompositeGlyphFlags.WeHaveATwoByTwo))
|
||||||
{
|
{
|
||||||
/*
|
|
||||||
* We build the 2 by 2 matrix:
|
|
||||||
* x 0
|
|
||||||
* 0 y
|
|
||||||
*/
|
|
||||||
xscale = ReadTwoFourteenFormat(data);
|
xscale = ReadTwoFourteenFormat(data);
|
||||||
scale01 = ReadTwoFourteenFormat(data);
|
scale01 = ReadTwoFourteenFormat(data);
|
||||||
scale10 = ReadTwoFourteenFormat(data);
|
scale10 = ReadTwoFourteenFormat(data);
|
||||||
yscale = ReadTwoFourteenFormat(data);
|
yscale = ReadTwoFourteenFormat(data);
|
||||||
hasScale = true;
|
|
||||||
hasMatrix = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (HasFlag(flags, CompositeGlyphFlags.ArgsAreXAndYValues))
|
if (HasFlag(flags, CompositeGlyphFlags.ArgsAreXAndYValues))
|
||||||
{
|
{
|
||||||
|
components.Add(new CompositeComponent(glyphIndex, new PdfMatrix3By2(xscale, scale01, scale10, yscale, arg1, arg2)));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -197,9 +197,9 @@
|
|||||||
return new CompositeGlyphDescription();
|
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;
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
31
src/UglyToad.PdfPig/Geometry/PdfMatrix3By2.cs
Normal file
31
src/UglyToad.PdfPig/Geometry/PdfMatrix3By2.cs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user