mirror of
https://github.com/UglyToad/PdfPig.git
synced 2025-10-15 03:34:52 +08:00
return both glyph and character bounding boxes for each letter, glyph is the actual outline of the letter whereas character is the surrounding space as defined in the pdf
This commit is contained in:
@@ -70,7 +70,7 @@
|
||||
break;
|
||||
}
|
||||
|
||||
var myX = pageLetter.Rectangle.BottomLeft.X;
|
||||
var myX = pageLetter.CharacterRectangle.Left;
|
||||
var theirX = pdfBoxData[index].X;
|
||||
|
||||
var myLetter = pageLetter.Value;
|
||||
@@ -87,7 +87,7 @@
|
||||
|
||||
Assert.Equal(theirX, myX, comparer);
|
||||
|
||||
Assert.Equal(pdfBoxData[index].Width, pageLetter.Rectangle.Width, comparer);
|
||||
Assert.Equal(pdfBoxData[index].Width, pageLetter.CharacterRectangle.Width, comparer);
|
||||
|
||||
index++;
|
||||
}
|
||||
@@ -97,6 +97,8 @@
|
||||
[Fact]
|
||||
public void LetterPositionsAreCorrectXfinium()
|
||||
{
|
||||
var comparer = new DecimalComparer(1);
|
||||
|
||||
using (var document = PdfDocument.Open(GetFilename()))
|
||||
{
|
||||
var page = document.GetPage(1);
|
||||
@@ -111,7 +113,7 @@
|
||||
break;
|
||||
}
|
||||
|
||||
var myX = pageLetter.Rectangle.Left;
|
||||
var myX = pageLetter.CharacterRectangle.Left;
|
||||
var theirX = positions[index].X;
|
||||
|
||||
var myLetter = pageLetter.Value;
|
||||
@@ -123,9 +125,9 @@
|
||||
}
|
||||
|
||||
Assert.Equal(theirLetter, myLetter);
|
||||
Assert.Equal(theirX, myX, 2);
|
||||
Assert.Equal(theirX, myX, comparer);
|
||||
|
||||
Assert.Equal(positions[index].Width, pageLetter.Rectangle.Width, 1);
|
||||
Assert.Equal(positions[index].Width, pageLetter.CharacterRectangle.Width, 1);
|
||||
|
||||
index++;
|
||||
}
|
||||
|
@@ -134,12 +134,12 @@ namespace UglyToad.PdfPig.Tests.Integration
|
||||
}
|
||||
|
||||
Assert.Equal(datum.Text, letter.Value);
|
||||
Assert.Equal(datum.X, letter.Rectangle.BottomLeft.X, 2);
|
||||
Assert.Equal(datum.X, letter.CharacterRectangle.BottomLeft.X, 2);
|
||||
|
||||
var transformed = page.Height - letter.Rectangle.BottomLeft.Y;
|
||||
var transformed = page.Height - letter.CharacterRectangle.BottomLeft.Y;
|
||||
Assert.Equal(datum.Y, transformed, 2);
|
||||
|
||||
Assert.Equal(datum.Width, letter.Rectangle.Width, 2);
|
||||
Assert.Equal(datum.Width, letter.CharacterRectangle.Width, 2);
|
||||
|
||||
Assert.Equal(datum.FontName, letter.FontName);
|
||||
|
||||
@@ -179,13 +179,13 @@ namespace UglyToad.PdfPig.Tests.Integration
|
||||
}
|
||||
|
||||
Assert.Equal(datum.Text, letter.Value);
|
||||
Assert.Equal(datum.X, letter.Rectangle.BottomLeft.X, 2);
|
||||
Assert.Equal(datum.X, letter.CharacterRectangle.BottomLeft.X, 2);
|
||||
|
||||
var transformed = page.Height - letter.Rectangle.BottomLeft.Y;
|
||||
var transformed = page.Height - letter.CharacterRectangle.BottomLeft.Y;
|
||||
Assert.Equal(datum.Y, transformed, 2);
|
||||
|
||||
// Until we get width from glyphs we're a bit out.
|
||||
Assert.True(Math.Abs(datum.Width - letter.Rectangle.Width) < 0.03m);
|
||||
Assert.True(Math.Abs(datum.Width - letter.CharacterRectangle.Width) < 0.03m);
|
||||
|
||||
index++;
|
||||
}
|
||||
|
@@ -48,27 +48,27 @@
|
||||
|
||||
Assert.Equal("I", page.Letters[0].Value);
|
||||
|
||||
Assert.Equal(90.1m, page.Letters[0].Rectangle.BottomLeft.X, comparer);
|
||||
Assert.Equal(709.2m, page.Letters[0].Rectangle.BottomLeft.Y, comparer);
|
||||
Assert.Equal(90.1m, page.Letters[0].GlyphRectangle.BottomLeft.X, comparer);
|
||||
Assert.Equal(709.2m, page.Letters[0].GlyphRectangle.BottomLeft.Y, comparer);
|
||||
|
||||
Assert.Equal(94.0m, page.Letters[0].Rectangle.TopRight.X, comparer);
|
||||
Assert.Equal(719.89m, page.Letters[0].Rectangle.TopRight.Y, comparer);
|
||||
Assert.Equal(94.0m, page.Letters[0].GlyphRectangle.TopRight.X, comparer);
|
||||
Assert.Equal(719.89m, page.Letters[0].GlyphRectangle.TopRight.Y, comparer);
|
||||
|
||||
Assert.Equal("a", page.Letters[5].Value);
|
||||
|
||||
Assert.Equal(114.5m, page.Letters[5].Rectangle.BottomLeft.X, comparer);
|
||||
Assert.Equal(709.2m, page.Letters[5].Rectangle.BottomLeft.Y, comparer);
|
||||
Assert.Equal(114.5m, page.Letters[5].GlyphRectangle.BottomLeft.X, comparer);
|
||||
Assert.Equal(709.2m, page.Letters[5].GlyphRectangle.BottomLeft.Y, comparer);
|
||||
|
||||
Assert.Equal(119.82m, page.Letters[5].Rectangle.TopRight.X, comparer);
|
||||
Assert.Equal(714.89m, page.Letters[5].Rectangle.TopRight.Y, comparer);
|
||||
Assert.Equal(119.82m, page.Letters[5].GlyphRectangle.TopRight.X, comparer);
|
||||
Assert.Equal(714.89m, page.Letters[5].GlyphRectangle.TopRight.Y, comparer);
|
||||
|
||||
Assert.Equal("f", page.Letters[16].Value);
|
||||
|
||||
Assert.Equal(169.9m, page.Letters[16].Rectangle.BottomLeft.X, comparer);
|
||||
Assert.Equal(709.2m, page.Letters[16].Rectangle.BottomLeft.Y, comparer);
|
||||
Assert.Equal(169.9m, page.Letters[16].GlyphRectangle.BottomLeft.X, comparer);
|
||||
Assert.Equal(709.2m, page.Letters[16].GlyphRectangle.BottomLeft.Y, comparer);
|
||||
|
||||
Assert.Equal(176.89m, page.Letters[16].Rectangle.TopRight.X, comparer);
|
||||
Assert.Equal(719.89m, page.Letters[16].Rectangle.TopRight.Y, comparer);
|
||||
Assert.Equal(176.89m, page.Letters[16].GlyphRectangle.TopRight.X, comparer);
|
||||
Assert.Equal(719.89m, page.Letters[16].GlyphRectangle.TopRight.Y, comparer);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -57,8 +57,8 @@ namespace UglyToad.PdfPig.Tests.Integration
|
||||
{
|
||||
var page = document.GetPage(1);
|
||||
|
||||
Assert.True((bool) page.Letters.Any(x => x.Rectangle.Width != 0));
|
||||
Assert.True((bool) page.Letters.Any(x => x.Rectangle.Height != 0));
|
||||
Assert.True((bool) page.Letters.Any(x => x.GlyphRectangle.Width != 0));
|
||||
Assert.True((bool) page.Letters.Any(x => x.GlyphRectangle.Height != 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -57,8 +57,8 @@
|
||||
{
|
||||
foreach (var word in page.Letters)
|
||||
{
|
||||
graphics.DrawRectangle(redPen, new Rectangle((int)word.Rectangle.Left,
|
||||
792 - (int)word.Rectangle.Top, (int)Math.Max(1, word.Rectangle.Width), (int)word.Rectangle.Height));
|
||||
graphics.DrawRectangle(redPen, new Rectangle((int)word.GlyphRectangle.Left,
|
||||
792 - (int)word.GlyphRectangle.Top, (int)Math.Max(1, word.GlyphRectangle.Width), (int)word.GlyphRectangle.Height));
|
||||
}
|
||||
|
||||
var imageName = $"{file}.jpg";
|
||||
|
@@ -13,9 +13,14 @@
|
||||
public string Value { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Position of the bounding box.
|
||||
/// Position of the bounding box for the glyph.
|
||||
/// </summary>
|
||||
public PdfRectangle Rectangle { get; }
|
||||
public PdfRectangle GlyphRectangle { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The bounding box for the entire character.
|
||||
/// </summary>
|
||||
public PdfRectangle CharacterRectangle { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Size as defined in the PDF file. This is not equivalent to font size in points but is relative to other font sizes on the page.
|
||||
@@ -35,13 +40,14 @@
|
||||
/// <summary>
|
||||
/// Create a new letter to represent some text drawn by the Tj operator.
|
||||
/// </summary>
|
||||
internal Letter(string value, PdfRectangle rectangle, decimal fontSize, string fontName, decimal pointSize)
|
||||
internal Letter(string value, PdfRectangle glyphRectangle, PdfRectangle characterRectangle, decimal fontSize, string fontName, decimal pointSize)
|
||||
{
|
||||
Value = value;
|
||||
Rectangle = rectangle;
|
||||
GlyphRectangle = glyphRectangle;
|
||||
FontSize = fontSize;
|
||||
FontName = fontName;
|
||||
PointSize = pointSize;
|
||||
CharacterRectangle = characterRectangle;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -49,7 +55,7 @@
|
||||
/// </summary>
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{Value} {Rectangle} {FontName} {PointSize}";
|
||||
return $"{Value} {GlyphRectangle} {FontName} {PointSize}";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -42,6 +42,8 @@
|
||||
|
||||
decimal GetWidthFromDictionary(int cid);
|
||||
|
||||
decimal GetWidthFromFont(int characterIdentifier);
|
||||
|
||||
PdfRectangle GetBoundingBox(int characterIdentifier);
|
||||
}
|
||||
}
|
@@ -12,7 +12,9 @@
|
||||
|
||||
bool TryGetBoundingBox(int characterIdentifier, Func<int, int> characterIdentifierToGlyphIndex, out PdfRectangle boundingBox);
|
||||
|
||||
bool TryGetBoundingAdvancedWidth(int characterCode, out decimal width);
|
||||
bool TryGetBoundingAdvancedWidth(int characterIdentifier, Func<int, int> characterIdentifierToGlyphIndex, out decimal width);
|
||||
|
||||
bool TryGetBoundingAdvancedWidth(int characterIdentifier, out decimal width);
|
||||
|
||||
int GetFontMatrixMultiplier();
|
||||
}
|
||||
|
@@ -46,10 +46,14 @@
|
||||
FontMatrix = TransformationMatrix.FromValues(scale, 0, 0, scale, 0, 0);
|
||||
}
|
||||
|
||||
public decimal GetWidthFromFont(int characterCode)
|
||||
public decimal GetWidthFromFont(int characterIdentifier)
|
||||
{
|
||||
if (fontProgram.TryGetBoundingAdvancedWidth(characterIdentifier, cidToGid.GetGlyphIndex, out var width))
|
||||
{
|
||||
return width;
|
||||
}
|
||||
// TODO: Read the font width from the font program.
|
||||
throw new System.NotImplementedException();
|
||||
return GetWidthFromDictionary(characterIdentifier);
|
||||
}
|
||||
|
||||
public decimal GetWidthFromDictionary(int characterIdentifier)
|
||||
|
@@ -69,22 +69,22 @@
|
||||
return ToUnicode.TryGet(characterCode, out value);
|
||||
}
|
||||
|
||||
public PdfRectangle GetBoundingBox(int characterCode)
|
||||
public CharacterBoundingBox GetBoundingBox(int characterCode)
|
||||
{
|
||||
var matrix = GetFontMatrix();
|
||||
|
||||
var boundingBox = GetBoundingBoxInGlyphSpace(characterCode);
|
||||
|
||||
return matrix.Transform(boundingBox);
|
||||
}
|
||||
boundingBox = matrix.Transform(boundingBox);
|
||||
|
||||
public decimal GetWidth(int characterCode)
|
||||
{
|
||||
var cid = CMap.ConvertToCid(characterCode);
|
||||
var characterIdentifier = CMap.ConvertToCid(characterCode);
|
||||
|
||||
var fromFont = CidFont.GetWidthFromDictionary(cid);
|
||||
var width = CidFont.GetWidthFromFont(characterIdentifier);
|
||||
|
||||
return fromFont;
|
||||
var advanceWidth = new PdfRectangle(0, 0, width, 0);
|
||||
advanceWidth = matrix.Transform(advanceWidth);
|
||||
|
||||
return new CharacterBoundingBox(boundingBox, advanceWidth);
|
||||
}
|
||||
|
||||
public PdfRectangle GetBoundingBoxInGlyphSpace(int characterCode)
|
||||
|
@@ -15,8 +15,21 @@
|
||||
|
||||
bool TryGetUnicode(int characterCode, out string value);
|
||||
|
||||
PdfRectangle GetBoundingBox(int characterCode);
|
||||
CharacterBoundingBox GetBoundingBox(int characterCode);
|
||||
|
||||
TransformationMatrix GetFontMatrix();
|
||||
}
|
||||
|
||||
internal class CharacterBoundingBox
|
||||
{
|
||||
public PdfRectangle GlyphBounds { get; }
|
||||
|
||||
public PdfRectangle CharacterBounds { get; }
|
||||
|
||||
public CharacterBoundingBox(PdfRectangle glyphBounds, PdfRectangle characterBounds)
|
||||
{
|
||||
GlyphBounds = glyphBounds;
|
||||
CharacterBounds = characterBounds;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,6 +1,4 @@
|
||||
using System;
|
||||
|
||||
namespace UglyToad.PdfPig.Fonts.Simple
|
||||
namespace UglyToad.PdfPig.Fonts.Simple
|
||||
{
|
||||
using Cmap;
|
||||
using Composite;
|
||||
@@ -88,15 +86,32 @@ namespace UglyToad.PdfPig.Fonts.Simple
|
||||
return true;
|
||||
}
|
||||
|
||||
public PdfRectangle GetBoundingBox(int characterCode)
|
||||
public CharacterBoundingBox GetBoundingBox(int characterCode)
|
||||
{
|
||||
var fontMatrix = GetFontMatrix();
|
||||
|
||||
var boundingBox = GetBoundingBoxInGlyphSpace(characterCode);
|
||||
|
||||
var result = fontMatrix.Transform(boundingBox);
|
||||
boundingBox = fontMatrix.Transform(boundingBox);
|
||||
|
||||
return result;
|
||||
decimal width;
|
||||
if (font == null)
|
||||
{
|
||||
width = widths[characterCode];
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!font.TryGetBoundingAdvancedWidth(characterCode, out width))
|
||||
{
|
||||
width = boundingBox.Width;
|
||||
}
|
||||
}
|
||||
|
||||
var advancedRectangle = new PdfRectangle(0, 0, width, 0);
|
||||
|
||||
advancedRectangle = fontMatrix.Transform(advancedRectangle);
|
||||
|
||||
return new CharacterBoundingBox(boundingBox, advancedRectangle);
|
||||
}
|
||||
|
||||
private PdfRectangle GetBoundingBoxInGlyphSpace(int characterCode)
|
||||
|
@@ -86,9 +86,13 @@
|
||||
return true;
|
||||
}
|
||||
|
||||
public PdfRectangle GetBoundingBox(int characterCode)
|
||||
public CharacterBoundingBox GetBoundingBox(int characterCode)
|
||||
{
|
||||
return fontMatrix.Transform(GetBoundingBoxInGlyphSpace(characterCode));
|
||||
var boundingBox = GetBoundingBoxInGlyphSpace(characterCode);
|
||||
|
||||
boundingBox = fontMatrix.Transform(boundingBox);
|
||||
|
||||
return new CharacterBoundingBox(boundingBox, boundingBox);
|
||||
}
|
||||
|
||||
private PdfRectangle GetBoundingBoxInGlyphSpace(int characterCode)
|
||||
|
@@ -44,9 +44,13 @@
|
||||
return true;
|
||||
}
|
||||
|
||||
public PdfRectangle GetBoundingBox(int characterCode)
|
||||
public CharacterBoundingBox GetBoundingBox(int characterCode)
|
||||
{
|
||||
return fontMatrix.Transform(GetBoundingBoxInGlyphSpace(characterCode));
|
||||
var boundingBox = GetBoundingBoxInGlyphSpace(characterCode);
|
||||
|
||||
boundingBox = fontMatrix.Transform(boundingBox);
|
||||
|
||||
return new CharacterBoundingBox(boundingBox, boundingBox);
|
||||
}
|
||||
|
||||
private PdfRectangle GetBoundingBoxInGlyphSpace(int characterCode)
|
||||
|
@@ -63,9 +63,13 @@
|
||||
return true;
|
||||
}
|
||||
|
||||
public PdfRectangle GetBoundingBox(int characterCode)
|
||||
public CharacterBoundingBox GetBoundingBox(int characterCode)
|
||||
{
|
||||
return fontMatrix.Transform(GetBoundingBoxInGlyphSpace(characterCode));
|
||||
var boundingBox = GetBoundingBoxInGlyphSpace(characterCode);
|
||||
|
||||
boundingBox = fontMatrix.Transform(boundingBox);
|
||||
|
||||
return new CharacterBoundingBox(boundingBox, boundingBox);
|
||||
}
|
||||
|
||||
private PdfRectangle GetBoundingBoxInGlyphSpace(int characterCode)
|
||||
|
@@ -57,11 +57,12 @@
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool TryGetBoundingAdvancedWidth(int characterCode, out decimal width)
|
||||
public bool TryGetBoundingAdvancedWidth(int characterIdentifier, out decimal width) => TryGetBoundingAdvancedWidth(characterIdentifier, null, out width);
|
||||
public bool TryGetBoundingAdvancedWidth(int characterIdentifier, Func<int, int> characterIdentifierToGlyphIndex, out decimal width)
|
||||
{
|
||||
width = 0m;
|
||||
|
||||
if (!TryGetGlyphIndex(characterCode, null, out var index))
|
||||
if (!TryGetGlyphIndex(characterIdentifier, characterIdentifierToGlyphIndex, out var index))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
@@ -128,22 +128,25 @@
|
||||
|
||||
var boundingBox = font.GetBoundingBox(code);
|
||||
|
||||
var transformedDisplacement = transformationMatrix
|
||||
var transformedGlyphBounds = transformationMatrix
|
||||
.Transform(TextMatrices.TextMatrix
|
||||
.Transform(renderingMatrix
|
||||
.Transform(boundingBox)));
|
||||
.Transform(boundingBox.GlyphBounds)));
|
||||
var transformedGlyphOrigin = transformationMatrix
|
||||
.Transform(TextMatrices.TextMatrix
|
||||
.Transform(renderingMatrix.Transform(boundingBox.CharacterBounds)));
|
||||
|
||||
ShowGlyph(font, transformedDisplacement, unicode, fontSize, pointSize);
|
||||
ShowGlyph(font, transformedGlyphBounds, transformedGlyphOrigin, unicode, fontSize, pointSize);
|
||||
|
||||
decimal tx, ty;
|
||||
if (font.IsVertical)
|
||||
{
|
||||
tx = 0;
|
||||
ty = boundingBox.Height * fontSize + characterSpacing + wordSpacing;
|
||||
ty = boundingBox.CharacterBounds.Height * fontSize + characterSpacing + wordSpacing;
|
||||
}
|
||||
else
|
||||
{
|
||||
tx = (boundingBox.Width * fontSize + characterSpacing + wordSpacing) * horizontalScaling;
|
||||
tx = (boundingBox.CharacterBounds.Width * fontSize + characterSpacing + wordSpacing) * horizontalScaling;
|
||||
ty = 0;
|
||||
}
|
||||
|
||||
@@ -211,9 +214,9 @@
|
||||
TextMatrices.TextMatrix = newMatrix;
|
||||
}
|
||||
|
||||
private void ShowGlyph(IFont font, PdfRectangle rectangle, string unicode, decimal fontSize, decimal pointSize)
|
||||
private void ShowGlyph(IFont font, PdfRectangle glyphRectangle, PdfRectangle characterRectangle, string unicode, decimal fontSize, decimal pointSize)
|
||||
{
|
||||
var letter = new Letter(unicode, rectangle, fontSize, font.Name.Data, pointSize);
|
||||
var letter = new Letter(unicode, glyphRectangle, characterRectangle, fontSize, font.Name.Data, pointSize);
|
||||
|
||||
Letters.Add(letter);
|
||||
}
|
||||
|
Reference in New Issue
Block a user