From 6f90494bbbd36fa145ad4f79530d9b7fd0f26499 Mon Sep 17 00:00:00 2001 From: Raul Ruiz Date: Thu, 25 Jul 2024 15:00:33 +0200 Subject: [PATCH 1/4] FEAT: Horizontal and vertical alignment within XRect --- PdfSharpCore/Drawing.Layout/XTextFormatter.cs | 105 +++++++++++++++--- 1 file changed, 88 insertions(+), 17 deletions(-) diff --git a/PdfSharpCore/Drawing.Layout/XTextFormatter.cs b/PdfSharpCore/Drawing.Layout/XTextFormatter.cs index b4e1e5bf..5701c5f9 100644 --- a/PdfSharpCore/Drawing.Layout/XTextFormatter.cs +++ b/PdfSharpCore/Drawing.Layout/XTextFormatter.cs @@ -150,8 +150,9 @@ public void DrawString(string text, XFont font, XBrush brush, XRect layoutRectan /// The font. /// The text brush. /// The layout rectangle. - /// The format. Must be XStringFormat.TopLeft - public void DrawString(string text, XFont font, XBrush brush, XRect layoutRectangle, XStringFormat format, XUnit? lineHeight = null) + /// The height of each line + public XRect GetLayout(string text, XFont font, XBrush brush, XRect layoutRectangle, + XUnit? lineHeight = null) { if (text == null) throw new ArgumentNullException("text"); @@ -159,8 +160,6 @@ public void DrawString(string text, XFont font, XBrush brush, XRect layoutRectan throw new ArgumentNullException("font"); if (brush == null) throw new ArgumentNullException("brush"); - if (format.Alignment != XStringAlignment.Near || format.LineAlignment != XLineAlignment.Near) - throw new ArgumentException("Only TopLeft alignment is currently implemented."); Text = text; Font = font; @@ -169,36 +168,78 @@ public void DrawString(string text, XFont font, XBrush brush, XRect layoutRectan _lineHeight = lineHeight?.Point ?? _lineSpace; if (text.Length == 0) - return; + return new XRect(layoutRectangle.Location.X, layoutRectangle.Location.Y, 0, 0); CreateBlocks(); CreateLayout(); + return _layoutRectangle; + } + + /// + /// Draws the text. + /// + /// The text to be drawn. + /// The font. + /// The text brush. + /// The layout rectangle. + /// The format. Must be XStringFormat.TopLeft + /// The height of each line + public void DrawString(string text, XFont font, XBrush brush, XRect layoutRectangle, XStringFormat format, + XUnit? lineHeight = null) + { + if (format == null) + throw new ArgumentNullException(nameof(format)); + + if (text.Length == 0) + return; + + GetLayout(text, font, brush, layoutRectangle, lineHeight); + + SetAlignment(new TextFormatAlignment() + { + Horizontal = GetHorizontalAlignmentFromFormat(format), + Vertical = GetVerticalAlignmentFromFormat(format) + }); + double dx = layoutRectangle.Location.X; - double dy = layoutRectangle.Location.Y + _cyAscent; - + double dy = layoutRectangle.Location.Y; + + var lines = GetLines(_blocks).ToArray(); + if (VerticalAlignment == XVerticalAlignment.Middle) { - dy += layoutRectangle.Height / 2 - _layoutRectangle.Height / 2 - _cyDescent; + dy += (layoutRectangle.Height - _layoutRectangle.Height + _lineHeight) / 2; } else if (VerticalAlignment == XVerticalAlignment.Bottom) { - dy = layoutRectangle.Location.Y + layoutRectangle.Height - _layoutRectangle.Height + _lineHeight - _cyDescent; + dy = layoutRectangle.Location.Y + layoutRectangle.Height - _layoutRectangle.Height + _lineHeight - + _cyDescent; } - + int count = _blocks.Count; - for (int idx = 0; idx < count; idx++) + foreach (var line in lines) { - Block block = _blocks[idx]; - if (block.Stop) - break; - if (block.Type == BlockType.LineBreak) - continue; - _gfx.DrawString(block.Text, font, brush, dx + block.Location.X, dy + block.Location.Y); + if (Alignment != XParagraphAlignment.Justify) + { + var enumerable = line as Block[] ?? line.ToArray(); + var lineText = string.Join(" ", enumerable.Select(l => l.Text)); + var locationX = dx; + if (format.Alignment == XStringAlignment.Center) + locationX = dx + layoutRectangle.Width / 2; + if (format.Alignment == XStringAlignment.Far) + locationX += layoutRectangle.Width; + _gfx.DrawString(lineText, font, brush, locationX, dy + enumerable.First().Location.Y, format); + } } } + private static IEnumerable> GetLines(List blocks) + { + return blocks.GroupBy(b => b.Location.Y); + } + void CreateBlocks() { _blocks.Clear(); @@ -378,6 +419,36 @@ void HorizontalAlignLine(int firstIndex, int lastIndex, double layoutWidth) // - underline and strike-out variation // - super- and sub-script // - ... + + private static XParagraphAlignment GetHorizontalAlignmentFromFormat(XStringFormat format) + { + switch (format.Alignment) + { + case XStringAlignment.Center: + return XParagraphAlignment.Center; + case XStringAlignment.Near: + return XParagraphAlignment.Left; + case XStringAlignment.Far: + return XParagraphAlignment.Right; + } + + return XParagraphAlignment.Justify; + } + + private static XVerticalAlignment GetVerticalAlignmentFromFormat(XStringFormat format) + { + switch (format.LineAlignment) + { + case XLineAlignment.Center: + return XVerticalAlignment.Middle; + case XLineAlignment.BaseLine: + case XLineAlignment.Far: + return XVerticalAlignment.Bottom; + case XLineAlignment.Near: + default: + return XVerticalAlignment.Top; + } + } } public class TextFormatAlignment From 7e87915c7c9c50cf8bacdf2fd171b1e1402c4aa9 Mon Sep 17 00:00:00 2001 From: Raul Ruiz Date: Thu, 25 Jul 2024 16:19:02 +0200 Subject: [PATCH 2/4] FEAT: Vertical and horizontal alignment --- PdfSharpCore/PdfSharpCore.csproj | 1 + SampleApp/Program.cs | 35 ++++++++++++++++++++++++++++++-- 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/PdfSharpCore/PdfSharpCore.csproj b/PdfSharpCore/PdfSharpCore.csproj index f203595a..7fc4d462 100644 --- a/PdfSharpCore/PdfSharpCore.csproj +++ b/PdfSharpCore/PdfSharpCore.csproj @@ -18,6 +18,7 @@ PdfSharpCore is a partial port of PdfSharp.Xamarin for .NET Core Additionally Mi PdfSharp for .NET Core true true + 1.0.1 diff --git a/SampleApp/Program.cs b/SampleApp/Program.cs index 1f46c514..ff064b19 100644 --- a/SampleApp/Program.cs +++ b/SampleApp/Program.cs @@ -40,12 +40,43 @@ public static void Main(string[] args) PdfSharpCore.Drawing.XGraphics? renderer = PdfSharpCore.Drawing.XGraphics.FromPdfPage(pageNewRenderer); renderer.DrawString( - "Testy Test Test" + "Testy Test Test" , new PdfSharpCore.Drawing.XFont("Arial", 12) , PdfSharpCore.Drawing.XBrushes.Black , new PdfSharpCore.Drawing.XPoint(12, 12) ); - + + PdfSharpCore.Drawing.Layout.XTextFormatter? formatter = new PdfSharpCore.Drawing.Layout.XTextFormatter(renderer); + + var font = new PdfSharpCore.Drawing.XFont("Arial", 12); + var brush = PdfSharpCore.Drawing.XBrushes.Black; + + formatter.AllowVerticalOverflow = true; + var originalLayout = new PdfSharpCore.Drawing.XRect(0, 30, 120, 120); + var text = "More and more text boxes to show alignment capabilities"; // " with addipional gline"; + var anotherText = + "Text to determine the size of the box I would like to place the text I'm goint to test"; + var rect = formatter.GetLayout( + anotherText, + font, + brush, + originalLayout); + rect.Location = new PdfSharpCore.Drawing.XPoint(50, 50); + formatter.AllowVerticalOverflow = false; + + // Draw the string + formatter.DrawString( + text, + font, + brush, + rect, + PdfSharpCore.Drawing.XStringFormats.BottomRight + ); + + // Draw the box to check that the text fits and aligns correctly + var transparentBrush = new PdfSharpCore.Drawing.XSolidBrush(PdfSharpCore.Drawing.XColor.FromArgb(20, brush.Color.R, brush.Color.G, brush.Color.B)); + renderer.DrawRectangle(transparentBrush, rect); + SaveDocument(document, outName); System.Console.WriteLine("Done!"); From 326e08088c74cdf8aaad9d561c664f4030c9b988 Mon Sep 17 00:00:00 2001 From: Raul Ruiz Date: Thu, 25 Jul 2024 17:37:37 +0200 Subject: [PATCH 3/4] FEAT: Justified text --- PdfSharpCore.Test/IO/LargePDFReadWrite.cs | 2 +- PdfSharpCore/Drawing.Layout/XTextFormatter.cs | 82 +++++++++---------- SampleApp/Program.cs | 6 +- 3 files changed, 45 insertions(+), 45 deletions(-) diff --git a/PdfSharpCore.Test/IO/LargePDFReadWrite.cs b/PdfSharpCore.Test/IO/LargePDFReadWrite.cs index a83d8c43..db91d056 100644 --- a/PdfSharpCore.Test/IO/LargePDFReadWrite.cs +++ b/PdfSharpCore.Test/IO/LargePDFReadWrite.cs @@ -53,7 +53,7 @@ private void AddAPage(PdfDocument document, XFont font) var height = page.Height.Value - 50 - y; var rect = new XRect(40, 50, width, height); renderer.DrawRectangle(XBrushes.SeaShell, rect); - tf.DrawString(TestData.LoremIpsumText, font, XBrushes.Black, rect, XStringFormats.TopLeft); + tf.DrawString(TestData.LoremIpsumText, font, XBrushes.Black, rect); } } } \ No newline at end of file diff --git a/PdfSharpCore/Drawing.Layout/XTextFormatter.cs b/PdfSharpCore/Drawing.Layout/XTextFormatter.cs index 5701c5f9..c3367059 100644 --- a/PdfSharpCore/Drawing.Layout/XTextFormatter.cs +++ b/PdfSharpCore/Drawing.Layout/XTextFormatter.cs @@ -138,13 +138,17 @@ public void SetAlignment(TextFormatAlignment alignments) /// The font. /// The text brush. /// The layout rectangle. + /// The line height. public void DrawString(string text, XFont font, XBrush brush, XRect layoutRectangle, XUnit? lineHeight = null) { - DrawString(text, font, brush, layoutRectangle, XStringFormats.TopLeft, lineHeight); + DrawString(text, font, brush, layoutRectangle, new TextFormatAlignment() + { + Horizontal = XParagraphAlignment.Justify, Vertical = XVerticalAlignment.Top + }, lineHeight); } /// - /// Draws the text. + /// Get the layout rectangle required. /// /// The text to be drawn. /// The font. @@ -184,24 +188,20 @@ public XRect GetLayout(string text, XFont font, XBrush brush, XRect layoutRectan /// The font. /// The text brush. /// The layout rectangle. - /// The format. Must be XStringFormat.TopLeft - /// The height of each line - public void DrawString(string text, XFont font, XBrush brush, XRect layoutRectangle, XStringFormat format, + /// The alignments. + /// The height of each line. + public void DrawString(string text, XFont font, XBrush brush, XRect layoutRectangle, TextFormatAlignment alignments, XUnit? lineHeight = null) { - if (format == null) - throw new ArgumentNullException(nameof(format)); + if (alignments == null) + throw new ArgumentNullException(nameof(alignments)); if (text.Length == 0) return; GetLayout(text, font, brush, layoutRectangle, lineHeight); - SetAlignment(new TextFormatAlignment() - { - Horizontal = GetHorizontalAlignmentFromFormat(format), - Vertical = GetVerticalAlignmentFromFormat(format) - }); + SetAlignment(alignments); double dx = layoutRectangle.Location.X; double dy = layoutRectangle.Location.Y; @@ -210,7 +210,7 @@ public void DrawString(string text, XFont font, XBrush brush, XRect layoutRectan if (VerticalAlignment == XVerticalAlignment.Middle) { - dy += (layoutRectangle.Height - _layoutRectangle.Height + _lineHeight) / 2; + dy += (layoutRectangle.Height - _layoutRectangle.Height) / 2; } else if (VerticalAlignment == XVerticalAlignment.Bottom) { @@ -221,16 +221,26 @@ public void DrawString(string text, XFont font, XBrush brush, XRect layoutRectan int count = _blocks.Count; foreach (var line in lines) { - if (Alignment != XParagraphAlignment.Justify) + var lineBlocks = line as Block[] ?? line.ToArray(); + if (Alignment == XParagraphAlignment.Justify) + { + var locationX = dx; + var gapSize = (layoutRectangle.Width - lineBlocks.Select(l => l.Width).Sum())/ (lineBlocks.Count() - 1); + foreach (var block in lineBlocks) + { + _gfx.DrawString(block.Text.Trim(), font, brush, locationX, dy + lineBlocks.First().Location.Y, XStringFormats.TopLeft); + locationX += block.Width + gapSize; + } + } + else { - var enumerable = line as Block[] ?? line.ToArray(); - var lineText = string.Join(" ", enumerable.Select(l => l.Text)); + var lineText = string.Join(" ", lineBlocks.Select(l => l.Text)); var locationX = dx; - if (format.Alignment == XStringAlignment.Center) + if (Alignment == XParagraphAlignment.Center) locationX = dx + layoutRectangle.Width / 2; - if (format.Alignment == XStringAlignment.Far) + if (Alignment == XParagraphAlignment.Right) locationX += layoutRectangle.Width; - _gfx.DrawString(lineText, font, brush, locationX, dy + enumerable.First().Location.Y, format); + _gfx.DrawString(lineText, font, brush, locationX, dy + lineBlocks.First().Location.Y, GetXStringFormat()); } } } @@ -420,33 +430,19 @@ void HorizontalAlignLine(int firstIndex, int lastIndex, double layoutWidth) // - super- and sub-script // - ... - private static XParagraphAlignment GetHorizontalAlignmentFromFormat(XStringFormat format) - { - switch (format.Alignment) - { - case XStringAlignment.Center: - return XParagraphAlignment.Center; - case XStringAlignment.Near: - return XParagraphAlignment.Left; - case XStringAlignment.Far: - return XParagraphAlignment.Right; - } - - return XParagraphAlignment.Justify; - } - - private static XVerticalAlignment GetVerticalAlignmentFromFormat(XStringFormat format) + private XStringFormat GetXStringFormat() { - switch (format.LineAlignment) + switch (Alignment) { - case XLineAlignment.Center: - return XVerticalAlignment.Middle; - case XLineAlignment.BaseLine: - case XLineAlignment.Far: - return XVerticalAlignment.Bottom; - case XLineAlignment.Near: + case XParagraphAlignment.Center: + return XStringFormats.TopCenter; + case XParagraphAlignment.Right: + return XStringFormats.TopRight; + case XParagraphAlignment.Default: + case XParagraphAlignment.Justify: + case XParagraphAlignment.Left: default: - return XVerticalAlignment.Top; + return XStringFormats.TopLeft; } } } diff --git a/SampleApp/Program.cs b/SampleApp/Program.cs index ff064b19..b012216a 100644 --- a/SampleApp/Program.cs +++ b/SampleApp/Program.cs @@ -1,4 +1,8 @@ - +using PdfSharpCore.Drawing; +using PdfSharpCore.Drawing.Layout; +using PdfSharpCore.Drawing.Layout.enums; +using PdfSharpCore.Pdf; + namespace SampleApp { From 668d423e212c40f01560c0b8cc8b2cab0f8b6975 Mon Sep 17 00:00:00 2001 From: Raul Ruiz Date: Thu, 25 Jul 2024 17:40:14 +0200 Subject: [PATCH 4/4] FIX: Code cleanup --- SampleApp/Program.cs | 46 ++++++++++++++++++++++++++++---------------- 1 file changed, 29 insertions(+), 17 deletions(-) diff --git a/SampleApp/Program.cs b/SampleApp/Program.cs index b012216a..2a049427 100644 --- a/SampleApp/Program.cs +++ b/SampleApp/Program.cs @@ -37,26 +37,26 @@ public static void Main(string[] args) const string outName = "test1.pdf"; - PdfSharpCore.Pdf.PdfDocument? document = new PdfSharpCore.Pdf.PdfDocument(); + PdfDocument? document = new PdfDocument(); - PdfSharpCore.Pdf.PdfPage? pageNewRenderer = document.AddPage(); + PdfPage? pageNewRenderer = document.AddPage(); - PdfSharpCore.Drawing.XGraphics? renderer = PdfSharpCore.Drawing.XGraphics.FromPdfPage(pageNewRenderer); + XGraphics? renderer = XGraphics.FromPdfPage(pageNewRenderer); renderer.DrawString( "Testy Test Test" - , new PdfSharpCore.Drawing.XFont("Arial", 12) - , PdfSharpCore.Drawing.XBrushes.Black - , new PdfSharpCore.Drawing.XPoint(12, 12) + , new XFont("Arial", 12) + , XBrushes.Black + , new XPoint(12, 12) ); - PdfSharpCore.Drawing.Layout.XTextFormatter? formatter = new PdfSharpCore.Drawing.Layout.XTextFormatter(renderer); + XTextFormatter? formatter = new XTextFormatter(renderer); - var font = new PdfSharpCore.Drawing.XFont("Arial", 12); - var brush = PdfSharpCore.Drawing.XBrushes.Black; + var font = new XFont("Arial", 12); + var brush = XBrushes.Black; formatter.AllowVerticalOverflow = true; - var originalLayout = new PdfSharpCore.Drawing.XRect(0, 30, 120, 120); + var originalLayout = new XRect(0, 30, 120, 120); var text = "More and more text boxes to show alignment capabilities"; // " with addipional gline"; var anotherText = "Text to determine the size of the box I would like to place the text I'm goint to test"; @@ -65,21 +65,33 @@ public static void Main(string[] args) font, brush, originalLayout); - rect.Location = new PdfSharpCore.Drawing.XPoint(50, 50); + rect.Location = new XPoint(50, 50); formatter.AllowVerticalOverflow = false; - // Draw the string + // Prepare brush to draw the box that demostrates the text fits and aligns correctly + var translucentBrush = new XSolidBrush(XColor.FromArgb(20, 0, 0, 0)); + + // Draw the string with default alignments formatter.DrawString( text, font, brush, - rect, - PdfSharpCore.Drawing.XStringFormats.BottomRight + rect ); + // For checking purposes + renderer.DrawRectangle(translucentBrush, rect); + + rect.Location = new XPoint(300, 50); + + // Draw the string with custom alignments + formatter.DrawString(text, font, brush, rect, new TextFormatAlignment() + { + Horizontal = XParagraphAlignment.Center, + Vertical = XVerticalAlignment.Middle + }); - // Draw the box to check that the text fits and aligns correctly - var transparentBrush = new PdfSharpCore.Drawing.XSolidBrush(PdfSharpCore.Drawing.XColor.FromArgb(20, brush.Color.R, brush.Color.G, brush.Color.B)); - renderer.DrawRectangle(transparentBrush, rect); + // For checking purposes + renderer.DrawRectangle(translucentBrush, rect); SaveDocument(document, outName);