#21 fix widths for system font baskerville old face

This commit is contained in:
Eliot Jones
2018-12-11 21:29:39 +00:00
parent d1722dd23e
commit 29f9885fc4
6 changed files with 139 additions and 14 deletions

View File

@@ -134,6 +134,69 @@
}
}
[Fact]
public void WindowsOnlyCanWriteSinglePageHelloWorldSystemFont()
{
var builder = new PdfDocumentBuilder();
var page = builder.AddPage(PageSize.A4);
var file = @"C:\Windows\Fonts\BASKVILL.TTF";
if (!File.Exists(file))
{
return;
}
byte[] bytes;
try
{
bytes = File.ReadAllBytes(file);
}
catch
{
return;
}
var font = builder.AddTrueTypeFont(bytes);
var letters = page.AddText("Hello World!", 16, new PdfPoint(30, 520), font);
page.AddText("This is some further text continuing to write", 12, new PdfPoint(30, 500), font);
Assert.NotEmpty(page.Operations);
var b = builder.Build();
WriteFile(nameof(WindowsOnlyCanWriteSinglePageHelloWorldSystemFont), b);
Assert.NotEmpty(b);
using (var document = PdfDocument.Open(b))
{
var page1 = document.GetPage(1);
Assert.StartsWith("Hello World!", page1.Text);
var h = page1.Letters[0];
Assert.Equal("H", h.Value);
Assert.Equal("BaskOldFace", h.FontName);
for (int i = 0; i < letters.Count; i++)
{
var readerLetter = page1.Letters[i];
var writerLetter = letters[i];
Assert.Equal(readerLetter.Value, writerLetter.Value);
Assert.Equal(readerLetter.Location, writerLetter.Location);
Assert.Equal(readerLetter.FontSize, writerLetter.FontSize);
Assert.Equal(readerLetter.GlyphRectangle.Width, writerLetter.GlyphRectangle.Width);
Assert.Equal(readerLetter.GlyphRectangle.Height, writerLetter.GlyphRectangle.Height);
Assert.Equal(readerLetter.GlyphRectangle.BottomLeft, writerLetter.GlyphRectangle.BottomLeft);
}
}
}
private static void WriteFile(string name, byte[] bytes)
{
try

View File

@@ -1,6 +1,7 @@
namespace UglyToad.PdfPig.Writer
{
using System.IO;
using Core;
using Geometry;
using Tokens;
@@ -14,6 +15,8 @@
bool TryGetAdvanceWidth(char character, out decimal width);
TransformationMatrix GetFontMatrix();
ObjectToken WriteFont(NameToken fontKeyName, Stream outputStream, BuilderContext context);
}
}

View File

