mirror of
https://github.com/UglyToad/PdfPig.git
synced 2025-10-15 19:54:52 +08:00
#20 add name table to truetype parsing so we can check the name of parsed system fonts
This commit is contained in:
@@ -152,6 +152,10 @@
|
||||
var font = parser.Parse(input);
|
||||
|
||||
Assert.NotNull(font.TableRegister.GlyphTable);
|
||||
|
||||
var name = font.Name;
|
||||
|
||||
Assert.Equal("Andada Regular", name);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@@ -0,0 +1,51 @@
|
||||
namespace UglyToad.PdfPig.Fonts.TrueType.Names
|
||||
{
|
||||
/// <summary>
|
||||
/// The meaning of the platform specific encoding when the <see cref="TrueTypePlatformIdentifier"/> is <see cref="TrueTypePlatformIdentifier.Macintosh"/>.
|
||||
/// </summary>
|
||||
internal enum TrueTypeMacintoshEncodingIdentifier
|
||||
{
|
||||
/// <summary>
|
||||
/// Roman.
|
||||
/// </summary>
|
||||
Roman = 0,
|
||||
/// <summary>
|
||||
/// Japanese.
|
||||
/// </summary>
|
||||
Japanese = 1,
|
||||
/// <summary>
|
||||
/// Traditional Chinese.
|
||||
/// </summary>
|
||||
ChineseTraditional = 2,
|
||||
Korean = 3,
|
||||
Arabic = 5,
|
||||
Hebrew = 5,
|
||||
Greek = 6,
|
||||
Russian = 7,
|
||||
RSymbol = 8,
|
||||
Devanagari = 9,
|
||||
Gurmukhi = 10,
|
||||
Gujarati = 11,
|
||||
Oriya = 12,
|
||||
Bengali = 13,
|
||||
Tamil = 14,
|
||||
Telugu = 15,
|
||||
Kannada = 16,
|
||||
Malayalam = 17,
|
||||
Sinhalese = 18,
|
||||
Burmese = 19,
|
||||
Khmer = 20,
|
||||
Thai = 21,
|
||||
Laotian = 22,
|
||||
Georgian = 23,
|
||||
Armenian = 24,
|
||||
ChineseSimplified = 25,
|
||||
Tibetan = 26,
|
||||
Mongolian = 27,
|
||||
Geez = 28,
|
||||
Slavic = 29,
|
||||
Vietnamese = 30,
|
||||
Sindhi = 31,
|
||||
Uninterpreted = 32
|
||||
}
|
||||
}
|
@@ -0,0 +1,124 @@
|
||||
namespace UglyToad.PdfPig.Fonts.TrueType.Names
|
||||
{
|
||||
internal enum TrueTypeMacintoshLanguageIdentifier
|
||||
{
|
||||
English = 0,
|
||||
French = 1,
|
||||
German = 2,
|
||||
Italian = 3,
|
||||
Dutch = 4,
|
||||
Swedish = 5,
|
||||
Spanish = 6,
|
||||
Danish = 7,
|
||||
Portuguese = 8,
|
||||
Norwegian = 9,
|
||||
Hebrew = 10,
|
||||
Japanese = 11,
|
||||
Arabic = 12,
|
||||
Finnish = 13,
|
||||
Greek = 14,
|
||||
Icelandic = 15,
|
||||
Maltese = 16,
|
||||
Turkish = 17,
|
||||
Croatian = 18,
|
||||
ChineseTraditional = 19,
|
||||
Urdu = 20,
|
||||
Hindi = 21,
|
||||
Thai = 22,
|
||||
Korean = 23,
|
||||
Lithuanian = 24,
|
||||
Polish = 25,
|
||||
Hungarian = 26,
|
||||
Estonian = 27,
|
||||
Latvian = 28,
|
||||
Sami = 29,
|
||||
Faroese = 30,
|
||||
FarsiPersian = 31,
|
||||
Russian = 32,
|
||||
ChineseSimplified = 33,
|
||||
Flemish = 34,
|
||||
IrishGaelic = 35,
|
||||
Albanian = 36,
|
||||
Romanian = 37,
|
||||
Czech = 38,
|
||||
Slovak = 39,
|
||||
Slovenian = 40,
|
||||
Yiddish = 41,
|
||||
Serbian = 42,
|
||||
Macedonian = 43,
|
||||
Bulgarian = 44,
|
||||
Ukrainian = 45,
|
||||
Byelorussian = 46,
|
||||
Uzbek = 47,
|
||||
Kazakh = 48,
|
||||
AzerbaijaniCyrillic = 49,
|
||||
AzerbaijaniArabic = 50,
|
||||
Armenian = 51,
|
||||
Georgian = 52,
|
||||
Moldavian = 53,
|
||||
Kirghiz = 54,
|
||||
Tajiki = 55,
|
||||
Turkmen = 56,
|
||||
Mongolian = 57,
|
||||
MongolianCyrillic = 58,
|
||||
Pashto = 59,
|
||||
Kurdish = 60,
|
||||
Kashmiri = 61,
|
||||
Sindhi = 62,
|
||||
Tibetan = 63,
|
||||
Nepali = 64,
|
||||
Sanskrit = 65,
|
||||
Marathi = 66,
|
||||
Bengali = 67,
|
||||
Assamese = 68,
|
||||
Gujarati = 69,
|
||||
Punjabi = 70,
|
||||
Oriya = 71,
|
||||
Malayalam = 72,
|
||||
Kannada = 73,
|
||||
Tamil = 74,
|
||||
Telugu = 75,
|
||||
Sinhalese = 76,
|
||||
Burmese = 77,
|
||||
Khmer = 78,
|
||||
Lao = 79,
|
||||
Vietnamese = 80,
|
||||
Indonesian = 81,
|
||||
Tagalog = 82,
|
||||
MalayRoman = 83,
|
||||
MalayArabic = 84,
|
||||
Amharic = 85,
|
||||
Tigrinya = 86,
|
||||
Galla = 87,
|
||||
Somali = 88,
|
||||
Swahili = 89,
|
||||
KinyarwandaRuanda = 90,
|
||||
Rundi = 91,
|
||||
NyanjaChewa = 92,
|
||||
Malagasy = 93,
|
||||
Esperanto = 94,
|
||||
Welsh = 128,
|
||||
Basque = 129,
|
||||
Catalan = 130,
|
||||
Latin = 131,
|
||||
Quechua = 132,
|
||||
Guarani = 133,
|
||||
Aymara = 134,
|
||||
Tatar = 135,
|
||||
Uighur = 136,
|
||||
Dzongkha = 137,
|
||||
JavaneseRoman = 138,
|
||||
SundaneseRoman = 139,
|
||||
Galician = 140,
|
||||
Afrikaans = 141,
|
||||
Breton = 142,
|
||||
Inuktitut = 143,
|
||||
ScottishGaelic = 144,
|
||||
ManxGaelic = 145,
|
||||
IrishGaelicWithDotAbove = 146,
|
||||
Tongan = 147,
|
||||
GreekPolytonic = 148,
|
||||
Greenlandic = 149,
|
||||
AzerbaijaniRoman = 150
|
||||
}
|
||||
}
|
@@ -0,0 +1,30 @@
|
||||
namespace UglyToad.PdfPig.Fonts.TrueType.Names
|
||||
{
|
||||
internal class TrueTypeNameRecord
|
||||
{
|
||||
public TrueTypePlatformIdentifier PlatformId { get; }
|
||||
|
||||
public int PlatformEncodingId { get; }
|
||||
|
||||
public int LanguageId { get; }
|
||||
|
||||
public int NameId { get; }
|
||||
|
||||
public int Length { get; }
|
||||
|
||||
public int Offset { get; }
|
||||
|
||||
public string Value { get; }
|
||||
|
||||
public TrueTypeNameRecord(TrueTypePlatformIdentifier platformId, int platformEncodingId, int languageId, int nameId, int length, int offset, string value)
|
||||
{
|
||||
PlatformId = platformId;
|
||||
PlatformEncodingId = platformEncodingId;
|
||||
LanguageId = languageId;
|
||||
NameId = nameId;
|
||||
Length = length;
|
||||
Offset = offset;
|
||||
Value = value;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,25 @@
|
||||
namespace UglyToad.PdfPig.Fonts.TrueType.Names
|
||||
{
|
||||
/// <summary>
|
||||
/// The platform identifier for a TrueType/OpenType font allows for platform specific implementations.
|
||||
/// </summary>
|
||||
internal enum TrueTypePlatformIdentifier
|
||||
{
|
||||
/// <summary>
|
||||
/// Unicode
|
||||
/// </summary>
|
||||
Unicode = 0,
|
||||
/// <summary>
|
||||
/// Macintosh.
|
||||
/// </summary>
|
||||
Macintosh = 1,
|
||||
/// <summary>
|
||||
/// The platform identifier 2 was originally to use with ISO 10646, but is now deprecated, as it and Unicode have identical character code assignments.
|
||||
/// </summary>
|
||||
Iso = 2,
|
||||
/// <summary>
|
||||
/// Microsoft Windows.
|
||||
/// </summary>
|
||||
Windows = 3
|
||||
}
|
||||
}
|
@@ -0,0 +1,37 @@
|
||||
namespace UglyToad.PdfPig.Fonts.TrueType.Names
|
||||
{
|
||||
/// <summary>
|
||||
/// The meaning of the platform specific encoding when the <see cref="TrueTypePlatformIdentifier"/> is <see cref="TrueTypePlatformIdentifier.Unicode"/>.
|
||||
/// </summary>
|
||||
internal enum TrueTypeUnicodeEncodingIndentifier
|
||||
{
|
||||
/// <summary>
|
||||
/// Default semantics.
|
||||
/// </summary>
|
||||
Default = 0,
|
||||
/// <summary>
|
||||
/// Version 1.1 semantics.
|
||||
/// </summary>
|
||||
Version1Point1 = 1,
|
||||
/// <summary>
|
||||
/// ISO 10646 1993 semantics (deprecated).
|
||||
/// </summary>
|
||||
Iso10646 = 2,
|
||||
/// <summary>
|
||||
/// Unicode 2.0 and above semantics for BMP characters only.
|
||||
/// </summary>
|
||||
Unicode2BmpOnly = 3,
|
||||
/// <summary>
|
||||
/// Uncidoe 2.0 and above semantics including non-BMP characters.
|
||||
/// </summary>
|
||||
Unicode2NonBmpAllowed = 4,
|
||||
/// <summary>
|
||||
/// Unicode Variation Sequences.
|
||||
/// </summary>
|
||||
UnicodeVariationSequences = 5,
|
||||
/// <summary>
|
||||
/// Full Unicode coverage.
|
||||
/// </summary>
|
||||
FullUnicode = 6
|
||||
}
|
||||
}
|
@@ -0,0 +1,53 @@
|
||||
namespace UglyToad.PdfPig.Fonts.TrueType.Names
|
||||
{
|
||||
/// <summary>
|
||||
/// The meaning of the platform specific encoding when the <see cref="TrueTypePlatformIdentifier"/> is <see cref="TrueTypePlatformIdentifier.Windows"/>.
|
||||
/// </summary>
|
||||
internal enum TrueTypeWindowsEncodingIdentifier
|
||||
{
|
||||
/// <summary>
|
||||
/// Symbol.
|
||||
/// </summary>
|
||||
Symbol = 0,
|
||||
/// <summary>
|
||||
/// Unicode BMP.
|
||||
/// </summary>
|
||||
UnicodeBmp = 1,
|
||||
/// <summary>
|
||||
/// ShiftJIS.
|
||||
/// </summary>
|
||||
ShiftJis = 2,
|
||||
/// <summary>
|
||||
/// PRC.
|
||||
/// </summary>
|
||||
Prc = 3,
|
||||
/// <summary>
|
||||
/// Big5.
|
||||
/// </summary>
|
||||
Big5 = 4,
|
||||
/// <summary>
|
||||
/// Wansung.
|
||||
/// </summary>
|
||||
Wansung = 5,
|
||||
/// <summary>
|
||||
/// Johab.
|
||||
/// </summary>
|
||||
Johab = 6,
|
||||
/// <summary>
|
||||
/// Reserved.
|
||||
/// </summary>
|
||||
Reserved7 = 7,
|
||||
/// <summary>
|
||||
/// Reserved.
|
||||
/// </summary>
|
||||
Reserved8 = 8,
|
||||
/// <summary>
|
||||
/// Reserved.
|
||||
/// </summary>
|
||||
Reserved9 = 9,
|
||||
/// <summary>
|
||||
/// Unicode full repertoire.
|
||||
/// </summary>
|
||||
FullUnicode = 10
|
||||
}
|
||||
}
|
@@ -45,6 +45,9 @@
|
||||
[NotNull]
|
||||
public BasicMaximumProfileTable MaximumProfileTable { get; }
|
||||
|
||||
[CanBeNull]
|
||||
public NameTable NameTable { get; }
|
||||
|
||||
public PostScriptTable PostScriptTable { get; }
|
||||
|
||||
/// <summary>
|
||||
@@ -73,6 +76,7 @@
|
||||
HorizontalMetricsTable = builder.HorizontalMetricsTable;
|
||||
IndexToLocationTable = builder.IndexToLocationTable ?? throw new ArgumentException("The builder did not contain the index to location table.");
|
||||
MaximumProfileTable = builder.MaximumProfileTable ?? throw new ArgumentException("The builder did not contain the maximum profile table.");
|
||||
NameTable = builder.NameTable;
|
||||
PostScriptTable = builder.PostScriptTable;
|
||||
CMapTable = builder.CMapTable;
|
||||
KerningTable = builder.KerningTable;
|
||||
@@ -106,6 +110,8 @@
|
||||
|
||||
public KerningTable KerningTable { get; set; }
|
||||
|
||||
public NameTable NameTable { get; set; }
|
||||
|
||||
public TableRegister Build()
|
||||
{
|
||||
return new TableRegister(this);
|
||||
|
@@ -2,6 +2,7 @@
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Exceptions;
|
||||
using Tables;
|
||||
using Util.JetBrains.Annotations;
|
||||
|
||||
@@ -61,7 +62,7 @@
|
||||
|
||||
if (!tables.TryGetValue(TrueTypeHeaderTable.Head, out var table))
|
||||
{
|
||||
throw new InvalidOperationException($"The {TrueTypeHeaderTable.Head} table is required.");
|
||||
throw new InvalidFontFormatException($"The {TrueTypeHeaderTable.Head} table is required.");
|
||||
}
|
||||
|
||||
// head
|
||||
@@ -69,7 +70,7 @@
|
||||
|
||||
if (!tables.TryGetValue(TrueTypeHeaderTable.Hhea, out var hHead))
|
||||
{
|
||||
throw new InvalidOperationException("The horizontal header table is required.");
|
||||
throw new InvalidFontFormatException("The horizontal header table is required.");
|
||||
}
|
||||
|
||||
// hhea
|
||||
@@ -77,7 +78,7 @@
|
||||
|
||||
if (!tables.TryGetValue(TrueTypeHeaderTable.Maxp, out var maxHeaderTable))
|
||||
{
|
||||
throw new InvalidOperationException("The maximum profile table is required.");
|
||||
throw new InvalidFontFormatException("The maximum profile table is required.");
|
||||
}
|
||||
|
||||
// maxp
|
||||
@@ -88,12 +89,17 @@
|
||||
{
|
||||
builder.PostScriptTable = PostScriptTable.Load(data, postscriptHeaderTable, builder.MaximumProfileTable);
|
||||
}
|
||||
|
||||
if (tables.TryGetValue(TrueTypeHeaderTable.Name, out var nameTable))
|
||||
{
|
||||
builder.NameTable = NameTable.Load(data, nameTable);
|
||||
}
|
||||
|
||||
if (!isPostScript)
|
||||
{
|
||||
if (!tables.TryGetValue(TrueTypeHeaderTable.Loca, out var indexToLocationHeaderTable))
|
||||
{
|
||||
throw new InvalidOperationException("The location to index table is required for non-PostScript fonts.");
|
||||
throw new InvalidFontFormatException("The location to index table is required for non-PostScript fonts.");
|
||||
}
|
||||
|
||||
// loca
|
||||
@@ -102,7 +108,7 @@
|
||||
|
||||
if (!tables.TryGetValue(TrueTypeHeaderTable.Glyf, out var glyphHeaderTable))
|
||||
{
|
||||
throw new InvalidOperationException("The glyph table is required for non-PostScript fonts.");
|
||||
throw new InvalidFontFormatException("The glyph table is required for non-PostScript fonts.");
|
||||
}
|
||||
|
||||
// glyf
|
||||
|
@@ -1,53 +1,170 @@
|
||||
namespace UglyToad.PdfPig.Fonts.TrueType.Tables
|
||||
{
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using Names;
|
||||
using Util;
|
||||
using Util.JetBrains.Annotations;
|
||||
|
||||
internal class NameTable
|
||||
{
|
||||
public string Tag => TrueTypeHeaderTable.Name;
|
||||
|
||||
public TrueTypeHeaderTable DirectoryTable { get; }
|
||||
|
||||
public static void Load(TrueTypeDataBytes data, TrueTypeHeaderTable table)
|
||||
public string FontName { get; }
|
||||
|
||||
public string FontFamilyName { get; }
|
||||
|
||||
public string FontSubFamilyName { get; }
|
||||
|
||||
public IReadOnlyList<TrueTypeNameRecord> NameRecords { get; }
|
||||
|
||||
public NameTable(TrueTypeHeaderTable directoryTable,
|
||||
string fontName,
|
||||
string fontFamilyName,
|
||||
string fontSubFamilyName,
|
||||
IReadOnlyList<TrueTypeNameRecord> nameRecords)
|
||||
{
|
||||
DirectoryTable = directoryTable;
|
||||
FontName = fontName;
|
||||
FontFamilyName = fontFamilyName;
|
||||
FontSubFamilyName = fontSubFamilyName;
|
||||
NameRecords = nameRecords;
|
||||
}
|
||||
|
||||
internal static NameTable Load(TrueTypeDataBytes data, TrueTypeHeaderTable table)
|
||||
{
|
||||
data.Seek(table.Offset);
|
||||
// ReSharper disable once UnusedVariable
|
||||
var format = data.ReadUnsignedShort();
|
||||
var count = data.ReadUnsignedShort();
|
||||
var stringOffset = data.ReadUnsignedShort();
|
||||
|
||||
var names = new NameRecord[count];
|
||||
|
||||
var names = new NameRecordBuilder[count];
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
names[i] = NameRecord.Read(data);
|
||||
names[i] = NameRecordBuilder.Read(data);
|
||||
}
|
||||
|
||||
var strings = new TrueTypeNameRecord[count];
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
var nameRecord = names[i];
|
||||
|
||||
var encoding = OtherEncodings.Iso88591;
|
||||
|
||||
switch (nameRecord.PlatformId)
|
||||
{
|
||||
case TrueTypePlatformIdentifier.Windows:
|
||||
{
|
||||
var platformEncoding = (TrueTypeWindowsEncodingIdentifier) nameRecord.PlatformEncodingId;
|
||||
|
||||
if (platformEncoding == TrueTypeWindowsEncodingIdentifier.Symbol
|
||||
|| platformEncoding == TrueTypeWindowsEncodingIdentifier.UnicodeBmp)
|
||||
{
|
||||
encoding = Encoding.BigEndianUnicode;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case TrueTypePlatformIdentifier.Unicode:
|
||||
{
|
||||
encoding = Encoding.BigEndianUnicode;
|
||||
break;
|
||||
}
|
||||
case TrueTypePlatformIdentifier.Iso:
|
||||
{
|
||||
switch (nameRecord.PlatformEncodingId)
|
||||
{
|
||||
case 0:
|
||||
encoding = Encoding.GetEncoding("US-ASCII");
|
||||
break;
|
||||
case 1:
|
||||
encoding = Encoding.GetEncoding("ISO-10646-UCS-2");
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
var position = table.Offset + stringOffset + nameRecord.Offset;
|
||||
|
||||
data.Seek(position);
|
||||
|
||||
var str = data.ReadString(nameRecord.Length, encoding);
|
||||
|
||||
strings[i] = nameRecord.ToNameRecord(str);
|
||||
}
|
||||
|
||||
return new NameTable(table, GetName(4, strings), GetName(1, strings), GetName(2, strings), strings);
|
||||
}
|
||||
|
||||
public struct NameRecord
|
||||
[CanBeNull]
|
||||
private static string GetName(int nameId, TrueTypeNameRecord[] names)
|
||||
{
|
||||
public int PlatformId { get; }
|
||||
const int windowsEnUs = 409;
|
||||
string windows = null;
|
||||
string any = null;
|
||||
|
||||
public int PlatformSpecificId { get; }
|
||||
for (var i = 0; i < names.Length; i++)
|
||||
{
|
||||
var name = names[i];
|
||||
|
||||
public int LanguageId { get; }
|
||||
if (name.NameId != nameId)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
public int NameId { get; }
|
||||
if (name.PlatformId == TrueTypePlatformIdentifier.Windows && name.LanguageId == windowsEnUs)
|
||||
{
|
||||
return name.Value;
|
||||
}
|
||||
|
||||
if (name.PlatformId == TrueTypePlatformIdentifier.Windows)
|
||||
{
|
||||
windows = name.Value;
|
||||
}
|
||||
|
||||
any = name.Value;
|
||||
}
|
||||
|
||||
return windows ?? any;
|
||||
}
|
||||
|
||||
private struct NameRecordBuilder
|
||||
{
|
||||
public TrueTypePlatformIdentifier PlatformId { get; }
|
||||
|
||||
public int PlatformEncodingId { get; }
|
||||
|
||||
private int LanguageId { get; }
|
||||
|
||||
private int NameId { get; }
|
||||
|
||||
public int Length { get; }
|
||||
|
||||
public int Offset { get; }
|
||||
|
||||
public NameRecord(int platformId, int platformSpecificId, int languageId, int nameId, int length, int offset)
|
||||
private NameRecordBuilder(int platformId, int platformEncodingId, int languageId, int nameId, int length, int offset)
|
||||
{
|
||||
PlatformId = platformId;
|
||||
PlatformSpecificId = platformSpecificId;
|
||||
PlatformId = (TrueTypePlatformIdentifier)platformId;
|
||||
PlatformEncodingId = platformEncodingId;
|
||||
LanguageId = languageId;
|
||||
NameId = nameId;
|
||||
Length = length;
|
||||
Offset = offset;
|
||||
}
|
||||
|
||||
public static NameRecord Read(TrueTypeDataBytes data)
|
||||
public TrueTypeNameRecord ToNameRecord(string s)
|
||||
{
|
||||
return new NameRecord(data.ReadUnsignedShort(),
|
||||
return new TrueTypeNameRecord(PlatformId, PlatformEncodingId,
|
||||
LanguageId, NameId, Length, Offset, s);
|
||||
}
|
||||
|
||||
public static NameRecordBuilder Read(TrueTypeDataBytes data)
|
||||
{
|
||||
return new NameRecordBuilder(data.ReadUnsignedShort(),
|
||||
data.ReadUnsignedShort(),
|
||||
data.ReadUnsignedShort(),
|
||||
data.ReadUnsignedShort(),
|
||||
|
@@ -5,6 +5,7 @@
|
||||
using CidFonts;
|
||||
using Geometry;
|
||||
using Parser;
|
||||
using Util.JetBrains.Annotations;
|
||||
|
||||
internal class TrueTypeFontProgram : ICidFontProgram
|
||||
{
|
||||
@@ -12,8 +13,12 @@
|
||||
|
||||
public IReadOnlyDictionary<string, TrueTypeHeaderTable> TableHeaders { get; }
|
||||
|
||||
[NotNull]
|
||||
public TableRegister TableRegister { get; }
|
||||
|
||||
[CanBeNull]
|
||||
public string Name => TableRegister.NameTable?.FontName;
|
||||
|
||||
public TrueTypeFontProgram(decimal version, IReadOnlyDictionary<string, TrueTypeHeaderTable> tableHeaders, TableRegister tableRegister)
|
||||
{
|
||||
Version = version;
|
||||
|
Reference in New Issue
Block a user