mirror of
https://github.com/UglyToad/PdfPig.git
synced 2025-04-05 20:55:01 +08:00
create javascript to verify the output of the true type font parser. fix bugs
This commit is contained in:
parent
33c10a3ff7
commit
0ca417e21c
513
documentation/true-type-reader.html
Normal file
513
documentation/true-type-reader.html
Normal file
@ -0,0 +1,513 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<!-- Code taken from http://stevehanov.ca/blog/index.php?id=143 -->
|
||||
<script src="https://code.jquery.com/jquery-3.2.1.min.js" integrity="sha256-hwg4gsxgFZhOsEEamdOYGBf13FyQuiTwlAQgxVSNgt4="
|
||||
crossorigin="anonymous"></script>
|
||||
<style>
|
||||
#dropTarget {
|
||||
width: 100%;
|
||||
min-width: 500px;
|
||||
height: 1000px;
|
||||
background: lavender;
|
||||
border: 1px solid lightgray;
|
||||
}
|
||||
|
||||
#details-container {
|
||||
background: #333;
|
||||
color: #ededed;
|
||||
font-family: 'Franklin Gothic Medium', 'Arial Narrow', Arial, sans-serif;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
#details-container span {
|
||||
margin: 5px;
|
||||
display: block;
|
||||
padding: 5px;
|
||||
width: 160px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body id="dropTarget">
|
||||
|
||||
<div id="details-container"></div>
|
||||
<div id="font-container"></div>
|
||||
<script>
|
||||
var dropTarget = document.getElementById("dropTarget");
|
||||
dropTarget.ondragover = function (e) {
|
||||
e.preventDefault();
|
||||
};
|
||||
dropTarget.ondrop = function (e) {
|
||||
e.preventDefault();
|
||||
|
||||
if (!e.dataTransfer || !e.dataTransfer.files) {
|
||||
alert("Your browser didn't include any files in the drop event");
|
||||
return;
|
||||
}
|
||||
|
||||
var reader = new FileReader();
|
||||
reader.readAsArrayBuffer(e.dataTransfer.files[0]);
|
||||
reader.onload = function (e) {
|
||||
ShowTtfFile(reader.result);
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
function assert(condition) {
|
||||
if (!condition) alert("False assert");
|
||||
}
|
||||
|
||||
function BinaryReader(arrayBuffer) {
|
||||
assert(arrayBuffer instanceof ArrayBuffer);
|
||||
this.pos = 0;
|
||||
this.data = new Uint8Array(arrayBuffer);
|
||||
}
|
||||
|
||||
BinaryReader.prototype = {
|
||||
seek: function (pos) {
|
||||
assert(pos >= 0 && pos <= this.data.length);
|
||||
var oldPos = this.pos;
|
||||
this.pos = pos;
|
||||
return oldPos;
|
||||
},
|
||||
|
||||
tell: function () {
|
||||
return this.pos;
|
||||
},
|
||||
|
||||
getUint8: function () {
|
||||
assert(this.pos < this.data.length);
|
||||
return this.data[this.pos++];
|
||||
},
|
||||
|
||||
getUint16: function () {
|
||||
return ((this.getUint8() << 8) | this.getUint8()) >>> 0;
|
||||
},
|
||||
|
||||
getUint32: function () {
|
||||
return this.getInt32() >>> 0;
|
||||
},
|
||||
|
||||
getInt16: function () {
|
||||
var result = this.getUint16();
|
||||
if (result & 0x8000) {
|
||||
result -= (1 << 16);
|
||||
}
|
||||
return result;
|
||||
},
|
||||
|
||||
getInt32: function () {
|
||||
return ((this.getUint8() << 24) |
|
||||
(this.getUint8() << 16) |
|
||||
(this.getUint8() << 8) |
|
||||
(this.getUint8()));
|
||||
},
|
||||
|
||||
getFword: function () {
|
||||
return this.getInt16();
|
||||
},
|
||||
|
||||
get2Dot14: function () {
|
||||
return this.getInt16() / (1 << 14);
|
||||
},
|
||||
|
||||
getFixed: function () {
|
||||
return this.getInt32() / (1 << 16);
|
||||
},
|
||||
|
||||
getString: function (length) {
|
||||
var result = "";
|
||||
for (var i = 0; i < length; i++) {
|
||||
result += String.fromCharCode(this.getUint8());
|
||||
}
|
||||
return result;
|
||||
},
|
||||
|
||||
getDate: function () {
|
||||
var macTime = this.getUint32() * 0x100000000 + this.getUint32();
|
||||
var utcTime = macTime * 1000 + Date.UTC(1904, 1, 1);
|
||||
return new Date(utcTime);
|
||||
}
|
||||
};
|
||||
|
||||
function TrueTypeFont(arrayBuffer) {
|
||||
this.file = new BinaryReader(arrayBuffer);
|
||||
this.tables = this.readOffsetTables(this.file);
|
||||
this.readHeadTable(this.file);
|
||||
this.length = this.glyphCount();
|
||||
}
|
||||
|
||||
TrueTypeFont.prototype = {
|
||||
readOffsetTables: function (file) {
|
||||
var tables = {};
|
||||
this.scalarType = file.getUint32();
|
||||
var numTables = file.getUint16();
|
||||
this.searchRange = file.getUint16();
|
||||
this.entrySelector = file.getUint16();
|
||||
this.rangeShift = file.getUint16();
|
||||
|
||||
for (var i = 0; i < numTables; i++) {
|
||||
var tag = file.getString(4);
|
||||
tables[tag] = {
|
||||
checksum: file.getUint32(),
|
||||
offset: file.getUint32(),
|
||||
length: file.getUint32()
|
||||
};
|
||||
|
||||
if (tag !== 'head') {
|
||||
assert(this.calculateTableChecksum(file, tables[tag].offset,
|
||||
tables[tag].length) === tables[tag].checksum);
|
||||
}
|
||||
}
|
||||
|
||||
return tables;
|
||||
},
|
||||
|
||||
calculateTableChecksum: function (file, offset, length) {
|
||||
var old = file.seek(offset);
|
||||
var sum = 0;
|
||||
var nlongs = ((length + 3) / 4) | 0;
|
||||
while (nlongs--) {
|
||||
sum = (sum + file.getUint32() & 0xffffffff) >>> 0;
|
||||
}
|
||||
|
||||
file.seek(old);
|
||||
return sum;
|
||||
},
|
||||
|
||||
|
||||
|
||||
glyphCount: function () {
|
||||
assert("maxp" in this.tables);
|
||||
var old = this.file.seek(this.tables["maxp"].offset + 4);
|
||||
var count = this.file.getUint16();
|
||||
this.file.seek(old);
|
||||
return count;
|
||||
},
|
||||
|
||||
readHeadTable: function (file) {
|
||||
assert("head" in this.tables);
|
||||
file.seek(this.tables["head"].offset);
|
||||
|
||||
this.version = file.getFixed();
|
||||
this.fontRevision = file.getFixed();
|
||||
this.checksumAdjustment = file.getUint32();
|
||||
this.magicNumber = file.getUint32();
|
||||
assert(this.magicNumber === 0x5f0f3cf5);
|
||||
this.flags = file.getUint16();
|
||||
this.unitsPerEm = file.getUint16();
|
||||
this.created = file.getDate();
|
||||
this.modified = file.getDate();
|
||||
this.xMin = file.getFword();
|
||||
this.yMin = file.getFword();
|
||||
this.xMax = file.getFword();
|
||||
this.yMax = file.getFword();
|
||||
this.macStyle = file.getUint16();
|
||||
this.lowestRecPPEM = file.getUint16();
|
||||
this.fontDirectionHint = file.getInt16();
|
||||
this.indexToLocFormat = file.getInt16();
|
||||
this.glyphDataFormat = file.getInt16();
|
||||
},
|
||||
|
||||
getGlyphOffset: function (index) {
|
||||
assert("loca" in this.tables);
|
||||
var table = this.tables["loca"];
|
||||
var file = this.file;
|
||||
var offset, old;
|
||||
|
||||
if (this.indexToLocFormat === 1) {
|
||||
old = file.seek(table.offset + index * 4);
|
||||
offset = file.getUint32();
|
||||
} else {
|
||||
old = file.seek(table.offset + index * 2);
|
||||
offset = file.getUint16() * 2;
|
||||
}
|
||||
|
||||
file.seek(old);
|
||||
|
||||
return offset + this.tables["glyf"].offset;
|
||||
},
|
||||
|
||||
readGlyph: function (index) {
|
||||
var offset = this.getGlyphOffset(index);
|
||||
var file = this.file;
|
||||
|
||||
if (offset >= this.tables["glyf"].offset + this.tables["glyf"].length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
assert(offset >= this.tables["glyf"].offset);
|
||||
assert(offset < this.tables["glyf"].offset + this.tables["glyf"].length);
|
||||
|
||||
file.seek(offset);
|
||||
|
||||
var glyph = {
|
||||
numberOfContours: file.getInt16(),
|
||||
xMin: file.getFword(),
|
||||
yMin: file.getFword(),
|
||||
xMax: file.getFword(),
|
||||
yMax: file.getFword()
|
||||
};
|
||||
|
||||
assert(glyph.numberOfContours >= -1);
|
||||
|
||||
if (glyph.numberOfContours === -1) {
|
||||
this.readCompoundGlyph(file, glyph);
|
||||
} else {
|
||||
this.readSimpleGlyph(file, glyph);
|
||||
}
|
||||
|
||||
return glyph;
|
||||
},
|
||||
|
||||
readSimpleGlyph: function (file, glyph) {
|
||||
|
||||
var ON_CURVE = 1,
|
||||
X_IS_BYTE = 2,
|
||||
Y_IS_BYTE = 4,
|
||||
REPEAT = 8,
|
||||
X_DELTA = 16,
|
||||
Y_DELTA = 32;
|
||||
|
||||
glyph.type = "simple";
|
||||
glyph.contourEnds = [];
|
||||
var points = glyph.points = [];
|
||||
|
||||
for (var i = 0; i < glyph.numberOfContours; i++) {
|
||||
glyph.contourEnds.push(file.getUint16());
|
||||
}
|
||||
|
||||
// skip over intructions
|
||||
file.seek(file.getUint16() + file.tell());
|
||||
|
||||
if (glyph.numberOfContours === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
var numPoints = Math.max.apply(null, glyph.contourEnds) + 1;
|
||||
|
||||
var flags = [];
|
||||
|
||||
for (i = 0; i < numPoints; i++) {
|
||||
var flag = file.getUint8();
|
||||
flags.push(flag);
|
||||
points.push({
|
||||
onCurve: (flag & ON_CURVE) > 0
|
||||
});
|
||||
|
||||
if (flag & REPEAT) {
|
||||
var repeatCount = file.getUint8();
|
||||
assert(repeatCount > 0);
|
||||
i += repeatCount;
|
||||
while (repeatCount--) {
|
||||
flags.push(flag);
|
||||
points.push({
|
||||
onCurve: (flag & ON_CURVE) > 0
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function readCoords(name, byteFlag, deltaFlag, min, max) {
|
||||
var value = 0;
|
||||
|
||||
for (var i = 0; i < numPoints; i++) {
|
||||
var flag = flags[i];
|
||||
if (flag & byteFlag) {
|
||||
if (flag & deltaFlag) {
|
||||
value += file.getUint8();
|
||||
} else {
|
||||
value -= file.getUint8();
|
||||
}
|
||||
} else if (~flag & deltaFlag) {
|
||||
value += file.getInt16();
|
||||
} else {
|
||||
// value is unchanged.
|
||||
}
|
||||
|
||||
points[i][name] = value;
|
||||
}
|
||||
}
|
||||
|
||||
readCoords("x", X_IS_BYTE, X_DELTA, glyph.xMin, glyph.xMax);
|
||||
readCoords("y", Y_IS_BYTE, Y_DELTA, glyph.yMin, glyph.yMax);
|
||||
},
|
||||
|
||||
readCompoundGlyph: function (file, glyph) {
|
||||
var ARG_1_AND_2_ARE_WORDS = 1,
|
||||
ARGS_ARE_XY_VALUES = 2,
|
||||
ROUND_XY_TO_GRID = 4,
|
||||
WE_HAVE_A_SCALE = 8,
|
||||
// RESERVED = 16
|
||||
MORE_COMPONENTS = 32,
|
||||
WE_HAVE_AN_X_AND_Y_SCALE = 64,
|
||||
WE_HAVE_A_TWO_BY_TWO = 128,
|
||||
WE_HAVE_INSTRUCTIONS = 256,
|
||||
USE_MY_METRICS = 512,
|
||||
OVERLAP_COMPONENT = 1024;
|
||||
|
||||
glyph.type = "compound";
|
||||
glyph.components = [];
|
||||
|
||||
var flags = MORE_COMPONENTS;
|
||||
while (flags & MORE_COMPONENTS) {
|
||||
var arg1, arg2;
|
||||
|
||||
flags = file.getUint16();
|
||||
var component = {
|
||||
glyphIndex: file.getUint16(),
|
||||
matrix: {
|
||||
a: 1, b: 0, c: 0, d: 1, e: 0, f: 0
|
||||
}
|
||||
};
|
||||
|
||||
if (flags & ARG_1_AND_2_ARE_WORDS) {
|
||||
arg1 = file.getInt16();
|
||||
arg2 = file.getInt16();
|
||||
} else {
|
||||
arg1 = file.getUint8();
|
||||
arg2 = file.getUint8();
|
||||
}
|
||||
|
||||
if (flags & ARGS_ARE_XY_VALUES) {
|
||||
component.matrix.e = arg1;
|
||||
component.matrix.f = arg2;
|
||||
} else {
|
||||
component.destPointIndex = arg1;
|
||||
component.srcPointIndex = arg2;
|
||||
}
|
||||
|
||||
if (flags & WE_HAVE_A_SCALE) {
|
||||
component.matrix.a = file.get2Dot14();
|
||||
component.matrix.d = component.matrix.a;
|
||||
} else if (flags & WE_HAVE_AN_X_AND_Y_SCALE) {
|
||||
component.matrix.a = file.get2Dot14();
|
||||
component.matrix.d = file.get2Dot14();
|
||||
} else if (flags & WE_HAVE_A_TWO_BY_TWO) {
|
||||
component.matrix.a = file.get2Dot14();
|
||||
component.matrix.b = file.get2Dot14();
|
||||
component.matrix.c = file.get2Dot14();
|
||||
component.matrix.d = file.get2Dot14();
|
||||
}
|
||||
|
||||
glyph.components.push(component);
|
||||
}
|
||||
|
||||
if (flags & WE_HAVE_INSTRUCTIONS) {
|
||||
file.seek(file.getUint16() + file.tell());
|
||||
}
|
||||
},
|
||||
|
||||
drawGlyph: function (index, ctx) {
|
||||
|
||||
var glyph = this.readGlyph(index);
|
||||
|
||||
if (glyph === null || glyph.type !== "simple") {
|
||||
return false;
|
||||
}
|
||||
|
||||
var p = 0,
|
||||
c = 0,
|
||||
first = 1;
|
||||
|
||||
while (p < glyph.points.length) {
|
||||
var point = glyph.points[p];
|
||||
if (first === 1) {
|
||||
ctx.moveTo(point.x, point.y);
|
||||
first = 0;
|
||||
} else {
|
||||
ctx.lineTo(point.x, point.y);
|
||||
}
|
||||
|
||||
if (p === glyph.contourEnds[c]) {
|
||||
c += 1;
|
||||
first = 1;
|
||||
}
|
||||
|
||||
p += 1;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
function CreateInformation(s, v, container) {
|
||||
var span = document.createElement("span");
|
||||
span.innerHTML = s + ": " + v;
|
||||
container.appendChild(span);
|
||||
}
|
||||
|
||||
function ShowTtfFile(arrayBuffer) {
|
||||
var font = new TrueTypeFont(arrayBuffer);
|
||||
|
||||
var details = document.getElementById("details-container");
|
||||
|
||||
while (details.firstChild) details.removeChild(details.firstChild);
|
||||
|
||||
var content = "<table class='table table-striped'><thead><tr><td>Tag</td><td>Checksum</td><td>Offset</td><td>Length</td></tr></thead><tbody>";
|
||||
for (var property in font.tables) {
|
||||
if (font.tables.hasOwnProperty(property)) {
|
||||
var tab = font.tables[property];
|
||||
content += "<tr>";
|
||||
content += "<td>" + property + "</td>";
|
||||
content += "<td>" + tab.checksum + "</td>";
|
||||
content += "<td>" + tab.offset + "</td>";
|
||||
content += "<td>" + tab.length + "</td>";
|
||||
content += "</tr>";
|
||||
}
|
||||
}
|
||||
|
||||
content += "</tbody></table>";
|
||||
$("#details-container").append(content);
|
||||
|
||||
CreateInformation("Version", font.version, details);
|
||||
CreateInformation("Revision", font.fontRevision, details);
|
||||
CreateInformation("Checksum adjustment", font.checksumAdjustment, details);
|
||||
CreateInformation("Magic Number", font.magicNumber, details);
|
||||
CreateInformation("Flags", font.flags, details);
|
||||
CreateInformation("Units Per Em", font.unitsPerEm, details);
|
||||
CreateInformation("Created", font.created, details);
|
||||
CreateInformation("Modified", font.modified, details);
|
||||
CreateInformation("Min X", font.xMin, details);
|
||||
CreateInformation("Min Y", font.yMin, details);
|
||||
CreateInformation("Max X", font.xMax, details);
|
||||
CreateInformation("Max Y", font.yMax, details);
|
||||
CreateInformation("Mac Style", font.macStyle, details);
|
||||
CreateInformation("Lowest recommended PPEM", font.lowestRecPPEM, details);
|
||||
CreateInformation("Font Direction", font.fontDirectionHint, details);
|
||||
CreateInformation("Index to loc format", font.indexToLocFormat, details);
|
||||
CreateInformation("Glyph data format", font.glyphDataFormat, details);
|
||||
|
||||
var width = font.xMax - font.xMin;
|
||||
var height = font.yMax - font.yMin;
|
||||
var scale = 64 / font.unitsPerEm;
|
||||
|
||||
var container = document.getElementById("font-container");
|
||||
|
||||
while (container.firstChild) {
|
||||
container.removeChild(container.firstChild);
|
||||
}
|
||||
|
||||
for (var i = 0; i < font.length; i++) {
|
||||
var canvas = document.createElement("canvas");
|
||||
canvas.style.border = "1px solid gray";
|
||||
canvas.width = width * scale;
|
||||
canvas.height = height * scale;
|
||||
var ctx = canvas.getContext("2d");
|
||||
ctx.scale(scale, -scale);
|
||||
ctx.translate(-font.xMin, -font.yMin - height);
|
||||
ctx.fillStyle = "#000000";
|
||||
ctx.beginPath();
|
||||
if (font.drawGlyph(i, ctx)) {
|
||||
ctx.fill();
|
||||
container.appendChild(canvas);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
@ -6,6 +6,7 @@
|
||||
using IO;
|
||||
using Pdf.Fonts.TrueType;
|
||||
using Pdf.Fonts.TrueType.Parser;
|
||||
using Pdf.Fonts.TrueType.Tables;
|
||||
using Xunit;
|
||||
|
||||
public class TrueTypeFontParserTests
|
||||
@ -34,7 +35,47 @@
|
||||
|
||||
var input = new TrueTypeDataBytes(new ByteArrayInputBytes(bytes));
|
||||
|
||||
parser.Parse(input);
|
||||
var font = parser.Parse(input);
|
||||
|
||||
Assert.Equal(1, font.Version);
|
||||
|
||||
Assert.Equal(1, font.HeaderTable.Version);
|
||||
Assert.Equal(1, font.HeaderTable.Revision);
|
||||
|
||||
Assert.Equal(1142661421, font.HeaderTable.CheckSumAdjustment);
|
||||
Assert.Equal(1594834165, font.HeaderTable.MagicNumber);
|
||||
|
||||
Assert.Equal(9, font.HeaderTable.Flags);
|
||||
|
||||
Assert.Equal(2048, font.HeaderTable.UnitsPerEm);
|
||||
|
||||
Assert.Equal(2008, font.HeaderTable.Created.Year);
|
||||
Assert.Equal(10, font.HeaderTable.Created.Month);
|
||||
Assert.Equal(13, font.HeaderTable.Created.Day);
|
||||
Assert.Equal(12, font.HeaderTable.Created.Hour);
|
||||
Assert.Equal(29, font.HeaderTable.Created.Minute);
|
||||
Assert.Equal(34, font.HeaderTable.Created.Second);
|
||||
|
||||
Assert.Equal(2011, font.HeaderTable.Modified.Year);
|
||||
Assert.Equal(12, font.HeaderTable.Modified.Month);
|
||||
Assert.Equal(31, font.HeaderTable.Modified.Day);
|
||||
Assert.Equal(5, font.HeaderTable.Modified.Hour);
|
||||
Assert.Equal(13, font.HeaderTable.Modified.Minute);
|
||||
Assert.Equal(10, font.HeaderTable.Modified.Second);
|
||||
|
||||
Assert.Equal(-980, font.HeaderTable.XMin);
|
||||
Assert.Equal(-555, font.HeaderTable.YMin);
|
||||
|
||||
Assert.Equal(2396, font.HeaderTable.XMax);
|
||||
Assert.Equal(2163, font.HeaderTable.YMax);
|
||||
|
||||
Assert.Equal(0, font.HeaderTable.MacStyle);
|
||||
Assert.Equal(9, font.HeaderTable.LowestRecommendedPpem);
|
||||
|
||||
Assert.Equal(HeaderTable.FontDirection.StronglyLeftToRightWithNeutrals, font.HeaderTable.FontDirectionHint);
|
||||
|
||||
Assert.Equal(0, font.HeaderTable.IndexToLocFormat);
|
||||
Assert.Equal(0, font.HeaderTable.GlyphDataFormat);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -2,12 +2,13 @@
|
||||
{
|
||||
using Tables;
|
||||
|
||||
internal class HeaderTableParser : ITrueTypeTableParser
|
||||
internal class HeaderTableParser
|
||||
{
|
||||
public string Tag => TrueTypeFontTable.Head;
|
||||
|
||||
public ITable Parse(TrueTypeDataBytes data, TrueTypeFontTable table)
|
||||
public HeaderTable Parse(TrueTypeDataBytes data, TrueTypeFontTable table)
|
||||
{
|
||||
data.Seek(table.Offset - 1);
|
||||
var version = data.Read32Fixed();
|
||||
var fontRevision = data.Read32Fixed();
|
||||
var checkSumAdjustment = data.ReadUnsignedInt();
|
||||
|
17
src/UglyToad.Pdf/Fonts/TrueType/Parser/TrueTypeFont.cs
Normal file
17
src/UglyToad.Pdf/Fonts/TrueType/Parser/TrueTypeFont.cs
Normal file
@ -0,0 +1,17 @@
|
||||
namespace UglyToad.Pdf.Fonts.TrueType.Parser
|
||||
{
|
||||
using Tables;
|
||||
|
||||
internal class TrueTypeFont
|
||||
{
|
||||
public decimal Version { get; }
|
||||
|
||||
public HeaderTable HeaderTable { get; }
|
||||
|
||||
public TrueTypeFont(decimal version, HeaderTable headerTable)
|
||||
{
|
||||
Version = version;
|
||||
HeaderTable = headerTable;
|
||||
}
|
||||
}
|
||||
}
|
@ -8,15 +8,11 @@
|
||||
{
|
||||
private const int TagLength = 4;
|
||||
|
||||
private static readonly IReadOnlyDictionary<string, ITrueTypeTableParser> parsers =
|
||||
new Dictionary<string, ITrueTypeTableParser>
|
||||
{
|
||||
{TrueTypeFontTable.Head, new HeaderTableParser()}
|
||||
};
|
||||
|
||||
public void Parse(TrueTypeDataBytes data)
|
||||
private static readonly HeaderTableParser HeaderTableParser = new HeaderTableParser();
|
||||
|
||||
public TrueTypeFont Parse(TrueTypeDataBytes data)
|
||||
{
|
||||
var version = data.Read32Fixed();
|
||||
var version = (decimal)data.Read32Fixed();
|
||||
int numberOfTables = data.ReadUnsignedShort();
|
||||
int searchRange = data.ReadUnsignedShort();
|
||||
int entrySelector = data.ReadUnsignedShort();
|
||||
@ -34,9 +30,9 @@
|
||||
}
|
||||
}
|
||||
|
||||
ParseTables(tables, data);
|
||||
var result = ParseTables(version, tables, data);
|
||||
|
||||
return;
|
||||
return result;
|
||||
}
|
||||
|
||||
[CanBeNull]
|
||||
@ -56,7 +52,7 @@
|
||||
return new TrueTypeFontTable(tag, checksum, offset, length);
|
||||
}
|
||||
|
||||
private static void ParseTables(IReadOnlyDictionary<string, TrueTypeFontTable> tables, TrueTypeDataBytes data)
|
||||
private static TrueTypeFont ParseTables(decimal version, IReadOnlyDictionary<string, TrueTypeFontTable> tables, TrueTypeDataBytes data)
|
||||
{
|
||||
var isPostScript = tables.ContainsKey(TrueTypeFontTable.Cff);
|
||||
|
||||
@ -65,7 +61,9 @@
|
||||
throw new InvalidOperationException($"The {TrueTypeFontTable.Head} table is required.");
|
||||
}
|
||||
|
||||
var header = parsers[TrueTypeFontTable.Head].Parse(data, table);
|
||||
var header = HeaderTableParser.Parse(data, table);
|
||||
|
||||
return new TrueTypeFont(version, header);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -135,8 +135,15 @@
|
||||
var date = new DateTime(1904, 1, 1, 0, 0, 0, 0, new GregorianCalendar());
|
||||
|
||||
var result = date.AddSeconds(secondsSince1904);
|
||||
result = result.AddMonths(1);
|
||||
result = result.AddDays(1);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public void Seek(long position)
|
||||
{
|
||||
inputBytes.Seek(position);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -42,5 +42,11 @@
|
||||
{
|
||||
return CurrentOffset == bytes.Count - 1;
|
||||
}
|
||||
|
||||
public void Seek(long position)
|
||||
{
|
||||
CurrentOffset = (int)position;
|
||||
CurrentByte = bytes[CurrentOffset];
|
||||
}
|
||||
}
|
||||
}
|
@ -11,5 +11,7 @@
|
||||
byte? Peek();
|
||||
|
||||
bool IsAtEnd();
|
||||
|
||||
void Seek(long position);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user