mirror of
https://github.com/UglyToad/PdfPig.git
synced 2025-09-23 20:53:39 +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)
|
private static void WriteFile(string name, byte[] bytes)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
namespace UglyToad.PdfPig.Writer
|
namespace UglyToad.PdfPig.Writer
|
||||||
{
|
{
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using Core;
|
||||||
using Geometry;
|
using Geometry;
|
||||||
using Tokens;
|
using Tokens;
|
||||||
|
|
||||||
@@ -14,6 +15,8 @@
|
|||||||
|
|
||||||
bool TryGetAdvanceWidth(char character, out decimal width);
|
bool TryGetAdvanceWidth(char character, out decimal width);
|
||||||
|
|
||||||
|
TransformationMatrix GetFontMatrix();
|
||||||
|
|
||||||
ObjectToken WriteFont(NameToken fontKeyName, Stream outputStream, BuilderContext context);
|
ObjectToken WriteFont(NameToken fontKeyName, Stream outputStream, BuilderContext context);
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -17,7 +17,7 @@
|
|||||||
|
|
||||||
internal class PdfDocumentBuilder
|
internal class PdfDocumentBuilder
|
||||||
{
|
{
|
||||||
private static readonly byte Break = (byte)'\n';
|
private const byte Break = (byte) '\n';
|
||||||
private static readonly TrueTypeFontParser Parser = new TrueTypeFontParser();
|
private static readonly TrueTypeFontParser Parser = new TrueTypeFontParser();
|
||||||
|
|
||||||
private readonly Dictionary<int, PdfPageBuilder> pages = new Dictionary<int, PdfPageBuilder>();
|
private readonly Dictionary<int, PdfPageBuilder> pages = new Dictionary<int, PdfPageBuilder>();
|
||||||
@@ -26,6 +26,53 @@
|
|||||||
public IReadOnlyDictionary<int, PdfPageBuilder> Pages => pages;
|
public IReadOnlyDictionary<int, PdfPageBuilder> Pages => pages;
|
||||||
public IReadOnlyDictionary<Guid, IWritingFont> Fonts => fonts.ToDictionary(x => x.Key, x => x.Value.FontProgram);
|
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)
|
public AddedFont AddTrueTypeFont(IReadOnlyList<byte> fontFileBytes)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
@@ -51,20 +51,14 @@
|
|||||||
throw new ArgumentOutOfRangeException(nameof(fontSize), "Font size must be greater than 0");
|
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 textMatrix = TransformationMatrix.FromValues(1, 0, 0, 1, position.X, position.Y);
|
||||||
|
|
||||||
var letters = DrawLetters(text, fontProgram, fm, fontSize, textMatrix);
|
var letters = DrawLetters(text, fontProgram, fm, fontSize, textMatrix);
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
//var realWidth = widthRect.Width;
|
|
||||||
|
|
||||||
//if (realWidth + position.X > PageSize.Width)
|
|
||||||
//{
|
|
||||||
// throw new InvalidOperationException("Text would exceed the bounds.");
|
|
||||||
//}
|
|
||||||
|
|
||||||
var beginText = BeginText.Value;
|
var beginText = BeginText.Value;
|
||||||
|
|
||||||
operations.Add(beginText);
|
operations.Add(beginText);
|
||||||
|
@@ -2,6 +2,7 @@
|
|||||||
{
|
{
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using Core;
|
||||||
using Fonts;
|
using Fonts;
|
||||||
using Fonts.Encodings;
|
using Fonts.Encodings;
|
||||||
using Geometry;
|
using Geometry;
|
||||||
@@ -48,6 +49,11 @@
|
|||||||
return true;
|
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)
|
public ObjectToken WriteFont(NameToken fontKeyName, Stream outputStream, BuilderContext context)
|
||||||
{
|
{
|
||||||
var dictionary = new Dictionary<NameToken, IToken>
|
var dictionary = new Dictionary<NameToken, IToken>
|
||||||
|
@@ -4,6 +4,7 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using Core;
|
||||||
using Fonts;
|
using Fonts;
|
||||||
using Fonts.Encodings;
|
using Fonts.Encodings;
|
||||||
using Fonts.Exceptions;
|
using Fonts.Exceptions;
|
||||||
@@ -37,6 +38,12 @@
|
|||||||
return font.TryGetBoundingAdvancedWidth(character, out width);
|
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)
|
public ObjectToken WriteFont(NameToken fontKeyName, Stream outputStream, BuilderContext context)
|
||||||
{
|
{
|
||||||
var bytes = fontFileBytes;
|
var bytes = fontFileBytes;
|
||||||
@@ -86,7 +93,7 @@
|
|||||||
|
|
||||||
descriptorDictionary[NameToken.StemV] = new NumericToken(bbox.Width * scaling * 0.13m);
|
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);
|
var widthsRef = context.WriteObject(outputStream, metrics.Widths);
|
||||||
|
|
||||||
@@ -131,7 +138,7 @@
|
|||||||
this.font = font ?? throw new ArgumentNullException(nameof(font));
|
this.font = font ?? throw new ArgumentNullException(nameof(font));
|
||||||
}
|
}
|
||||||
|
|
||||||
public FontDictionaryMetrics GetMetrics()
|
public FontDictionaryMetrics GetMetrics(decimal scaling)
|
||||||
{
|
{
|
||||||
var encoding = ReadFontEncoding();
|
var encoding = ReadFontEncoding();
|
||||||
var firstCharacter = encoding.CodeToNameMap.Keys.Min();
|
var firstCharacter = encoding.CodeToNameMap.Keys.Min();
|
||||||
@@ -151,12 +158,17 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
var characterCode = (int) unicode[0];
|
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
|
return new FontDictionaryMetrics
|
||||||
|
Reference in New Issue
Block a user