From 0e844d88df171a049d2d33dc5ce30ddd7aa9be91 Mon Sep 17 00:00:00 2001 From: Eliot Jones Date: Wed, 18 Apr 2018 19:24:10 +0100 Subject: [PATCH] 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 --- ...SinglePageNonLatinAcrobatDistillerTests.cs | 12 +++++---- .../SinglePageSimpleGoogleChromeTests.cs | 12 ++++----- .../SinglePageSimpleOpenOfficeTests.cs | 24 ++++++++--------- .../Integration/Type0FontTests.cs | 4 +-- .../GenerateLetterBoundingBoxImages.cs | 4 +-- src/UglyToad.PdfPig/Content/Letter.cs | 16 +++++++---- .../Fonts/CidFonts/ICidFont.cs | 2 ++ .../Fonts/CidFonts/ICidFontProgram.cs | 4 ++- .../Fonts/CidFonts/Type2CidFont.cs | 8 ++++-- .../Fonts/Composite/Type0Font.cs | 16 +++++------ src/UglyToad.PdfPig/Fonts/IFont.cs | 15 ++++++++++- .../Fonts/Simple/TrueTypeSimpleFont.cs | 27 ++++++++++++++----- .../Fonts/Simple/Type1FontSimple.cs | 8 ++++-- .../Fonts/Simple/Type1Standard14Font.cs | 8 ++++-- src/UglyToad.PdfPig/Fonts/Simple/Type3Font.cs | 8 ++++-- .../Fonts/TrueType/TrueTypeFont.cs | 5 ++-- .../Graphics/ContentStreamProcessor.cs | 17 +++++++----- 17 files changed, 125 insertions(+), 65 deletions(-) diff --git a/src/UglyToad.PdfPig.Tests/Integration/SinglePageNonLatinAcrobatDistillerTests.cs b/src/UglyToad.PdfPig.Tests/Integration/SinglePageNonLatinAcrobatDistillerTests.cs index f7627a0f..a5b10d70 100644 --- a/src/UglyToad.PdfPig.Tests/Integration/SinglePageNonLatinAcrobatDistillerTests.cs +++ b/src/UglyToad.PdfPig.Tests/Integration/SinglePageNonLatinAcrobatDistillerTests.cs @@ -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++; } diff --git a/src/UglyToad.PdfPig.Tests/Integration/SinglePageSimpleGoogleChromeTests.cs b/src/UglyToad.PdfPig.Tests/Integration/SinglePageSimpleGoogleChromeTests.cs index b90fd210..bbf32308 100644 --- a/src/UglyToad.PdfPig.Tests/Integration/SinglePageSimpleGoogleChromeTests.cs +++ b/src/UglyToad.PdfPig.Tests/Integration/SinglePageSimpleGoogleChromeTests.cs @@ -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++; } diff --git a/src/UglyToad.PdfPig.Tests/Integration/SinglePageSimpleOpenOfficeTests.cs b/src/UglyToad.PdfPig.Tests/Integration/SinglePageSimpleOpenOfficeTests.cs index 24e152e4..5cb876fe 100644 --- a/src/UglyToad.PdfPig.Tests/Integration/SinglePageSimpleOpenOfficeTests.cs +++ b/src/UglyToad.PdfPig.Tests/Integration/SinglePageSimpleOpenOfficeTests.cs @@ -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); } } diff --git a/src/UglyToad.PdfPig.Tests/Integration/Type0FontTests.cs b/src/UglyToad.PdfPig.Tests/Integration/Type0FontTests.cs index b1d45bd2..802dc6bb 100644 --- a/src/UglyToad.PdfPig.Tests/Integration/Type0FontTests.cs +++ b/src/UglyToad.PdfPig.Tests/Integration/Type0FontTests.cs @@ -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)); } } } diff --git a/src/UglyToad.PdfPig.Tests/Integration/VisualVerification/GenerateLetterBoundingBoxImages.cs b/src/UglyToad.PdfPig.Tests/Integration/VisualVerification/GenerateLetterBoundingBoxImages.cs index 1d0b7f74..61e07c95 100644 --- a/src/UglyToad.PdfPig.Tests/Integration/VisualVerification/GenerateLetterBoundingBoxImages.cs +++ b/src/UglyToad.PdfPig.Tests/Integration/VisualVerification/GenerateLetterBoundingBoxImages.cs @@ -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"; diff --git a/src/UglyToad.PdfPig/Content/Letter.cs b/src/UglyToad.PdfPig/Content/Letter.cs index a0ebd4ad..26364b02 100644 --- a/src/UglyToad.PdfPig/Content/Letter.cs +++ b/src/UglyToad.PdfPig/Content/Letter.cs @@ -13,9 +13,14 @@ public string Value { get; } /// - /// Position of the bounding box. + /// Position of the bounding box for the glyph. /// - public PdfRectangle Rectangle { get; } + public PdfRectangle GlyphRectangle { get; } + + /// + /// The bounding box for the entire character. + /// + public PdfRectangle CharacterRectangle { get; } /// /// 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 @@ /// /// Create a new letter to represent some text drawn by the Tj operator. /// - 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; } /// @@ -49,7 +55,7 @@ /// public override string ToString() { - return $"{Value} {Rectangle} {FontName} {PointSize}"; + return $"{Value} {GlyphRectangle} {FontName} {PointSize}"; } } } diff --git a/src/UglyToad.PdfPig/Fonts/CidFonts/ICidFont.cs b/src/UglyToad.PdfPig/Fonts/CidFonts/ICidFont.cs index 4a3593da..abb715fe 100644 --- a/src/UglyToad.PdfPig/Fonts/CidFonts/ICidFont.cs +++ b/src/UglyToad.PdfPig/Fonts/CidFonts/ICidFont.cs @@ -42,6 +42,8 @@ decimal GetWidthFromDictionary(int cid); + decimal GetWidthFromFont(int characterIdentifier); + PdfRectangle GetBoundingBox(int characterIdentifier); } } \ No newline at end of file diff --git a/src/UglyToad.PdfPig/Fonts/CidFonts/ICidFontProgram.cs b/src/UglyToad.PdfPig/Fonts/CidFonts/ICidFontProgram.cs index 379a3dbd..2d29c626 100644 --- a/src/UglyToad.PdfPig/Fonts/CidFonts/ICidFontProgram.cs +++ b/src/UglyToad.PdfPig/Fonts/CidFonts/ICidFontProgram.cs @@ -12,7 +12,9 @@ bool TryGetBoundingBox(int characterIdentifier, Func characterIdentifierToGlyphIndex, out PdfRectangle boundingBox); - bool TryGetBoundingAdvancedWidth(int characterCode, out decimal width); + bool TryGetBoundingAdvancedWidth(int characterIdentifier, Func characterIdentifierToGlyphIndex, out decimal width); + + bool TryGetBoundingAdvancedWidth(int characterIdentifier, out decimal width); int GetFontMatrixMultiplier(); } diff --git a/src/UglyToad.PdfPig/Fonts/CidFonts/Type2CidFont.cs b/src/UglyToad.PdfPig/Fonts/CidFonts/Type2CidFont.cs index fe693300..173bb941 100644 --- a/src/UglyToad.PdfPig/Fonts/CidFonts/Type2CidFont.cs +++ b/src/UglyToad.PdfPig/Fonts/CidFonts/Type2CidFont.cs @@ -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) diff --git a/src/UglyToad.PdfPig/Fonts/Composite/Type0Font.cs b/src/UglyToad.PdfPig/Fonts/Composite/Type0Font.cs index 048faedf..68907103 100644 --- a/src/UglyToad.PdfPig/Fonts/Composite/Type0Font.cs +++ b/src/UglyToad.PdfPig/Fonts/Composite/Type0Font.cs @@ -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) diff --git a/src/UglyToad.PdfPig/Fonts/IFont.cs b/src/UglyToad.PdfPig/Fonts/IFont.cs index 42d35195..fa26af5d 100644 --- a/src/UglyToad.PdfPig/Fonts/IFont.cs +++ b/src/UglyToad.PdfPig/Fonts/IFont.cs @@ -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; + } + } } diff --git a/src/UglyToad.PdfPig/Fonts/Simple/TrueTypeSimpleFont.cs b/src/UglyToad.PdfPig/Fonts/Simple/TrueTypeSimpleFont.cs index 48e9f9cc..9b5ace7f 100644 --- a/src/UglyToad.PdfPig/Fonts/Simple/TrueTypeSimpleFont.cs +++ b/src/UglyToad.PdfPig/Fonts/Simple/TrueTypeSimpleFont.cs @@ -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) diff --git a/src/UglyToad.PdfPig/Fonts/Simple/Type1FontSimple.cs b/src/UglyToad.PdfPig/Fonts/Simple/Type1FontSimple.cs index b30c5ef4..9cf2e0c6 100644 --- a/src/UglyToad.PdfPig/Fonts/Simple/Type1FontSimple.cs +++ b/src/UglyToad.PdfPig/Fonts/Simple/Type1FontSimple.cs @@ -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) diff --git a/src/UglyToad.PdfPig/Fonts/Simple/Type1Standard14Font.cs b/src/UglyToad.PdfPig/Fonts/Simple/Type1Standard14Font.cs index 581404bf..49e68059 100644 --- a/src/UglyToad.PdfPig/Fonts/Simple/Type1Standard14Font.cs +++ b/src/UglyToad.PdfPig/Fonts/Simple/Type1Standard14Font.cs @@ -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) diff --git a/src/UglyToad.PdfPig/Fonts/Simple/Type3Font.cs b/src/UglyToad.PdfPig/Fonts/Simple/Type3Font.cs index 98ddc981..8defdfe6 100644 --- a/src/UglyToad.PdfPig/Fonts/Simple/Type3Font.cs +++ b/src/UglyToad.PdfPig/Fonts/Simple/Type3Font.cs @@ -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) diff --git a/src/UglyToad.PdfPig/Fonts/TrueType/TrueTypeFont.cs b/src/UglyToad.PdfPig/Fonts/TrueType/TrueTypeFont.cs index 01c21797..ec0b1f91 100644 --- a/src/UglyToad.PdfPig/Fonts/TrueType/TrueTypeFont.cs +++ b/src/UglyToad.PdfPig/Fonts/TrueType/TrueTypeFont.cs @@ -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 characterIdentifierToGlyphIndex, out decimal width) { width = 0m; - if (!TryGetGlyphIndex(characterCode, null, out var index)) + if (!TryGetGlyphIndex(characterIdentifier, characterIdentifierToGlyphIndex, out var index)) { return false; } diff --git a/src/UglyToad.PdfPig/Graphics/ContentStreamProcessor.cs b/src/UglyToad.PdfPig/Graphics/ContentStreamProcessor.cs index dc081bd5..25c8aeae 100644 --- a/src/UglyToad.PdfPig/Graphics/ContentStreamProcessor.cs +++ b/src/UglyToad.PdfPig/Graphics/ContentStreamProcessor.cs @@ -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); }