diff --git a/src/UglyToad.PdfPig.Tests/Integration/Documents/Single Page Non Latin - from acrobat distiller.jpg b/src/UglyToad.PdfPig.Tests/Integration/Documents/Single Page Non Latin - from acrobat distiller.jpg new file mode 100644 index 00000000..30c7c734 Binary files /dev/null and b/src/UglyToad.PdfPig.Tests/Integration/Documents/Single Page Non Latin - from acrobat distiller.jpg differ diff --git a/src/UglyToad.PdfPig.Tests/Integration/Documents/Single Page Simple - from google drive.jpg b/src/UglyToad.PdfPig.Tests/Integration/Documents/Single Page Simple - from google drive.jpg new file mode 100644 index 00000000..35c8c0df Binary files /dev/null and b/src/UglyToad.PdfPig.Tests/Integration/Documents/Single Page Simple - from google drive.jpg differ diff --git a/src/UglyToad.PdfPig.Tests/Integration/Documents/Type0 Font.jpg b/src/UglyToad.PdfPig.Tests/Integration/Documents/Type0 Font.jpg new file mode 100644 index 00000000..1ca28e4a Binary files /dev/null and b/src/UglyToad.PdfPig.Tests/Integration/Documents/Type0 Font.jpg differ diff --git a/src/UglyToad.PdfPig.Tests/Integration/VisualVerification/GenerateLetterBoundingBoxImages.cs b/src/UglyToad.PdfPig.Tests/Integration/VisualVerification/GenerateLetterBoundingBoxImages.cs new file mode 100644 index 00000000..81ce17f9 --- /dev/null +++ b/src/UglyToad.PdfPig.Tests/Integration/VisualVerification/GenerateLetterBoundingBoxImages.cs @@ -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); + } + } +} diff --git a/src/UglyToad.PdfPig.Tests/UglyToad.PdfPig.Tests.csproj b/src/UglyToad.PdfPig.Tests/UglyToad.PdfPig.Tests.csproj index 6fb55125..424de5e6 100644 --- a/src/UglyToad.PdfPig.Tests/UglyToad.PdfPig.Tests.csproj +++ b/src/UglyToad.PdfPig.Tests/UglyToad.PdfPig.Tests.csproj @@ -31,6 +31,7 @@ + diff --git a/src/UglyToad.PdfPig/Content/Letter.cs b/src/UglyToad.PdfPig/Content/Letter.cs index e83f940a..a0ebd4ad 100644 --- a/src/UglyToad.PdfPig/Content/Letter.cs +++ b/src/UglyToad.PdfPig/Content/Letter.cs @@ -49,7 +49,7 @@ /// public override string ToString() { - return $"{Rectangle} {Value} {FontName} {PointSize}"; + return $"{Value} {Rectangle} {FontName} {PointSize}"; } } } diff --git a/src/UglyToad.PdfPig/Fonts/Composite/Type0Font.cs b/src/UglyToad.PdfPig/Fonts/Composite/Type0Font.cs index 544e2343..3702d8a8 100644 --- a/src/UglyToad.PdfPig/Fonts/Composite/Type0Font.cs +++ b/src/UglyToad.PdfPig/Fonts/Composite/Type0Font.cs @@ -82,8 +82,6 @@ var fromFont = CidFont.GetWidthFromDictionary(cid); - var box = GetBoundingBox(characterCode); - return fromFont; } diff --git a/src/UglyToad.PdfPig/Fonts/Simple/TrueTypeSimpleFont.cs b/src/UglyToad.PdfPig/Fonts/Simple/TrueTypeSimpleFont.cs index d02ebd7c..74c07cc9 100644 --- a/src/UglyToad.PdfPig/Fonts/Simple/TrueTypeSimpleFont.cs +++ b/src/UglyToad.PdfPig/Fonts/Simple/TrueTypeSimpleFont.cs @@ -90,7 +90,13 @@ namespace UglyToad.PdfPig.Fonts.Simple 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) @@ -138,3 +144,4 @@ namespace UglyToad.PdfPig.Fonts.Simple } } } + diff --git a/src/UglyToad.PdfPig/Fonts/TrueType/Glyphs/Glyph.cs b/src/UglyToad.PdfPig/Fonts/TrueType/Glyphs/Glyph.cs index 46fdce35..7753284f 100644 --- a/src/UglyToad.PdfPig/Fonts/TrueType/Glyphs/Glyph.cs +++ b/src/UglyToad.PdfPig/Fonts/TrueType/Glyphs/Glyph.cs @@ -24,6 +24,8 @@ public bool IsSimple { get; } + public bool IsEmpty => Points.Length == 0; + public Glyph(bool isSimple, byte[] instructions, int[] endPointsOfContours, GlyphPoint[] points, PdfRectangle bounds) { diff --git a/src/UglyToad.PdfPig/Fonts/TrueType/Glyphs/IGlyphDescription.cs b/src/UglyToad.PdfPig/Fonts/TrueType/Glyphs/IGlyphDescription.cs index eff1eec8..2a25d0c7 100644 --- a/src/UglyToad.PdfPig/Fonts/TrueType/Glyphs/IGlyphDescription.cs +++ b/src/UglyToad.PdfPig/Fonts/TrueType/Glyphs/IGlyphDescription.cs @@ -14,6 +14,8 @@ GlyphPoint[] Points { get; } + bool IsEmpty { get; } + IGlyphDescription DeepClone(); } diff --git a/src/UglyToad.PdfPig/Fonts/TrueType/TrueTypeFont.cs b/src/UglyToad.PdfPig/Fonts/TrueType/TrueTypeFont.cs index 1096f338..8c06ecab 100644 --- a/src/UglyToad.PdfPig/Fonts/TrueType/TrueTypeFont.cs +++ b/src/UglyToad.PdfPig/Fonts/TrueType/TrueTypeFont.cs @@ -20,14 +20,9 @@ public TrueTypeFont(decimal version, IReadOnlyDictionary tableHeaders, TableRegister tableRegister) { - if (tableRegister == null) - { - throw new ArgumentNullException(nameof(tableRegister)); - } - Version = version; TableHeaders = tableHeaders; - TableRegister = tableRegister; + TableRegister = tableRegister ?? throw new ArgumentNullException(nameof(tableRegister)); HeaderTable = tableRegister.HeaderTable; CMapTable = tableRegister.CMapTable; GlyphTable = tableRegister.GlyphDataTable; @@ -38,18 +33,7 @@ { boundingBox = default(PdfRectangle); - int index; - - if (CMapTable == null) - { - if (characterIdentifierToGlyphIndex == null) - { - return false; - } - - index = characterIdentifierToGlyphIndex(characterCode); - } - else if (!CMapTable.TryGetGlyphIndex(characterCode, out index)) + if (!TryGetGlyphIndex(characterCode, characterIdentifierToGlyphIndex, out var index)) { return false; } @@ -61,8 +45,15 @@ 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; } @@ -70,18 +61,39 @@ { 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 characterIdentifierToGlyphIndex, out int glyphIndex) + { + glyphIndex = 0; + 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; } - width = TableRegister.HorizontalMetricsTable.GetAdvanceWidth(index); - return true; } }