mirror of
https://github.com/UglyToad/PdfPig.git
synced 2025-09-22 20:13:58 +08:00
#21 fix widths for system font baskerville old face
This commit is contained in:
@@ -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
|
||||
|
@@ -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);
|
||||
}
|
||||
}
|
@@ -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
|
||||
|
@@ -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);
|
||||
|
@@ -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>
|
||||
|
@@ -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
|
||||
|
Reference in New Issue
Block a user