2018-01-10 19:49:32 +00:00
|
|
|
|
namespace UglyToad.PdfPig.Fonts.Parser.Parts
|
2017-12-03 15:01:17 +00:00
|
|
|
|
{
|
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using CidFonts;
|
2017-12-26 19:18:19 +00:00
|
|
|
|
using Exceptions;
|
2017-12-03 15:01:17 +00:00
|
|
|
|
using Filters;
|
|
|
|
|
using Geometry;
|
2017-12-03 21:31:59 +00:00
|
|
|
|
using IO;
|
2018-01-10 19:49:32 +00:00
|
|
|
|
using PdfPig.Parser.Parts;
|
2018-01-19 00:35:04 +00:00
|
|
|
|
using Tokenization.Scanner;
|
|
|
|
|
using Tokenization.Tokens;
|
2017-12-03 21:31:59 +00:00
|
|
|
|
using TrueType;
|
|
|
|
|
using TrueType.Parser;
|
2018-01-19 00:35:04 +00:00
|
|
|
|
using Util;
|
2017-12-03 15:01:17 +00:00
|
|
|
|
|
|
|
|
|
internal class CidFontFactory
|
|
|
|
|
{
|
|
|
|
|
private readonly FontDescriptorFactory descriptorFactory;
|
2017-12-03 21:31:59 +00:00
|
|
|
|
private readonly TrueTypeFontParser trueTypeFontParser;
|
2017-12-22 23:54:54 +00:00
|
|
|
|
private readonly IFilterProvider filterProvider;
|
2018-01-20 18:42:29 +00:00
|
|
|
|
private readonly IPdfTokenScanner pdfScanner;
|
2017-12-03 15:01:17 +00:00
|
|
|
|
|
2018-01-20 18:42:29 +00:00
|
|
|
|
public CidFontFactory(IPdfTokenScanner pdfScanner, FontDescriptorFactory descriptorFactory, TrueTypeFontParser trueTypeFontParser,
|
2018-01-20 00:49:53 +00:00
|
|
|
|
IFilterProvider filterProvider)
|
2017-12-03 15:01:17 +00:00
|
|
|
|
{
|
|
|
|
|
this.descriptorFactory = descriptorFactory;
|
2017-12-03 21:31:59 +00:00
|
|
|
|
this.trueTypeFontParser = trueTypeFontParser;
|
2017-12-22 23:54:54 +00:00
|
|
|
|
this.filterProvider = filterProvider;
|
2018-01-19 00:35:04 +00:00
|
|
|
|
this.pdfScanner = pdfScanner;
|
2017-12-03 15:01:17 +00:00
|
|
|
|
}
|
|
|
|
|
|
2018-01-20 00:49:53 +00:00
|
|
|
|
public ICidFont Generate(DictionaryToken dictionary, bool isLenientParsing)
|
2017-12-03 15:01:17 +00:00
|
|
|
|
{
|
2018-01-19 00:35:04 +00:00
|
|
|
|
var type = dictionary.GetNameOrDefault(NameToken.Type);
|
|
|
|
|
if (!NameToken.Font.Equals(type))
|
2017-12-03 15:01:17 +00:00
|
|
|
|
{
|
2018-01-19 00:35:04 +00:00
|
|
|
|
throw new InvalidFontFormatException($"Expected \'Font\' dictionary but found \'{type}\'");
|
2017-12-03 15:01:17 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var widths = ReadWidths(dictionary);
|
|
|
|
|
var verticalWritingMetrics = ReadVerticalDisplacements(dictionary);
|
|
|
|
|
|
|
|
|
|
FontDescriptor descriptor = null;
|
2018-01-19 00:35:04 +00:00
|
|
|
|
if (TryGetFontDescriptor(dictionary, out var descriptorDictionary))
|
2017-12-03 15:01:17 +00:00
|
|
|
|
{
|
2017-12-22 23:54:54 +00:00
|
|
|
|
descriptor = descriptorFactory.Generate(descriptorDictionary, isLenientParsing);
|
2017-12-03 15:01:17 +00:00
|
|
|
|
}
|
|
|
|
|
|
2018-01-20 00:49:53 +00:00
|
|
|
|
var fontProgram = ReadDescriptorFile(descriptor);
|
2017-12-26 19:18:19 +00:00
|
|
|
|
|
2018-01-19 00:35:04 +00:00
|
|
|
|
var baseFont = dictionary.GetNameOrDefault(NameToken.BaseFont);
|
2017-12-26 19:18:19 +00:00
|
|
|
|
|
2018-01-20 00:49:53 +00:00
|
|
|
|
var systemInfo = GetSystemInfo(dictionary);
|
2017-12-03 15:01:17 +00:00
|
|
|
|
|
2018-01-19 00:35:04 +00:00
|
|
|
|
var subType = dictionary.GetNameOrDefault(NameToken.Subtype);
|
|
|
|
|
if (NameToken.CidFontType0.Equals(subType))
|
2017-12-03 15:01:17 +00:00
|
|
|
|
{
|
|
|
|
|
//return new PDCIDFontType0(dictionary, parent);
|
|
|
|
|
}
|
|
|
|
|
|
2018-01-19 00:35:04 +00:00
|
|
|
|
if (NameToken.CidFontType2.Equals(subType))
|
2017-12-03 15:01:17 +00:00
|
|
|
|
{
|
2017-12-26 19:18:19 +00:00
|
|
|
|
return new Type2CidFont(type, subType, baseFont, systemInfo, descriptor, fontProgram, verticalWritingMetrics, widths);
|
2017-12-03 15:01:17 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
2018-01-19 00:35:04 +00:00
|
|
|
|
private bool TryGetFontDescriptor(DictionaryToken dictionary, out DictionaryToken descriptorDictionary)
|
2017-12-03 15:01:17 +00:00
|
|
|
|
{
|
|
|
|
|
descriptorDictionary = null;
|
|
|
|
|
|
2018-01-19 00:35:04 +00:00
|
|
|
|
if (!dictionary.TryGet(NameToken.FontDesc, out var baseValue))
|
2017-12-03 15:01:17 +00:00
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2018-01-19 00:35:04 +00:00
|
|
|
|
var descriptor = DirectObjectFinder.Get<DictionaryToken>(baseValue, pdfScanner);
|
|
|
|
|
|
2017-12-03 15:01:17 +00:00
|
|
|
|
descriptorDictionary = descriptor;
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2018-01-20 00:49:53 +00:00
|
|
|
|
private ICidFontProgram ReadDescriptorFile(FontDescriptor descriptor)
|
2017-12-03 15:01:17 +00:00
|
|
|
|
{
|
|
|
|
|
if (descriptor?.FontFile == null)
|
|
|
|
|
{
|
2017-12-26 19:18:19 +00:00
|
|
|
|
return null;
|
2017-12-03 15:01:17 +00:00
|
|
|
|
}
|
|
|
|
|
|
2018-01-20 00:49:53 +00:00
|
|
|
|
var fontFileStream = DirectObjectFinder.Get<StreamToken>(descriptor.FontFile.ObjectKey, pdfScanner);
|
2017-12-03 15:01:17 +00:00
|
|
|
|
|
|
|
|
|
if (fontFileStream == null)
|
|
|
|
|
{
|
2017-12-26 19:18:19 +00:00
|
|
|
|
return null;
|
2017-12-03 15:01:17 +00:00
|
|
|
|
}
|
|
|
|
|
|
2017-12-22 23:54:54 +00:00
|
|
|
|
var fontFile = fontFileStream.Decode(filterProvider);
|
2017-12-04 07:46:43 +00:00
|
|
|
|
|
2017-12-03 21:31:59 +00:00
|
|
|
|
switch (descriptor.FontFile.FileType)
|
|
|
|
|
{
|
|
|
|
|
case DescriptorFontFile.FontFileType.TrueType:
|
|
|
|
|
var input = new TrueTypeDataBytes(new ByteArrayInputBytes(fontFile));
|
2017-12-26 19:18:19 +00:00
|
|
|
|
return trueTypeFontParser.Parse(input);
|
2017-12-03 21:31:59 +00:00
|
|
|
|
default:
|
|
|
|
|
throw new NotSupportedException("Currently only TrueType fonts are supported.");
|
|
|
|
|
}
|
2017-12-03 15:01:17 +00:00
|
|
|
|
}
|
|
|
|
|
|
2018-01-19 00:35:04 +00:00
|
|
|
|
private static IReadOnlyDictionary<int, decimal> ReadWidths(DictionaryToken dict)
|
2017-12-03 15:01:17 +00:00
|
|
|
|
{
|
|
|
|
|
var widths = new Dictionary<int, decimal>();
|
|
|
|
|
|
2018-01-19 00:35:04 +00:00
|
|
|
|
if (!dict.TryGet(NameToken.W, out var widthsItem) || !(widthsItem is ArrayToken widthArray))
|
2017-12-03 15:01:17 +00:00
|
|
|
|
{
|
|
|
|
|
return widths;
|
|
|
|
|
}
|
|
|
|
|
|
2018-01-19 00:35:04 +00:00
|
|
|
|
int size = widthArray.Data.Count;
|
2017-12-03 15:01:17 +00:00
|
|
|
|
int counter = 0;
|
|
|
|
|
while (counter < size)
|
|
|
|
|
{
|
2018-01-19 00:35:04 +00:00
|
|
|
|
var firstCode = (NumericToken)widthArray.Data[counter++];
|
|
|
|
|
var next = widthArray.Data[counter++];
|
|
|
|
|
if (next is ArrayToken array)
|
2017-12-03 15:01:17 +00:00
|
|
|
|
{
|
2018-01-19 00:35:04 +00:00
|
|
|
|
int startRange = firstCode.Int;
|
|
|
|
|
int arraySize = array.Data.Count;
|
|
|
|
|
|
2017-12-03 15:01:17 +00:00
|
|
|
|
for (int i = 0; i < arraySize; i++)
|
|
|
|
|
{
|
2018-01-19 00:35:04 +00:00
|
|
|
|
var width = (NumericToken)array.Data[i];
|
|
|
|
|
widths[startRange + i] = width.Data;
|
2017-12-03 15:01:17 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2018-01-19 00:35:04 +00:00
|
|
|
|
var secondCode = (NumericToken)next;
|
|
|
|
|
var rangeWidth = (NumericToken)widthArray.Data[counter++];
|
|
|
|
|
int startRange = firstCode.Int;
|
|
|
|
|
int endRange = secondCode.Int;
|
|
|
|
|
var width = rangeWidth.Data;
|
2017-12-03 15:01:17 +00:00
|
|
|
|
for (var i = startRange; i <= endRange; i++)
|
|
|
|
|
{
|
|
|
|
|
widths[i] = width;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return widths;
|
|
|
|
|
}
|
|
|
|
|
|
2018-01-19 00:35:04 +00:00
|
|
|
|
private VerticalWritingMetrics ReadVerticalDisplacements(DictionaryToken dict)
|
2017-12-03 15:01:17 +00:00
|
|
|
|
{
|
|
|
|
|
var verticalDisplacements = new Dictionary<int, decimal>();
|
|
|
|
|
var positionVectors = new Dictionary<int, PdfVector>();
|
|
|
|
|
|
|
|
|
|
VerticalVectorComponents dw2;
|
2018-01-19 00:35:04 +00:00
|
|
|
|
if (!dict.TryGet(NameToken.Dw2, out var dw2Token) || !(dw2Token is ArrayToken arrayVerticalComponents))
|
2017-12-03 15:01:17 +00:00
|
|
|
|
{
|
|
|
|
|
dw2 = new VerticalVectorComponents(880, -1000);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2018-01-19 00:35:04 +00:00
|
|
|
|
var position = ((NumericToken)arrayVerticalComponents.Data[0]).Data;
|
|
|
|
|
var displacement = ((NumericToken)arrayVerticalComponents.Data[1]).Data;
|
2017-12-03 15:01:17 +00:00
|
|
|
|
|
|
|
|
|
dw2 = new VerticalVectorComponents(position, displacement);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// vertical metrics for individual CIDs.
|
2018-01-19 00:35:04 +00:00
|
|
|
|
if (dict.TryGet(NameToken.W2, out var w2Token) && w2Token is ArrayToken w2)
|
2017-12-03 15:01:17 +00:00
|
|
|
|
{
|
2018-01-19 00:35:04 +00:00
|
|
|
|
for (var i = 0; i < w2.Data.Count; i++)
|
2017-12-03 15:01:17 +00:00
|
|
|
|
{
|
2018-01-19 00:35:04 +00:00
|
|
|
|
var c = (NumericToken)w2.Data[i];
|
|
|
|
|
var next = w2.Data[++i];
|
|
|
|
|
|
|
|
|
|
if (next is ArrayToken array)
|
2017-12-03 15:01:17 +00:00
|
|
|
|
{
|
2018-01-19 00:35:04 +00:00
|
|
|
|
for (int j = 0; j < array.Data.Count; j++)
|
2017-12-03 15:01:17 +00:00
|
|
|
|
{
|
2018-01-19 00:35:04 +00:00
|
|
|
|
int cid = c.Int + j;
|
|
|
|
|
var w1y = (NumericToken)array.Data[j];
|
|
|
|
|
var v1x = (NumericToken)array.Data[++j];
|
|
|
|
|
var v1y = (NumericToken)array.Data[++j];
|
2017-12-03 15:01:17 +00:00
|
|
|
|
|
2018-01-19 00:35:04 +00:00
|
|
|
|
verticalDisplacements[cid] = w1y.Data;
|
2017-12-03 15:01:17 +00:00
|
|
|
|
|
2018-01-19 00:35:04 +00:00
|
|
|
|
positionVectors[cid] = new PdfVector(v1x.Data, v1y.Data);
|
2017-12-03 15:01:17 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2018-01-19 00:35:04 +00:00
|
|
|
|
int first = c.Int;
|
|
|
|
|
int last = ((NumericToken)next).Int;
|
|
|
|
|
var w1y = (NumericToken)w2.Data[++i];
|
|
|
|
|
var v1x = (NumericToken)w2.Data[++i];
|
|
|
|
|
var v1y = (NumericToken)w2.Data[++i];
|
2017-12-03 15:01:17 +00:00
|
|
|
|
|
|
|
|
|
for (var cid = first; cid <= last; cid++)
|
|
|
|
|
{
|
2018-01-19 00:35:04 +00:00
|
|
|
|
verticalDisplacements[cid] = w1y.Data;
|
2017-12-03 15:01:17 +00:00
|
|
|
|
|
2018-01-19 00:35:04 +00:00
|
|
|
|
positionVectors[cid] = new PdfVector(v1x.Data, v1y.Data);
|
2017-12-03 15:01:17 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return new VerticalWritingMetrics(dw2, verticalDisplacements, positionVectors);
|
|
|
|
|
}
|
2017-12-26 19:18:19 +00:00
|
|
|
|
|
2018-01-20 00:49:53 +00:00
|
|
|
|
private CharacterIdentifierSystemInfo GetSystemInfo(DictionaryToken dictionary)
|
2017-12-26 19:18:19 +00:00
|
|
|
|
{
|
2018-01-19 00:35:04 +00:00
|
|
|
|
if(!dictionary.TryGet(NameToken.CidSystemInfo, out var cidEntry))
|
2017-12-26 19:18:19 +00:00
|
|
|
|
{
|
2018-01-05 23:08:20 +00:00
|
|
|
|
throw new InvalidFontFormatException($"No CID System Info was found in the CID Font dictionary: {dictionary}");
|
|
|
|
|
}
|
|
|
|
|
|
2018-01-19 00:35:04 +00:00
|
|
|
|
if (cidEntry is DictionaryToken cidDictionary)
|
2018-01-05 23:08:20 +00:00
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2018-01-19 00:35:04 +00:00
|
|
|
|
cidDictionary =
|
|
|
|
|
DirectObjectFinder.Get<DictionaryToken>(cidEntry, pdfScanner);
|
2017-12-26 19:18:19 +00:00
|
|
|
|
}
|
|
|
|
|
|
2018-01-20 00:49:53 +00:00
|
|
|
|
var registry = SafeKeyAccess(cidDictionary, NameToken.Registry);
|
|
|
|
|
var ordering = SafeKeyAccess(cidDictionary, NameToken.Ordering);
|
2018-01-19 00:35:04 +00:00
|
|
|
|
var supplement = cidDictionary.GetIntOrDefault(NameToken.Supplement);
|
2017-12-26 19:18:19 +00:00
|
|
|
|
|
2018-01-19 00:35:04 +00:00
|
|
|
|
return new CharacterIdentifierSystemInfo(registry, ordering, supplement);
|
2017-12-26 19:18:19 +00:00
|
|
|
|
}
|
2018-01-10 19:23:10 +00:00
|
|
|
|
|
2018-01-20 00:49:53 +00:00
|
|
|
|
private string SafeKeyAccess(DictionaryToken dictionary, NameToken keyName)
|
2018-01-10 19:23:10 +00:00
|
|
|
|
{
|
2018-01-19 00:35:04 +00:00
|
|
|
|
if (!dictionary.TryGet(keyName, out var token))
|
|
|
|
|
{
|
|
|
|
|
return string.Empty;
|
|
|
|
|
}
|
2018-01-10 19:23:10 +00:00
|
|
|
|
|
2018-01-19 00:35:04 +00:00
|
|
|
|
if (token is StringToken str)
|
2018-01-10 19:23:10 +00:00
|
|
|
|
{
|
2018-01-19 00:35:04 +00:00
|
|
|
|
return str.Data;
|
2018-01-10 19:23:10 +00:00
|
|
|
|
}
|
|
|
|
|
|
2018-01-19 00:35:04 +00:00
|
|
|
|
if (token is HexToken hex)
|
2018-01-10 19:23:10 +00:00
|
|
|
|
{
|
2018-01-19 00:35:04 +00:00
|
|
|
|
return hex.Data;
|
2018-01-10 19:23:10 +00:00
|
|
|
|
}
|
|
|
|
|
|
2018-01-19 00:35:04 +00:00
|
|
|
|
if (token is IndirectReferenceToken obj)
|
2018-01-10 19:23:10 +00:00
|
|
|
|
{
|
2018-01-19 00:35:04 +00:00
|
|
|
|
return DirectObjectFinder.Get<StringToken>(obj, pdfScanner).Data;
|
2018-01-10 19:23:10 +00:00
|
|
|
|
}
|
|
|
|
|
|
2018-01-19 00:35:04 +00:00
|
|
|
|
return string.Empty;
|
2018-01-10 19:23:10 +00:00
|
|
|
|
}
|
2017-12-03 15:01:17 +00:00
|
|
|
|
}
|
|
|
|
|
}
|