2018-04-28 19:33:50 +01:00
|
|
|
|
namespace UglyToad.PdfPig.Fonts.CompactFontFormat
|
|
|
|
|
|
{
|
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;
|
2018-04-29 14:42:54 +01:00
|
|
|
|
using Dictionaries;
|
2018-04-28 19:33:50 +01:00
|
|
|
|
|
|
|
|
|
|
internal class CompactFontFormatIndividualFontParser
|
|
|
|
|
|
{
|
2018-04-29 14:42:54 +01:00
|
|
|
|
private readonly CompactFontFormatIndexReader indexReader;
|
|
|
|
|
|
private readonly CompactFontFormatTopLevelDictionaryReader topLevelDictionaryReader;
|
|
|
|
|
|
private readonly CompactFontFormatPrivateDictionaryReader privateDictionaryReader;
|
2018-04-28 19:33:50 +01:00
|
|
|
|
|
2018-04-29 14:42:54 +01:00
|
|
|
|
public CompactFontFormatIndividualFontParser(CompactFontFormatIndexReader indexReader,
|
|
|
|
|
|
CompactFontFormatTopLevelDictionaryReader topLevelDictionaryReader,
|
|
|
|
|
|
CompactFontFormatPrivateDictionaryReader privateDictionaryReader)
|
2018-04-28 19:33:50 +01:00
|
|
|
|
{
|
2018-04-29 14:42:54 +01:00
|
|
|
|
this.indexReader = indexReader;
|
|
|
|
|
|
this.topLevelDictionaryReader = topLevelDictionaryReader;
|
|
|
|
|
|
this.privateDictionaryReader = privateDictionaryReader;
|
2018-04-28 19:33:50 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2018-04-29 14:42:54 +01:00
|
|
|
|
public void Parse(CompactFontFormatData data, string name, byte[] topDictionaryIndex, string[] stringIndex)
|
2018-04-28 19:33:50 +01:00
|
|
|
|
{
|
2018-04-29 14:42:54 +01:00
|
|
|
|
var individualData = new CompactFontFormatData(topDictionaryIndex);
|
2018-04-28 19:33:50 +01:00
|
|
|
|
|
2018-11-16 23:32:18 +00:00
|
|
|
|
var topDictionary = topLevelDictionaryReader.Read(individualData, stringIndex);
|
2018-04-28 19:33:50 +01:00
|
|
|
|
|
2018-04-29 14:42:54 +01:00
|
|
|
|
var privateDictionary = new CompactFontFormatPrivateDictionary();
|
2018-04-28 19:33:50 +01:00
|
|
|
|
|
2018-11-16 23:32:18 +00:00
|
|
|
|
if (topDictionary.PrivateDictionarySizeAndOffset.Item2 >= 0)
|
2018-04-28 19:33:50 +01:00
|
|
|
|
{
|
2018-11-16 23:32:18 +00:00
|
|
|
|
data.Seek(topDictionary.PrivateDictionarySizeAndOffset.Item2);
|
2018-04-28 19:33:50 +01:00
|
|
|
|
|
2018-04-29 14:42:54 +01:00
|
|
|
|
privateDictionary = privateDictionaryReader.Read(data, 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-16 23:32:18 +00:00
|
|
|
|
data.Seek(topDictionary.CharStringsOffset);
|
2018-04-28 19:33:50 +01:00
|
|
|
|
|
2018-11-16 23:32:18 +00:00
|
|
|
|
var charStringIndex = indexReader.ReadDictionaryData(data);
|
2018-11-16 21:30:59 +00:00
|
|
|
|
|
2018-11-16 23:32:18 +00:00
|
|
|
|
object charset = null;
|
|
|
|
|
|
|
|
|
|
|
|
if (topDictionary.IsCidFont && topDictionary.CharSetOffset >= 0 && topDictionary.CharSetOffset <= 2)
|
|
|
|
|
|
{
|
|
|
|
|
|
switch (topDictionary.CharSetOffset)
|
2018-11-16 21:30:59 +00:00
|
|
|
|
{
|
2018-11-16 23:32:18 +00:00
|
|
|
|
case 0:
|
|
|
|
|
|
charset = CompactFontFormatIsoAdobeCharset.Value;
|
2018-11-16 21:30:59 +00:00
|
|
|
|
break;
|
2018-11-16 23:32:18 +00:00
|
|
|
|
case 1:
|
|
|
|
|
|
charset = CompactFontFormatExpertCharset.Value;
|
|
|
|
|
|
break;
|
|
|
|
|
|
case 2:
|
|
|
|
|
|
charset = CompactFontFormatExpertSubsetCharset.Value;
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
data.Seek(topDictionary.CharSetOffset);
|
|
|
|
|
|
|
|
|
|
|
|
var format = data.ReadCard8();
|
|
|
|
|
|
|
|
|
|
|
|
switch (format)
|
|
|
|
|
|
{
|
|
|
|
|
|
case 0:
|
|
|
|
|
|
{
|
|
|
|
|
|
var glyphToNamesAndStringId = new List<(int glyphId, int stringId, string name)>();
|
|
|
|
|
|
|
|
|
|
|
|
for (var glyphId = 1; glyphId < charStringIndex.Length; glyphId++)
|
|
|
|
|
|
{
|
|
|
|
|
|
var stringId = data.ReadSid();
|
|
|
|
|
|
glyphToNamesAndStringId.Add((glyphId, stringId, ReadString(stringId, stringIndex)));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
charset = new CompactFontFormatFormat0Charset(glyphToNamesAndStringId);
|
|
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
case 1:
|
|
|
|
|
|
case 2:
|
|
|
|
|
|
{
|
|
|
|
|
|
var glyphToNamesAndStringId = new List<(int glyphId, int stringId, string name)>();
|
|
|
|
|
|
|
|
|
|
|
|
for (var glyphId = 1; glyphId < charStringIndex.Length; glyphId++)
|
|
|
|
|
|
{
|
|
|
|
|
|
var firstSid = data.ReadSid();
|
|
|
|
|
|
var numberInRange = format == 1 ? data.ReadCard8() : data.ReadCard16();
|
|
|
|
|
|
|
|
|
|
|
|
glyphToNamesAndStringId.Add((glyphId, firstSid, ReadString(firstSid, stringIndex)));
|
|
|
|
|
|
glyphId++;
|
|
|
|
|
|
for (var i = 0; i < numberInRange; i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
glyphToNamesAndStringId.Add((glyphId, firstSid + i + 1, ReadString(firstSid, stringIndex)));
|
|
|
|
|
|
glyphId++;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (format == 1)
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
|
|
charset = new CompactFontFormatFormat1Charset(glyphToNamesAndStringId);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
charset = new CompactFontFormatFormat2Charset(glyphToNamesAndStringId);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
2018-11-16 21:30:59 +00:00
|
|
|
|
default:
|
2018-11-16 23:32:18 +00:00
|
|
|
|
throw new InvalidOperationException($"Unrecognized format for the Charset table in a CFF font. Got: {format}.");
|
2018-11-16 21:30:59 +00:00
|
|
|
|
}
|
2018-04-28 19:33:50 +01:00
|
|
|
|
}
|
2018-11-16 23:32:18 +00:00
|
|
|
|
|
|
|
|
|
|
data.Seek(topDictionary.CharStringsOffset);
|
|
|
|
|
|
|
|
|
|
|
|
Type2CharStrings charStrings;
|
|
|
|
|
|
switch (topDictionary.CharStringType)
|
|
|
|
|
|
{
|
|
|
|
|
|
case CompactFontFormatCharStringType.Type1:
|
|
|
|
|
|
throw new NotImplementedException();
|
|
|
|
|
|
case CompactFontFormatCharStringType.Type2:
|
|
|
|
|
|
charStrings = Type2CharStringParser.Parse(charStringIndex);
|
|
|
|
|
|
break;
|
|
|
|
|
|
default:
|
|
|
|
|
|
throw new ArgumentOutOfRangeException($"Unexpected CharString type in CFF font: {topDictionary.CharStringType}.");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private static string ReadString(int index, string[] stringIndex)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (index >= 0 && index <= 390)
|
|
|
|
|
|
{
|
|
|
|
|
|
return CompactFontFormatStandardStrings.GetName(index);
|
|
|
|
|
|
}
|
|
|
|
|
|
if (index - 391 < stringIndex.Length)
|
|
|
|
|
|
{
|
|
|
|
|
|
return stringIndex[index - 391];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// technically this maps to .notdef, but we need a unique sid name
|
|
|
|
|
|
return "SID" + index;
|
2018-04-28 19:33:50 +01:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|