@@ -17,7 +17,7 @@
internal class PdfDocumentBuilder
{
private static readonly byte Break = (byte)'\n';
private const byte Break = (byte) '\n';
private static readonly TrueTypeFontParser Parser = new TrueTypeFontParser();
private readonly Dictionary<int, PdfPageBuilder> pages = new Dictionary<int, PdfPageBuilder>();
@@ -26,6 +26,53 @@
public IReadOnlyDictionary<int, PdfPageBuilder> Pages => pages;
public IReadOnlyDictionary<Guid, IWritingFont> Fonts => fonts.ToDictionary(x => x.Key, x => x.Value.FontProgram);
public bool CanUseTrueTypeFont(IReadOnlyList<byte> fontFileBytes, out IReadOnlyList<string> reasons)
{
var reasonsMutable = new List<string>();
reasons = reasonsMutable;
try
{
if (fontFileBytes == null)
{
reasonsMutable.Add("Provided bytes were null.");
return false;
}
if (fontFileBytes.Count == 0)
{
reasonsMutable.Add("Provided bytes were empty.");
return false;
}
var font = Parser.Parse(new TrueTypeDataBytes(new ByteArrayInputBytes(fontFileBytes)));
if (font.TableRegister.CMapTable == null)
{
reasonsMutable.Add("The provided font did not contain a cmap table, used to map character codes to glyph codes.");
return false;
}
if (font.TableRegister.Os2Table == null)
{
reasonsMutable.Add("The provided font did not contain an OS/2 table, used to fill in the font descriptor dictionary.");
return false;
}
if (font.TableRegister.PostScriptTable == null)
{
reasonsMutable.Add("The provided font did not contain a post PostScript table, used to map character codes to glyph codes.");
return false;
}
return true;
}
catch (Exception ex)
{
reasonsMutable.Add(ex.Message);
return false;
}
}
public AddedFont AddTrueTypeFont(IReadOnlyList<byte> fontFileBytes)
{
try

View File

@@ -51,20 +51,14 @@
throw new ArgumentOutOfRangeException(nameof(fontSize), "Font size must be greater than 0");
}
var fm = TransformationMatrix.FromValues(1 / 1000m, 0, 0, 1 / 1000m, 0, 0);
var fm = fontProgram.GetFontMatrix();
var textMatrix = TransformationMatrix.FromValues(1, 0, 0, 1, position.X, position.Y);
var letters = DrawLetters(text, fontProgram, fm, fontSize, textMatrix);
try
{
//var realWidth = widthRect.Width;
//if (realWidth + position.X > PageSize.Width)
//{
// throw new InvalidOperationException("Text would exceed the bounds.");
//}
var beginText = BeginText.Value;
operations.Add(beginText);

View File

@@ -2,6 +2,7 @@
{
using System.Collections.Generic;
using System.IO;
using Core;
using Fonts;
using Fonts.Encodings;
using Geometry;
@@ -48,6 +49,11 @@
return true;
}
public TransformationMatrix GetFontMatrix()
{
return TransformationMatrix.FromValues(1/1000m, 0, 0, 1/1000m, 0, 0);
}
public ObjectToken WriteFont(NameToken fontKeyName, Stream outputStream, BuilderContext context)
{
var dictionary = new Dictionary<NameToken, IToken>

View File

@@ -4,6 +4,7 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Core;
using Fonts;
using Fonts.Encodings;
using Fonts.Exceptions;
@@ -37,6 +38,12 @@
return font.TryGetBoundingAdvancedWidth(character, out width);
}
public TransformationMatrix GetFontMatrix()
{
var unitsPerEm = font.GetFontMatrixMultiplier();
return TransformationMatrix.FromValues(1m/unitsPerEm, 0, 0, 1m/unitsPerEm, 0, 0);
}
public ObjectToken WriteFont(NameToken fontKeyName, Stream outputStream, BuilderContext context)
{
var bytes = fontFileBytes;
@@ -86,7 +93,7 @@
descriptorDictionary[NameToken.StemV] = new NumericToken(bbox.Width * scaling * 0.13m);
var metrics = charCodeToGlyphId.GetMetrics();
var metrics = charCodeToGlyphId.GetMetrics(scaling);
var widthsRef = context.WriteObject(outputStream, metrics.Widths);
@@ -131,7 +138,7 @@
this.font = font ?? throw new ArgumentNullException(nameof(font));
}
public FontDictionaryMetrics GetMetrics()
public FontDictionaryMetrics GetMetrics(decimal scaling)
{
var encoding = ReadFontEncoding();
var firstCharacter = encoding.CodeToNameMap.Keys.Min();
@@ -151,12 +158,17 @@
}
var characterCode = (int) unicode[0];
if (!font.TryGetBoundingAdvancedWidth(characterCode, out var width))
if (characterCode < firstCharacter || characterCode > lastCharacter)
{
throw new InvalidFontFormatException();
continue;
}
widths[pair.Key - firstCharacter] = new NumericToken(width);
if (!font.TryGetBoundingAdvancedWidth(characterCode, out var width))
{
throw new InvalidFontFormatException($"Could not find advanced with for character named '{pair.Value}' with character code: {characterCode}.");
}
widths[pair.Key - firstCharacter] = new NumericToken(width * scaling);
}
return new FontDictionaryMetrics