add very basic handler for simple truetype fonts

This commit is contained in:
Eliot Jones
2017-12-31 15:30:47 +00:00
parent d668c4e892
commit 8f18a55c22
7 changed files with 240 additions and 11 deletions

View File

@@ -30,9 +30,9 @@
{
using (var document = PdfDocument.Open(GetFilename()))
{
//var page = document.GetPage(1);
var page = document.GetPage(1);
//Assert.Equal(PageSize.A4, page.Size);
Assert.Equal(PageSize.A4, page.Size);
}
}
}

View File

@@ -36,7 +36,9 @@
// Seems there is some disagreement 1190/1191
{new WidthHeight(842, 1190), PageSize.A3},
{new WidthHeight(842, 1191), PageSize.A3},
// Possibly some kind of rounding mix-up here
{new WidthHeight(595, 842), PageSize.A4},
{new WidthHeight(595, 841), PageSize.A4},
{new WidthHeight(420, 595), PageSize.A5},
{new WidthHeight(298, 420), PageSize.A6},
{new WidthHeight(210, 298), PageSize.A7},

View File

@@ -14,12 +14,13 @@
private readonly ILog log;
private readonly IReadOnlyDictionary<CosName, IFontHandler> handlers;
public FontFactory(ILog log, Type0FontHandler type0FontHandler)
public FontFactory(ILog log, Type0FontHandler type0FontHandler, TrueTypeFontHandler trueTypeFontHandler)
{
this.log = log;
handlers = new Dictionary<CosName, IFontHandler>
{
{CosName.TYPE0, type0FontHandler}
{CosName.TYPE0, type0FontHandler},
{CosName.TRUE_TYPE, trueTypeFontHandler}
};
}

View File

@@ -1,6 +1,160 @@
namespace UglyToad.Pdf.Fonts.Parser.Handlers
{
internal class TrueTypeFontHandler
using System.Linq;
using Cmap;
using ContentStream;
using Cos;
using Exceptions;
using Filters;
using IO;
using Parts;
using Pdf.Parser;
using Simple;
using TrueType;
using TrueType.Parser;
internal class TrueTypeFontHandler : IFontHandler
{
private readonly IPdfObjectParser pdfObjectParser;
private readonly IFilterProvider filterProvider;
private readonly CMapCache cMapCache;
private readonly FontDescriptorFactory fontDescriptorFactory;
private readonly TrueTypeFontParser trueTypeFontParser;
public TrueTypeFontHandler(IPdfObjectParser pdfObjectParser, IFilterProvider filterProvider,
CMapCache cMapCache,
FontDescriptorFactory fontDescriptorFactory,
TrueTypeFontParser trueTypeFontParser)
{
this.pdfObjectParser = pdfObjectParser;
this.filterProvider = filterProvider;
this.cMapCache = cMapCache;
this.fontDescriptorFactory = fontDescriptorFactory;
this.trueTypeFontParser = trueTypeFontParser;
}
public IFont Generate(PdfDictionary dictionary, IRandomAccessRead reader, bool isLenientParsing)
{
var firstCharacter = GetFirstCharacter(dictionary);
var lastCharacter = GetLastCharacter(dictionary);
var widths = GetWidths(dictionary);
var descriptor = GetFontDescriptor(dictionary, reader, isLenientParsing);
var font = ParseTrueTypeFont(descriptor, reader, isLenientParsing);
var name = GetName(dictionary, descriptor);
CMap toUnicodeCMap = null;
if (dictionary.TryGetItemOfType(CosName.TO_UNICODE, out CosObject toUnicodeObj))
{
var toUnicode = pdfObjectParser.Parse(toUnicodeObj.ToIndirectReference(), reader, isLenientParsing) as PdfRawStream;
var decodedUnicodeCMap = toUnicode?.Decode(filterProvider);
if (decodedUnicodeCMap != null)
{
toUnicodeCMap = cMapCache.Parse(new ByteArrayInputBytes(decodedUnicodeCMap), isLenientParsing);
}
}
return new TrueTypeSimpleFont(name, firstCharacter, lastCharacter, widths, descriptor, toUnicodeCMap);
}
private static int GetFirstCharacter(PdfDictionary dictionary)
{
if (!dictionary.TryGetItemOfType(CosName.FIRST_CHAR, out CosInt firstChar))
{
throw new InvalidFontFormatException(
$"No first character entry was found in the font dictionary for this TrueType font: {dictionary}.");
}
return firstChar.AsInt();
}
private static int GetLastCharacter(PdfDictionary dictionary)
{
if (!dictionary.TryGetItemOfType(CosName.LAST_CHAR, out CosInt lastChar))
{
throw new InvalidFontFormatException(
$"No last character entry was found in the font dictionary for this TrueType font: {dictionary}.");
}
return lastChar.AsInt();
}
private static decimal[] GetWidths(PdfDictionary dictionary)
{
if (!dictionary.TryGetItemOfType(CosName.WIDTHS, out COSArray widthArray))
{
throw new InvalidFontFormatException($"No widths array was found in the font dictionary for this TrueType font: {dictionary}.");
}
return widthArray.Select(x => ((ICosNumber)x).AsDecimal()).ToArray();
}
private FontDescriptor GetFontDescriptor(PdfDictionary dictionary, IRandomAccessRead reader, bool isLenientParsing)
{
if (!dictionary.TryGetItemOfType(CosName.FONT_DESC, out CosObject obj))
{
throw new InvalidFontFormatException($"No font descriptor indirect reference found in the TrueType font: {dictionary}.");
}
var parsed = pdfObjectParser.Parse(obj.ToIndirectReference(), reader, isLenientParsing);
if (!(parsed is PdfDictionary descriptorDictionary))
{
throw new InvalidFontFormatException($"Expected a font descriptor dictionary but instead found {parsed}.");
}
var descriptor = fontDescriptorFactory.Generate(descriptorDictionary, isLenientParsing);
return descriptor;
}
private TrueTypeFont ParseTrueTypeFont(FontDescriptor descriptor, IRandomAccessRead reader,
bool isLenientParsing)
{
if (descriptor?.FontFile == null)
{
return null;
}
if (descriptor.FontFile.FileType != DescriptorFontFile.FontFileType.TrueType)
{
throw new InvalidFontFormatException(
$"Expected a TrueType font in the TrueType font descriptor, instead it was {descriptor.FontFile.FileType}.");
}
var fontFileStream = pdfObjectParser.Parse(descriptor.FontFile.ObjectKey, reader, isLenientParsing) as PdfRawStream;
if (fontFileStream == null)
{
return null;
}
var fontFile = fontFileStream.Decode(filterProvider);
var font = trueTypeFontParser.Parse(new TrueTypeDataBytes(new ByteArrayInputBytes(fontFile)));
return font;
}
private static CosName GetName(PdfDictionary dictionary, FontDescriptor descriptor)
{
if (dictionary.TryGetName(CosName.BASE_FONT, out CosName name))
{
return name;
}
if (descriptor.FontName != null)
{
return descriptor.FontName;
}
throw new InvalidFontFormatException($"Could not find a name for this TrueType font {dictionary}.");
}
}
}

