mirror of
https://github.com/UglyToad/PdfPig.git
synced 2025-10-15 19:54:52 +08:00
add tests to visually verify the output for glpyh bounding boxes and use advance widths for empty glyphs in truetype fonts
This commit is contained in:
Binary file not shown.
After Width: | Height: | Size: 7.9 KiB |
Binary file not shown.
After Width: | Height: | Size: 11 KiB |
BIN
src/UglyToad.PdfPig.Tests/Integration/Documents/Type0 Font.jpg
Normal file
BIN
src/UglyToad.PdfPig.Tests/Integration/Documents/Type0 Font.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 96 KiB |
@@ -0,0 +1,87 @@
|
|||||||
|
namespace UglyToad.PdfPig.Tests.Integration.VisualVerification
|
||||||
|
{
|
||||||
|
using System;
|
||||||
|
using System.Drawing;
|
||||||
|
using System.IO;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
public class GenerateLetterBoundingBoxImages
|
||||||
|
{
|
||||||
|
private const string NonLatinAcrobatDistiller = "Single Page Non Latin - from acrobat distiller";
|
||||||
|
private const string SingleGoogleDrivePage = "Single Page Simple - from google drive";
|
||||||
|
private const string SinglePageFormattedType0Content = "Type0 Font";
|
||||||
|
|
||||||
|
private static string GetFilename(string name)
|
||||||
|
{
|
||||||
|
var documentFolder = Path.GetFullPath(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "..", "..", "..", "Integration", "Documents"));
|
||||||
|
|
||||||
|
if (!name.EndsWith(".pdf"))
|
||||||
|
{
|
||||||
|
name += ".pdf";
|
||||||
|
}
|
||||||
|
|
||||||
|
return Path.Combine(documentFolder, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void SinglePageNonLatinFromAcrobatDistiller()
|
||||||
|
{
|
||||||
|
Run(NonLatinAcrobatDistiller);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void SinglePageSimpleFromGoogleDrive()
|
||||||
|
{
|
||||||
|
Run(SingleGoogleDrivePage);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void SinglePageType0Font()
|
||||||
|
{
|
||||||
|
Run(SinglePageFormattedType0Content);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void Run(string file)
|
||||||
|
{
|
||||||
|
var pdfFileName = GetFilename(file);
|
||||||
|
|
||||||
|
using (var document = PdfDocument.Open(pdfFileName))
|
||||||
|
using (var image = GetCorrespondingImage(pdfFileName))
|
||||||
|
{
|
||||||
|
var page = document.GetPage(1);
|
||||||
|
|
||||||
|
var redPen = new Pen(Color.BlueViolet, 1);
|
||||||
|
|
||||||
|
using (var bitmap = new Bitmap(image))
|
||||||
|
using (var graphics = Graphics.FromImage(bitmap))
|
||||||
|
{
|
||||||
|
foreach (var word in page.Letters)
|
||||||
|
{
|
||||||
|
graphics.DrawRectangle(redPen, new Rectangle((int)word.Rectangle.Left,
|
||||||
|
792 - (int)word.Rectangle.Top, (int)word.Rectangle.Width, (int)word.Rectangle.Height));
|
||||||
|
}
|
||||||
|
|
||||||
|
var imageName = $"{file}.jpg";
|
||||||
|
|
||||||
|
if (!Directory.Exists("Images"))
|
||||||
|
{
|
||||||
|
Directory.CreateDirectory("Images");
|
||||||
|
}
|
||||||
|
|
||||||
|
var savePath = Path.Combine("Images", imageName);
|
||||||
|
|
||||||
|
bitmap.Save(savePath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Image GetCorrespondingImage(string filename)
|
||||||
|
{
|
||||||
|
var pdf = GetFilename(filename);
|
||||||
|
|
||||||
|
pdf = pdf.Replace(".pdf", ".jpg");
|
||||||
|
|
||||||
|
return Image.FromFile(pdf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -31,6 +31,7 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.5.0" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.5.0" />
|
||||||
|
<PackageReference Include="System.Drawing.Common" Version="4.5.0-preview2-26406-04" />
|
||||||
<PackageReference Include="xunit" Version="2.3.1" />
|
<PackageReference Include="xunit" Version="2.3.1" />
|
||||||
<PackageReference Include="xunit.extensibility.execution" Version="2.3.1" />
|
<PackageReference Include="xunit.extensibility.execution" Version="2.3.1" />
|
||||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.3.1" />
|
<PackageReference Include="xunit.runner.visualstudio" Version="2.3.1" />
|
||||||
|
@@ -49,7 +49,7 @@
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
{
|
{
|
||||||
return $"{Rectangle} {Value} {FontName} {PointSize}";
|
return $"{Value} {Rectangle} {FontName} {PointSize}";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -82,8 +82,6 @@
|
|||||||
|
|
||||||
var fromFont = CidFont.GetWidthFromDictionary(cid);
|
var fromFont = CidFont.GetWidthFromDictionary(cid);
|
||||||
|
|
||||||
var box = GetBoundingBox(characterCode);
|
|
||||||
|
|
||||||
return fromFont;
|
return fromFont;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -90,7 +90,13 @@ namespace UglyToad.PdfPig.Fonts.Simple
|
|||||||
|
|
||||||
public PdfRectangle GetBoundingBox(int characterCode)
|
public PdfRectangle GetBoundingBox(int characterCode)
|
||||||
{
|
{
|
||||||
return GetFontMatrix().Transform(GetBoundingBoxInGlyphSpace(characterCode));
|
var fontMatrix = GetFontMatrix();
|
||||||
|
|
||||||
|
var boundingBox = GetBoundingBoxInGlyphSpace(characterCode);
|
||||||
|
|
||||||
|
var result = fontMatrix.Transform(boundingBox);
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private PdfRectangle GetBoundingBoxInGlyphSpace(int characterCode)
|
private PdfRectangle GetBoundingBoxInGlyphSpace(int characterCode)
|
||||||
@@ -138,3 +144,4 @@ namespace UglyToad.PdfPig.Fonts.Simple
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -24,6 +24,8 @@
|
|||||||
|
|
||||||
public bool IsSimple { get; }
|
public bool IsSimple { get; }
|
||||||
|
|
||||||
|
public bool IsEmpty => Points.Length == 0;
|
||||||
|
|
||||||
public Glyph(bool isSimple, byte[] instructions, int[] endPointsOfContours, GlyphPoint[] points,
|
public Glyph(bool isSimple, byte[] instructions, int[] endPointsOfContours, GlyphPoint[] points,
|
||||||
PdfRectangle bounds)
|
PdfRectangle bounds)
|
||||||
{
|
{
|
||||||
|
@@ -14,6 +14,8 @@
|
|||||||
|
|
||||||
GlyphPoint[] Points { get; }
|
GlyphPoint[] Points { get; }
|
||||||
|
|
||||||
|
bool IsEmpty { get; }
|
||||||
|
|
||||||
IGlyphDescription DeepClone();
|
IGlyphDescription DeepClone();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -20,14 +20,9 @@
|
|||||||
|
|
||||||
public TrueTypeFont(decimal version, IReadOnlyDictionary<string, TrueTypeHeaderTable> tableHeaders, TableRegister tableRegister)
|
public TrueTypeFont(decimal version, IReadOnlyDictionary<string, TrueTypeHeaderTable> tableHeaders, TableRegister tableRegister)
|
||||||
{
|
{
|
||||||
if (tableRegister == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(tableRegister));
|
|
||||||
}
|
|
||||||
|
|
||||||
Version = version;
|
Version = version;
|
||||||
TableHeaders = tableHeaders;
|
TableHeaders = tableHeaders;
|
||||||
TableRegister = tableRegister;
|
TableRegister = tableRegister ?? throw new ArgumentNullException(nameof(tableRegister));
|
||||||
HeaderTable = tableRegister.HeaderTable;
|
HeaderTable = tableRegister.HeaderTable;
|
||||||
CMapTable = tableRegister.CMapTable;
|
CMapTable = tableRegister.CMapTable;
|
||||||
GlyphTable = tableRegister.GlyphDataTable;
|
GlyphTable = tableRegister.GlyphDataTable;
|
||||||
@@ -38,18 +33,7 @@
|
|||||||
{
|
{
|
||||||
boundingBox = default(PdfRectangle);
|
boundingBox = default(PdfRectangle);
|
||||||
|
|
||||||
int index;
|
if (!TryGetGlyphIndex(characterCode, characterIdentifierToGlyphIndex, out var index))
|
||||||
|
|
||||||
if (CMapTable == null)
|
|
||||||
{
|
|
||||||
if (characterIdentifierToGlyphIndex == null)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
index = characterIdentifierToGlyphIndex(characterCode);
|
|
||||||
}
|
|
||||||
else if (!CMapTable.TryGetGlyphIndex(characterCode, out index))
|
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -61,8 +45,15 @@
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
boundingBox = glyph.Bounds;
|
if (glyph.IsEmpty && TryGetBoundingAdvancedWidthByIndex(index, out var advanceWidth))
|
||||||
|
{
|
||||||
|
boundingBox = new PdfRectangle(0, 0, advanceWidth, 0);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
boundingBox = glyph.Bounds;
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,18 +61,39 @@
|
|||||||
{
|
{
|
||||||
width = 0m;
|
width = 0m;
|
||||||
|
|
||||||
|
if (!TryGetGlyphIndex(characterCode, null, out var index))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return TryGetBoundingAdvancedWidthByIndex(index, out width);
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool TryGetBoundingAdvancedWidthByIndex(int index, out decimal width)
|
||||||
|
{
|
||||||
|
width = TableRegister.HorizontalMetricsTable.GetAdvanceWidth(index);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool TryGetGlyphIndex(int characterIdentifier, Func<int, int> characterIdentifierToGlyphIndex, out int glyphIndex)
|
||||||
|
{
|
||||||
|
glyphIndex = 0;
|
||||||
|
|
||||||
if (CMapTable == null)
|
if (CMapTable == null)
|
||||||
{
|
{
|
||||||
return false;
|
if (characterIdentifierToGlyphIndex == null)
|
||||||
}
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (!CMapTable.TryGetGlyphIndex(characterCode, out var index))
|
glyphIndex = characterIdentifierToGlyphIndex(characterIdentifier);
|
||||||
|
}
|
||||||
|
else if (!CMapTable.TryGetGlyphIndex(characterIdentifier, out glyphIndex))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
width = TableRegister.HorizontalMetricsTable.GetAdvanceWidth(index);
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user