back-calculate first char if last char and widths present (#1081)

* back-calculate first char if last char and widths present

when a truetype font has a last char and widths array in its font
dictionary the first char can be calculated #644

* fix off by 1 in last char calculation
This commit is contained in:
Eliot Jones 2025-07-14 15:57:01 -05:00 committed by GitHub
parent de3b6ac6f4
commit 016b754c5b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -49,13 +49,18 @@
public IFont Generate(DictionaryToken dictionary)
{
if (!dictionary.TryGetOptionalTokenDirect(NameToken.FirstChar, pdfScanner, out NumericToken? firstCharacterToken)
int? firstChar = null;
if (!dictionary.TryGetOptionalTokenDirect(NameToken.FirstChar,
pdfScanner,
out NumericToken? firstCharacterToken)
|| !dictionary.TryGet<IToken>(NameToken.FontDescriptor, pdfScanner, out _)
|| !dictionary.TryGet(NameToken.Widths, out IToken _))
|| !dictionary.TryGet(NameToken.Widths, out _))
{
var isStandard14 = true;
if (!dictionary.TryGetOptionalTokenDirect(NameToken.BaseFont, pdfScanner, out NameToken? baseFont))
{
throw new InvalidFontFormatException($"The provided TrueType font dictionary did not contain a /FirstChar or a /BaseFont entry: {dictionary}.");
throw new InvalidFontFormatException(
$"The provided TrueType font dictionary did not contain a /FirstChar or a /BaseFont entry: {dictionary}.");
}
// Can use the AFM descriptor despite not being Type 1!
@ -63,37 +68,53 @@
if (standard14Font is null)
{
throw new InvalidFontFormatException($"The provided TrueType font dictionary did not have a /FirstChar and did not match a Standard 14 font: {dictionary}.");
if (dictionary.TryGet(NameToken.LastChar, pdfScanner, out NumericToken? lastCharToken)
&& dictionary.TryGet(NameToken.Widths, pdfScanner, out ArrayToken? widthsArrayLoc))
{
// If the widths array contains widths for characters 0 - 3 it will contain 4 entries but last char is 3.
firstChar = (lastCharToken.Int - widthsArrayLoc.Length) + 1;
isStandard14 = false;
}
else
{
throw new InvalidFontFormatException(
$"The provided TrueType font dictionary did not have a /FirstChar and did not match a Standard 14 font: {dictionary}.");
}
}
var fileSystemFont = systemFontFinder.GetTrueTypeFont(baseFont.Data);
var thisEncoding = encodingReader.Read(dictionary);
if (thisEncoding is null)
if (isStandard14)
{
thisEncoding = new AdobeFontMetricsEncoding(standard14Font);
var fileSystemFont = systemFontFinder.GetTrueTypeFont(baseFont.Data);
var thisEncoding = encodingReader.Read(dictionary);
if (thisEncoding is null)
{
thisEncoding = new AdobeFontMetricsEncoding(standard14Font);
}
double[]? widthsOverride = null;
if (dictionary.TryGet(NameToken.FirstChar, pdfScanner, out firstCharacterToken))
{
firstChar = firstCharacterToken.Int;
}
if (dictionary.TryGet(NameToken.Widths, pdfScanner, out ArrayToken? widthsArray))
{
widthsOverride = widthsArray.Data.OfType<NumericToken>()
.Select(x => x.Double).ToArray();
}
return new TrueTypeStandard14FallbackSimpleFont(baseFont,
standard14Font!,
thisEncoding,
fileSystemFont,
new TrueTypeStandard14FallbackSimpleFont.MetricOverrides(firstChar, widthsOverride));
}
int? firstChar = null;
double[]? widthsOverride = null;
if (dictionary.TryGet(NameToken.FirstChar, pdfScanner, out firstCharacterToken))
{
firstChar = firstCharacterToken.Int;
}
if (dictionary.TryGet(NameToken.Widths, pdfScanner, out ArrayToken? widthsArray))
{
widthsOverride = widthsArray.Data.OfType<NumericToken>()
.Select(x => x.Double).ToArray();
}
return new TrueTypeStandard14FallbackSimpleFont(baseFont, standard14Font, thisEncoding, fileSystemFont,
new TrueTypeStandard14FallbackSimpleFont.MetricOverrides(firstChar, widthsOverride));
}
var firstCharacter = firstCharacterToken.Int;
var firstCharacter = firstChar ?? firstCharacterToken!.Int;
var widths = FontDictionaryAccessHelper.GetWidths(pdfScanner, dictionary);