mirror of
https://github.com/UglyToad/PdfPig.git
synced 2025-10-15 03:34: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;
|
||||
|
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;
|
||||
|
||||
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;
|
||||
|
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -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