2020-01-05 12:08:01 +00:00
|
|
|
|
namespace UglyToad.PdfPig.Fonts.CompactFontFormat
|
2018-04-28 19:33:50 +01:00
|
|
|
|
{
|
2018-11-16 21:30:59 +00:00
|
|
|
|
using System;
|
2018-11-16 23:32:18 +00:00
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
|
using Charsets;
|
2018-11-16 21:30:59 +00:00
|
|
|
|
using CharStrings;
|
2020-01-04 16:38:18 +00:00
|
|
|
|
using Core;
|
2018-04-29 14:42:54 +01:00
|
|
|
|
using Dictionaries;
|
2019-01-12 18:07:53 +00:00
|
|
|
|
using Encodings;
|
2020-01-04 16:38:18 +00:00
|
|
|
|
using Fonts;
|
2018-11-17 14:59:58 +00:00
|
|
|
|
using Type1.CharStrings;
|
2018-04-28 19:33:50 +01:00
|
|
|
|
|
|
|
|
|
|
internal class CompactFontFormatIndividualFontParser
|
|
|
|
|
|
{
|
2018-04-29 14:42:54 +01:00
|
|
|
|
private readonly CompactFontFormatTopLevelDictionaryReader topLevelDictionaryReader;
|
|
|
|
|
|
private readonly CompactFontFormatPrivateDictionaryReader privateDictionaryReader;
|
2018-04-28 19:33:50 +01:00
|
|
|
|
|
2019-12-19 13:33:44 +00:00
|
|
|
|
public CompactFontFormatIndividualFontParser(CompactFontFormatTopLevelDictionaryReader topLevelDictionaryReader,
|
2018-04-29 14:42:54 +01:00
|
|
|
|
CompactFontFormatPrivateDictionaryReader privateDictionaryReader)
|
2018-04-28 19:33:50 +01:00
|
|
|
|
{
|
2018-04-29 14:42:54 +01:00
|
|
|
|
this.topLevelDictionaryReader = topLevelDictionaryReader;
|
|
|
|
|
|
this.privateDictionaryReader = privateDictionaryReader;
|
2018-04-28 19:33:50 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2024-04-18 11:58:40 -07:00
|
|
|
|
public CompactFontFormatFont Parse(CompactFontFormatData data, string name, ReadOnlySpan<byte> topDictionaryIndex, ReadOnlySpan<string> stringIndex,
|
2018-11-17 14:59:58 +00:00
|
|
|
|
CompactFontFormatIndex globalSubroutineIndex)
|
2018-04-28 19:33:50 +01:00
|
|
|
|
{
|
2018-11-17 14:59:58 +00:00
|
|
|
|
var individualData = new CompactFontFormatData(topDictionaryIndex.ToArray());
|
2018-04-28 19:33:50 +01:00
|
|
|
|
|
2018-11-16 23:32:18 +00:00
|
|
|
|
var topDictionary = topLevelDictionaryReader.Read(individualData, stringIndex);
|
2019-05-11 11:56:01 +01:00
|
|
|
|
|
2018-11-17 22:16:27 +00:00
|
|
|
|
var privateDictionary = CompactFontFormatPrivateDictionary.GetDefault();
|
2018-04-28 19:33:50 +01:00
|
|
|
|
|
2018-12-20 18:18:32 +00:00
|
|
|
|
if (topDictionary.PrivateDictionaryLocation.HasValue && topDictionary.PrivateDictionaryLocation.Value.Size > 0)
|
2018-04-28 19:33:50 +01:00
|
|
|
|
{
|
2018-11-18 13:53:43 +00:00
|
|
|
|
var privateDictionaryBytes = data.SnapshotPortion(topDictionary.PrivateDictionaryLocation.Value.Offset,
|
|
|
|
|
|
topDictionary.PrivateDictionaryLocation.Value.Size);
|
2018-04-28 19:33:50 +01:00
|
|
|
|
|
2018-11-18 13:53:43 +00:00
|
|
|
|
privateDictionary = privateDictionaryReader.Read(privateDictionaryBytes, stringIndex);
|
2018-04-28 19:33:50 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2018-11-16 23:32:18 +00:00
|
|
|
|
if (topDictionary.CharStringsOffset < 0)
|
2018-04-28 19:33:50 +01:00
|
|
|
|
{
|
2018-11-16 23:32:18 +00:00
|
|
|
|
throw new InvalidOperationException("Expected CFF to contain a CharString offset.");
|
2018-04-28 19:33:50 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2018-11-17 14:59:58 +00:00
|
|
|
|
var localSubroutines = CompactFontFormatIndex.None;
|
2018-11-18 13:53:43 +00:00
|
|
|
|
if (privateDictionary.LocalSubroutineOffset.HasValue && topDictionary.PrivateDictionaryLocation.HasValue)
|
2018-11-17 14:59:58 +00:00
|
|
|
|
{
|
2018-11-18 13:53:43 +00:00
|
|
|
|
data.Seek(privateDictionary.LocalSubroutineOffset.Value + topDictionary.PrivateDictionaryLocation.Value.Offset);
|
2018-11-17 14:59:58 +00:00
|
|
|
|
|
2019-12-19 13:33:44 +00:00
|
|
|
|
localSubroutines = CompactFontFormatIndexReader.ReadDictionaryData(data);
|
2018-11-17 14:59:58 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2018-11-16 23:32:18 +00:00
|
|
|
|
data.Seek(topDictionary.CharStringsOffset);
|
2018-04-28 19:33:50 +01:00
|
|
|
|
|
2019-12-19 13:33:44 +00:00
|
|
|
|
var charStringIndex = CompactFontFormatIndexReader.ReadDictionaryData(data);
|
2018-11-16 21:30:59 +00:00
|
|
|
|
|
2018-12-29 11:41:46 +00:00
|
|
|
|
ICompactFontFormatCharset charset;
|
|
|
|
|
|
if (topDictionary.CharSetOffset >= 0)
|
2018-11-16 23:32:18 +00:00
|
|
|
|
{
|
2018-12-29 11:41:46 +00:00
|
|
|
|
var charsetId = topDictionary.CharSetOffset;
|
|
|
|
|
|
if (!topDictionary.IsCidFont && charsetId == 0)
|
2018-11-16 21:30:59 +00:00
|
|
|
|
{
|
2018-12-29 11:41:46 +00:00
|
|
|
|
charset = CompactFontFormatIsoAdobeCharset.Value;
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (!topDictionary.IsCidFont && charsetId == 1)
|
|
|
|
|
|
{
|
|
|
|
|
|
charset = CompactFontFormatExpertCharset.Value;
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (!topDictionary.IsCidFont && charsetId == 2)
|
|
|
|
|
|
{
|
|
|
|
|
|
charset = CompactFontFormatExpertSubsetCharset.Value;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
charset = ReadCharset(data, topDictionary, charStringIndex, stringIndex);
|
2018-11-16 23:32:18 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2018-12-29 11:41:46 +00:00
|
|
|
|
if (topDictionary.IsCidFont)
|
2018-11-16 23:32:18 +00:00
|
|
|
|
{
|
2018-12-29 11:41:46 +00:00
|
|
|
|
// a CID font with no charset does not default to any predefined charset
|
|
|
|
|
|
charset = new CompactFontFormatEmptyCharset(charStringIndex.Count);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
charset = CompactFontFormatIsoAdobeCharset.Value;
|
2018-11-16 21:30:59 +00:00
|
|
|
|
}
|
2018-04-28 19:33:50 +01:00
|
|
|
|
}
|
2019-12-19 13:33:44 +00:00
|
|
|
|
|
2018-12-29 11:41:46 +00:00
|
|
|
|
if (topDictionary.IsCidFont)
|
|
|
|
|
|
{
|
2019-12-19 13:33:44 +00:00
|
|
|
|
return ReadCidFont(data, topDictionary, charStringIndex.Count, stringIndex, privateDictionary,
|
|
|
|
|
|
charset,
|
|
|
|
|
|
globalSubroutineIndex,
|
|
|
|
|
|
localSubroutines,
|
|
|
|
|
|
charStringIndex);
|
2018-12-29 11:41:46 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2019-01-12 18:07:53 +00:00
|
|
|
|
var encoding = topDictionary.EncodingOffset;
|
|
|
|
|
|
|
|
|
|
|
|
Encoding fontEncoding = null;
|
|
|
|
|
|
if (encoding != CompactFontFormatTopLevelDictionary.UnsetOffset)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (encoding == 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
fontEncoding = CompactFontFormatStandardEncoding.Instance;
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (encoding == 1)
|
|
|
|
|
|
{
|
|
|
|
|
|
fontEncoding = CompactFontFormatExpertEncoding.Instance;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
data.Seek(encoding);
|
|
|
|
|
|
fontEncoding = CompactFontFormatEncodingReader.ReadEncoding(data, charset, stringIndex);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2019-12-19 13:33:44 +00:00
|
|
|
|
var subroutineSelector = new CompactFontFormatSubroutinesSelector(globalSubroutineIndex, localSubroutines);
|
|
|
|
|
|
|
|
|
|
|
|
var charStrings = ReadCharStrings(data, topDictionary, charStringIndex, subroutineSelector, charset);
|
|
|
|
|
|
|
2019-01-12 18:07:53 +00:00
|
|
|
|
return new CompactFontFormatFont(topDictionary, privateDictionary, charset, Union<Type1CharStrings, Type2CharStrings>.Two(charStrings), fontEncoding);
|
2018-11-16 23:32:18 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2024-04-18 11:58:40 -07:00
|
|
|
|
private static ICompactFontFormatCharset ReadCharset(CompactFontFormatData data,
|
|
|
|
|
|
CompactFontFormatTopLevelDictionary topDictionary,
|
|
|
|
|
|
CompactFontFormatIndex charStringIndex,
|
|
|
|
|
|
ReadOnlySpan<string> stringIndex)
|
2018-12-29 11:41:46 +00:00
|
|
|
|
{
|
|
|
|
|
|
data.Seek(topDictionary.CharSetOffset);
|
|
|
|
|
|
|
|
|
|
|
|
var format = data.ReadCard8();
|
|
|
|
|
|
|
|
|
|
|
|
switch (format)
|
|
|
|
|
|
{
|
|
|
|
|
|
case 0:
|
|
|
|
|
|
{
|
2024-04-28 10:55:58 -07:00
|
|
|
|
using var glyphToNamesAndStringId = new ArrayPoolBufferWriter<(int glyphId, int stringId, string name)>();
|
2018-12-29 11:41:46 +00:00
|
|
|
|
|
|
|
|
|
|
for (var glyphId = 1; glyphId < charStringIndex.Count; glyphId++)
|
|
|
|
|
|
{
|
|
|
|
|
|
var stringId = data.ReadSid();
|
2024-04-28 10:55:58 -07:00
|
|
|
|
glyphToNamesAndStringId.Write((glyphId, stringId, ReadString(stringId, stringIndex)));
|
2018-12-29 11:41:46 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2024-04-28 10:55:58 -07:00
|
|
|
|
return new CompactFontFormatFormat0Charset(glyphToNamesAndStringId.WrittenSpan);
|
2018-12-29 11:41:46 +00:00
|
|
|
|
}
|
|
|
|
|
|
case 1:
|
|
|
|
|
|
case 2:
|
|
|
|
|
|
{
|
2024-04-28 10:55:58 -07:00
|
|
|
|
using var glyphToNamesAndStringId = new ArrayPoolBufferWriter<(int glyphId, int stringId, string name)>();
|
2018-12-29 11:41:46 +00:00
|
|
|
|
|
|
|
|
|
|
for (var glyphId = 1; glyphId < charStringIndex.Count; glyphId++)
|
|
|
|
|
|
{
|
|
|
|
|
|
var firstSid = data.ReadSid();
|
|
|
|
|
|
var numberInRange = format == 1 ? data.ReadCard8() : data.ReadCard16();
|
|
|
|
|
|
|
2024-04-28 10:55:58 -07:00
|
|
|
|
glyphToNamesAndStringId.Write((glyphId, firstSid, ReadString(firstSid, stringIndex)));
|
2018-12-29 11:41:46 +00:00
|
|
|
|
for (var i = 0; i < numberInRange; i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
glyphId++;
|
|
|
|
|
|
var sid = firstSid + i + 1;
|
2024-04-28 10:55:58 -07:00
|
|
|
|
glyphToNamesAndStringId.Write((glyphId, sid, ReadString(sid, stringIndex)));
|
2018-12-29 11:41:46 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (format == 1)
|
|
|
|
|
|
{
|
|
|
|
|
|
|
2024-04-28 10:55:58 -07:00
|
|
|
|
return new CompactFontFormatFormat1Charset(glyphToNamesAndStringId.WrittenSpan);
|
2018-12-29 11:41:46 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2024-04-28 10:55:58 -07:00
|
|
|
|
return new CompactFontFormatFormat2Charset(glyphToNamesAndStringId.WrittenSpan);
|
2018-12-29 11:41:46 +00:00
|
|
|
|
}
|
|
|
|
|
|
default:
|
|
|
|
|
|
throw new InvalidOperationException($"Unrecognized format for the Charset table in a CFF font. Got: {format}.");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-04-18 11:58:40 -07:00
|
|
|
|
private static string ReadString(int index, ReadOnlySpan<string> stringIndex)
|
2018-11-16 23:32:18 +00:00
|
|
|
|
{
|
|
|
|
|
|
if (index >= 0 && index <= 390)
|
|
|
|
|
|
{
|
|
|
|
|
|
return CompactFontFormatStandardStrings.GetName(index);
|
|
|
|
|
|
}
|
2024-04-18 11:58:40 -07:00
|
|
|
|
if (index - 391 < stringIndex.Length)
|
2018-11-16 23:32:18 +00:00
|
|
|
|
{
|
|
|
|
|
|
return stringIndex[index - 391];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2018-11-19 20:41:52 +00:00
|
|
|
|
// technically this maps to .notdef, but PDFBox uses this
|
2018-11-16 23:32:18 +00:00
|
|
|
|
return "SID" + index;
|
2018-04-28 19:33:50 +01:00
|
|
|
|
}
|
2018-12-29 11:41:46 +00:00
|
|
|
|
|
2019-12-19 13:33:44 +00:00
|
|
|
|
private static Type2CharStrings ReadCharStrings(CompactFontFormatData data, CompactFontFormatTopLevelDictionary topDictionary,
|
|
|
|
|
|
CompactFontFormatIndex charStringIndex,
|
|
|
|
|
|
CompactFontFormatSubroutinesSelector subroutinesSelector,
|
|
|
|
|
|
ICompactFontFormatCharset charset)
|
|
|
|
|
|
{
|
|
|
|
|
|
data.Seek(topDictionary.CharStringsOffset);
|
|
|
|
|
|
|
|
|
|
|
|
switch (topDictionary.CharStringType)
|
|
|
|
|
|
{
|
|
|
|
|
|
case CompactFontFormatCharStringType.Type1:
|
|
|
|
|
|
throw new NotImplementedException("Type 1 CharStrings are not currently supported in CFF font.");
|
|
|
|
|
|
case CompactFontFormatCharStringType.Type2:
|
|
|
|
|
|
return Type2CharStringParser.Parse(charStringIndex, subroutinesSelector, charset);
|
|
|
|
|
|
default:
|
|
|
|
|
|
throw new ArgumentOutOfRangeException($"Unexpected CharString type in CFF font: {topDictionary.CharStringType}.");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-04-18 11:58:40 -07:00
|
|
|
|
private CompactFontFormatCidFont ReadCidFont(CompactFontFormatData data,
|
|
|
|
|
|
CompactFontFormatTopLevelDictionary topLevelDictionary,
|
2018-12-29 11:41:46 +00:00
|
|
|
|
int numberOfGlyphs,
|
2024-04-18 11:58:40 -07:00
|
|
|
|
ReadOnlySpan<string> stringIndex,
|
2019-01-07 20:52:42 +00:00
|
|
|
|
CompactFontFormatPrivateDictionary privateDictionary,
|
|
|
|
|
|
ICompactFontFormatCharset charset,
|
2019-12-19 13:33:44 +00:00
|
|
|
|
CompactFontFormatIndex globalSubroutines,
|
|
|
|
|
|
CompactFontFormatIndex localSubroutinesTop,
|
|
|
|
|
|
CompactFontFormatIndex charStringIndex)
|
2018-12-29 11:41:46 +00:00
|
|
|
|
{
|
|
|
|
|
|
var offset = topLevelDictionary.CidFontOperators.FontDictionaryArray;
|
|
|
|
|
|
|
|
|
|
|
|
data.Seek(offset);
|
|
|
|
|
|
|
2019-12-19 13:33:44 +00:00
|
|
|
|
var fontDict = CompactFontFormatIndexReader.ReadDictionaryData(data);
|
2018-12-29 11:41:46 +00:00
|
|
|
|
|
2019-01-07 20:52:42 +00:00
|
|
|
|
var privateDictionaries = new List<CompactFontFormatPrivateDictionary>();
|
|
|
|
|
|
var fontDictionaries = new List<CompactFontFormatTopLevelDictionary>();
|
|
|
|
|
|
var fontLocalSubroutines = new List<CompactFontFormatIndex>();
|
2019-12-19 13:33:44 +00:00
|
|
|
|
|
2018-12-29 11:41:46 +00:00
|
|
|
|
foreach (var index in fontDict)
|
|
|
|
|
|
{
|
|
|
|
|
|
var topLevelDictionaryCid = topLevelDictionaryReader.Read(new CompactFontFormatData(index), stringIndex);
|
|
|
|
|
|
|
|
|
|
|
|
if (!topLevelDictionaryCid.PrivateDictionaryLocation.HasValue)
|
|
|
|
|
|
{
|
|
|
|
|
|
throw new InvalidFontFormatException("The CID keyed Compact Font Format font did not contain a private dictionary for the font dictionary.");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var privateDictionaryBytes = data.SnapshotPortion(topLevelDictionaryCid.PrivateDictionaryLocation.Value.Offset,
|
|
|
|
|
|
topLevelDictionaryCid.PrivateDictionaryLocation.Value.Size);
|
|
|
|
|
|
|
2019-01-07 20:52:42 +00:00
|
|
|
|
var privateDictionaryCid = privateDictionaryReader.Read(privateDictionaryBytes, stringIndex);
|
2018-12-29 11:41:46 +00:00
|
|
|
|
|
|
|
|
|
|
// CFFParser.java line 625 - read the local subroutines.
|
|
|
|
|
|
if (privateDictionaryCid.LocalSubroutineOffset.HasValue && privateDictionaryCid.LocalSubroutineOffset.Value > 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
data.Seek(topLevelDictionaryCid.PrivateDictionaryLocation.Value.Offset + privateDictionaryCid.LocalSubroutineOffset.Value);
|
2019-12-19 13:33:44 +00:00
|
|
|
|
var localSubroutines = CompactFontFormatIndexReader.ReadDictionaryData(data);
|
2019-01-07 20:52:42 +00:00
|
|
|
|
fontLocalSubroutines.Add(localSubroutines);
|
2018-12-29 11:41:46 +00:00
|
|
|
|
}
|
2019-12-19 13:33:44 +00:00
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
fontLocalSubroutines.Add(null);
|
|
|
|
|
|
}
|
2019-01-07 20:52:42 +00:00
|
|
|
|
|
|
|
|
|
|
fontDictionaries.Add(topLevelDictionaryCid);
|
|
|
|
|
|
privateDictionaries.Add(privateDictionaryCid);
|
2018-12-29 11:41:46 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
data.Seek(topLevelDictionary.CidFontOperators.FontDictionarySelect);
|
|
|
|
|
|
|
|
|
|
|
|
var format = data.ReadCard8();
|
|
|
|
|
|
|
|
|
|
|
|
ICompactFontFormatFdSelect fdSelect;
|
|
|
|
|
|
switch (format)
|
|
|
|
|
|
{
|
|
|
|
|
|
case 0:
|
|
|
|
|
|
{
|
|
|
|
|
|
fdSelect = ReadFormat0FdSelect(data, numberOfGlyphs, topLevelDictionary.CidFontOperators.Ros);
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
case 3:
|
|
|
|
|
|
{
|
|
|
|
|
|
fdSelect = ReadFormat3FdSelect(data, topLevelDictionary.CidFontOperators.Ros);
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
default:
|
|
|
|
|
|
throw new InvalidFontFormatException($"Invalid Font Dictionary Select format: {format}.");
|
|
|
|
|
|
}
|
2019-01-07 20:52:42 +00:00
|
|
|
|
|
2019-12-19 13:33:44 +00:00
|
|
|
|
var subroutineSelector = new CompactFontFormatSubroutinesSelector(globalSubroutines, localSubroutinesTop,
|
|
|
|
|
|
fdSelect, fontLocalSubroutines);
|
|
|
|
|
|
|
|
|
|
|
|
var charStrings = ReadCharStrings(data, topLevelDictionary, charStringIndex, subroutineSelector, charset);
|
|
|
|
|
|
|
|
|
|
|
|
var union = Union<Type1CharStrings, Type2CharStrings>.Two(charStrings);
|
|
|
|
|
|
|
|
|
|
|
|
return new CompactFontFormatCidFont(topLevelDictionary, privateDictionary, charset, union,
|
|
|
|
|
|
fontDictionaries, privateDictionaries, fdSelect);
|
2018-12-29 11:41:46 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private static CompactFontFormat0FdSelect ReadFormat0FdSelect(CompactFontFormatData data, int numberOfGlyphs,
|
|
|
|
|
|
RegistryOrderingSupplement registryOrderingSupplement)
|
|
|
|
|
|
{
|
|
|
|
|
|
var dictionaries = new int[numberOfGlyphs];
|
|
|
|
|
|
|
|
|
|
|
|
for (var i = 0; i < numberOfGlyphs; i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
dictionaries[i] = data.ReadCard8();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return new CompactFontFormat0FdSelect(registryOrderingSupplement, dictionaries);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private static CompactFontFormat3FdSelect ReadFormat3FdSelect(CompactFontFormatData data, RegistryOrderingSupplement registryOrderingSupplement)
|
|
|
|
|
|
{
|
|
|
|
|
|
var numberOfRanges = data.ReadCard16();
|
|
|
|
|
|
var ranges = new CompactFontFormat3FdSelect.Range3[numberOfRanges];
|
|
|
|
|
|
|
|
|
|
|
|
for (var i = 0; i < numberOfRanges; i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
var first = data.ReadCard16();
|
|
|
|
|
|
var dictionary = data.ReadCard8();
|
|
|
|
|
|
|
|
|
|
|
|
ranges[i] = new CompactFontFormat3FdSelect.Range3(first, dictionary);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var sentinel = data.ReadCard16();
|
|
|
|
|
|
|
|
|
|
|
|
return new CompactFontFormat3FdSelect(registryOrderingSupplement, ranges, sentinel);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
internal interface ICompactFontFormatFdSelect
|
|
|
|
|
|
{
|
|
|
|
|
|
int GetFontDictionaryIndex(int glyphId);
|
2018-04-28 19:33:50 +01:00
|
|
|
|
}
|
2018-12-29 11:41:46 +00:00
|
|
|
|
|
|
|
|
|
|
internal class CompactFontFormat0FdSelect : ICompactFontFormatFdSelect
|
|
|
|
|
|
{
|
|
|
|
|
|
public RegistryOrderingSupplement RegistryOrderingSupplement { get; }
|
|
|
|
|
|
|
|
|
|
|
|
public IReadOnlyList<int> FontDictionaries { get; }
|
|
|
|
|
|
|
|
|
|
|
|
public CompactFontFormat0FdSelect(RegistryOrderingSupplement registryOrderingSupplement, IReadOnlyList<int> fontDictionaries)
|
|
|
|
|
|
{
|
|
|
|
|
|
RegistryOrderingSupplement = registryOrderingSupplement ?? throw new ArgumentNullException(nameof(registryOrderingSupplement));
|
|
|
|
|
|
FontDictionaries = fontDictionaries ?? throw new ArgumentNullException(nameof(fontDictionaries));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public int GetFontDictionaryIndex(int glyphId)
|
|
|
|
|
|
{
|
2019-12-04 14:18:20 +00:00
|
|
|
|
if (glyphId < FontDictionaries.Count && glyphId >= 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
return FontDictionaries[glyphId];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return 0;
|
2018-12-29 11:41:46 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
internal class CompactFontFormat3FdSelect : ICompactFontFormatFdSelect
|
|
|
|
|
|
{
|
|
|
|
|
|
public RegistryOrderingSupplement RegistryOrderingSupplement { get; }
|
|
|
|
|
|
|
|
|
|
|
|
public IReadOnlyList<Range3> Ranges { get; }
|
|
|
|
|
|
|
|
|
|
|
|
public int Sentinel { get; }
|
|
|
|
|
|
|
|
|
|
|
|
public CompactFontFormat3FdSelect(RegistryOrderingSupplement registryOrderingSupplement, IReadOnlyList<Range3> ranges, int sentinel)
|
|
|
|
|
|
{
|
|
|
|
|
|
RegistryOrderingSupplement = registryOrderingSupplement ?? throw new ArgumentNullException(nameof(registryOrderingSupplement));
|
|
|
|
|
|
Ranges = ranges ?? throw new ArgumentNullException(nameof(ranges));
|
|
|
|
|
|
Sentinel = sentinel;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public int GetFontDictionaryIndex(int glyphId)
|
|
|
|
|
|
{
|
2019-12-04 14:18:20 +00:00
|
|
|
|
for (var i = 0; i < Ranges.Count; ++i)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (Ranges[i].First <= glyphId)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (i + 1 < Ranges.Count)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (Ranges[i + 1].First > glyphId)
|
|
|
|
|
|
{
|
|
|
|
|
|
return Ranges[i].FontDictionary;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
if (Sentinel > glyphId)
|
|
|
|
|
|
{
|
|
|
|
|
|
return Ranges[i].FontDictionary;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return 0;
|
2018-12-29 11:41:46 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2023-10-18 23:44:11 +01:00
|
|
|
|
internal readonly struct Range3
|
2018-12-29 11:41:46 +00:00
|
|
|
|
{
|
|
|
|
|
|
public int First { get; }
|
|
|
|
|
|
|
|
|
|
|
|
public int FontDictionary { get; }
|
|
|
|
|
|
|
|
|
|
|
|
public Range3(int first, int fontDictionary)
|
|
|
|
|
|
{
|
|
|
|
|
|
First = first;
|
|
|
|
|
|
FontDictionary = fontDictionary;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public override string ToString()
|
|
|
|
|
|
{
|
|
|
|
|
|
return $"First {First}, Dictionary {FontDictionary}.";
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2018-04-28 19:33:50 +01:00
|
|
|
|
}
|