View File

@@ -0,0 +1,75 @@
namespace UglyToad.Pdf.Fonts.Simple
{
using Cmap;
using Composite;
using Cos;
using Geometry;
using IO;
using Util.JetBrains.Annotations;
internal class TrueTypeSimpleFont : IFont
{
private readonly int firstCharacterCode;
private readonly int lastCharacterCode;
private readonly decimal[] widths;
private readonly FontDescriptor descriptor;
public CosName Name { get; }
public bool IsVertical { get; }
[NotNull]
public ToUnicodeCMap ToUnicode { get; set; }
public TrueTypeSimpleFont(CosName name, int firstCharacterCode, int lastCharacterCode, decimal[] widths,
FontDescriptor descriptor,
[CanBeNull]CMap toUnicodeCMap)
{
this.firstCharacterCode = firstCharacterCode;
this.lastCharacterCode = lastCharacterCode;
this.widths = widths;
this.descriptor = descriptor;
Name = name;
IsVertical = false;
ToUnicode = new ToUnicodeCMap(toUnicodeCMap);
}
public int ReadCharacterCode(IInputBytes bytes, out int codeLength)
{
codeLength = 1;
return bytes.CurrentByte;
}
public bool TryGetUnicode(int characterCode, out string value)
{
value = null;
if (!ToUnicode.CanMapToUnicode)
{
return false;
}
return ToUnicode.TryGet(characterCode, out value);
}
public PdfVector GetDisplacement(int characterCode)
{
var tx = GetWidth(characterCode);
return new PdfVector(tx / 1000m, 0);
}
public decimal GetWidth(int characterCode)
{
var index = characterCode - firstCharacterCode;
if (index < 0 || index >= widths.Length)
{
return descriptor.MissingWidth;
}
return widths[index];
}
}
}

View File

@@ -68,10 +68,11 @@
var cMapCache = new CMapCache(new CMapParser());
var fontFactory = new FontFactory(container.Get<ILog>(), new Type0FontHandler(cidFontFactory,
var fontFactory = new FontFactory(log, new Type0FontHandler(cidFontFactory,
cMapCache,
filterProvider,
pdfObjectParser));
pdfObjectParser),
new TrueTypeFontHandler(pdfObjectParser, filterProvider, cMapCache, fontDescriptorFactory, trueTypeFontParser));
var dynamicParser = container.Get<DynamicParser>();
var resourceContainer = new ResourceContainer(pdfObjectParser, fontFactory);

View File

@@ -227,9 +227,5 @@
<EmbeddedResource Include="Resources\CMap\UniKS-UTF16-V" />
<EmbeddedResource Include="Resources\CMap\V" />
</ItemGroup>
<ItemGroup>
<Folder Include="Fonts\Simple\" />
</ItemGroup>
</Project>