From 64456d2a713e47e2d291bfbe9fa7e841a3e28eb2 Mon Sep 17 00:00:00 2001 From: Chris Anderson Date: Fri, 29 Aug 2025 20:00:56 +0100 Subject: [PATCH 1/3] Proposed fix for bug #481 --- PdfSharpCore/Drawing.Layout/XTextFormatter.cs | 14 +++++++---- SampleApp/Program.cs | 23 ++++++++++++++++++- 2 files changed, 32 insertions(+), 5 deletions(-) diff --git a/PdfSharpCore/Drawing.Layout/XTextFormatter.cs b/PdfSharpCore/Drawing.Layout/XTextFormatter.cs index c336705..01c292a 100644 --- a/PdfSharpCore/Drawing.Layout/XTextFormatter.cs +++ b/PdfSharpCore/Drawing.Layout/XTextFormatter.cs @@ -219,16 +219,22 @@ public void DrawString(string text, XFont font, XBrush brush, XRect layoutRectan } int count = _blocks.Count; - foreach (var line in lines) + int lineCount = lines.Length; + for (var lineIndex = 0; lineIndex < lineCount; lineIndex++) { + var line = lines[lineIndex]; var lineBlocks = line as Block[] ?? line.ToArray(); - if (Alignment == XParagraphAlignment.Justify) + + // If alignment is Justify, leave last line to be Left justified + if (Alignment == XParagraphAlignment.Justify && lineIndex != lineCount - 1) { var locationX = dx; - var gapSize = (layoutRectangle.Width - lineBlocks.Select(l => l.Width).Sum())/ (lineBlocks.Count() - 1); + 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); + _gfx.DrawString(block.Text.Trim(), font, brush, locationX, dy + lineBlocks.First().Location.Y, + XStringFormats.TopLeft); locationX += block.Width + gapSize; } } diff --git a/SampleApp/Program.cs b/SampleApp/Program.cs index 2a04942..a267404 100644 --- a/SampleApp/Program.cs +++ b/SampleApp/Program.cs @@ -92,7 +92,28 @@ public static void Main(string[] args) // For checking purposes renderer.DrawRectangle(translucentBrush, rect); - + + + + var justifyLayout = new XRect(0, 30, 350, 520); + var justifytext = + "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum eleifend nibh at luctus posuere. Donec dictum elit ex, id consequat risus aliquam sit amet. Aenean ac nunc et massa vulputate rutrum."; + + var justifyrect = formatter.GetLayout( + justifytext, + font, + brush, + justifyLayout); + justifyrect.Location = new XPoint(100, 150); + // Draw the string with justify alignment + formatter.DrawString(justifytext, font, brush, justifyrect, new TextFormatAlignment() + { + Horizontal = XParagraphAlignment.Justify, + Vertical = XVerticalAlignment.Middle + }); + // For checking purposes + renderer.DrawRectangle(translucentBrush, justifyrect); + SaveDocument(document, outName); System.Console.WriteLine("Done!"); From 212bb247f1d31cdfaeda1c4c123c206cb6e93dc3 Mon Sep 17 00:00:00 2001 From: Chris Anderson Date: Sat, 27 Sep 2025 02:07:42 +0100 Subject: [PATCH 2/3] Left align last lines within multi-paragraph blocks --- PdfSharpCore/Drawing.Layout/XTextFormatter.cs | 4 +- SampleApp/Program.cs | 59 +++++++++++++------ 2 files changed, 44 insertions(+), 19 deletions(-) diff --git a/PdfSharpCore/Drawing.Layout/XTextFormatter.cs b/PdfSharpCore/Drawing.Layout/XTextFormatter.cs index 01c292a..73bb24e 100644 --- a/PdfSharpCore/Drawing.Layout/XTextFormatter.cs +++ b/PdfSharpCore/Drawing.Layout/XTextFormatter.cs @@ -225,8 +225,8 @@ public void DrawString(string text, XFont font, XBrush brush, XRect layoutRectan var line = lines[lineIndex]; var lineBlocks = line as Block[] ?? line.ToArray(); - // If alignment is Justify, leave last line to be Left justified - if (Alignment == XParagraphAlignment.Justify && lineIndex != lineCount - 1) + // If alignment is Justify, leave last paragraph lines to be Left justified + if (Alignment == XParagraphAlignment.Justify && lineIndex != lineCount - 1 && lineBlocks.First().Alignment == XParagraphAlignment.Default) { var locationX = dx; var gapSize = (layoutRectangle.Width - lineBlocks.Select(l => l.Width).Sum()) / diff --git a/SampleApp/Program.cs b/SampleApp/Program.cs index a267404..ca9d3bd 100644 --- a/SampleApp/Program.cs +++ b/SampleApp/Program.cs @@ -93,33 +93,58 @@ public static void Main(string[] args) // For checking purposes renderer.DrawRectangle(translucentBrush, rect); + //////////////// + // Use a new formatter to get rid of any cached values from above + formatter = new XTextFormatter(renderer); + formatter.AllowVerticalOverflow = true; + formatter.Alignment = XParagraphAlignment.Justify; + formatter.VerticalAlignment = XVerticalAlignment.Top; + + + // Test justified text without line breaks + text = @"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non sapien leo. Sed lacinia velit ex, id auctor tellus varius a. Cras scelerisque in risus vitae hendrerit. Duis venenatis felis in lacinia vestibulum. Proin mauris ex, efficitur nec tincidunt in, imperdiet eget risus. Nulla porttitor mollis pellentesque."; + + // Define the area to draw the text + rect = new XRect(0, 0, 310, 200); + // Get the actual layout rectangle + rect = formatter.GetLayout(text, font, brush, rect); + // Move it to the desired location + rect.Location = new XPoint(40, 150); - var justifyLayout = new XRect(0, 30, 350, 520); - var justifytext = - "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum eleifend nibh at luctus posuere. Donec dictum elit ex, id consequat risus aliquam sit amet. Aenean ac nunc et massa vulputate rutrum."; - - var justifyrect = formatter.GetLayout( - justifytext, - font, - brush, - justifyLayout); - justifyrect.Location = new XPoint(100, 150); // Draw the string with justify alignment - formatter.DrawString(justifytext, font, brush, justifyrect, new TextFormatAlignment() - { - Horizontal = XParagraphAlignment.Justify, - Vertical = XVerticalAlignment.Middle - }); + formatter.DrawString(text, font, brush, rect); + // For checking purposes + renderer.DrawRectangle(translucentBrush, rect); + + //////////////// + + // Test justified text with line breaks + // First paragraph should be laid out identically to the one above + text = @"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non sapien leo. Sed lacinia velit ex, id auctor tellus varius a. Cras scelerisque in risus vitae hendrerit. Duis venenatis felis in lacinia vestibulum. Proin mauris ex, efficitur nec tincidunt in, imperdiet eget risus. Nulla porttitor mollis pellentesque. + +Maecenas mollis sollicitudin felis at imperdiet. Duis dignissim purus quis interdum mattis. Nam sit amet quam quis enim hendrerit tincidunt. Aliquam euismod metus justo, non lobortis risus vehicula in. Pellentesque tempus, leo at placerat interdum, diam lectus gravida purus, id placerat justo quam nec mauris."; + + // Define the area to draw the text + rect = new XRect(0, 0, 310, 200); + // Get the actual layout rectangle + rect = formatter.GetLayout(text, font, brush, rect); + // Move it to the desired location + rect.Location = new XPoint(40, 280); + + // Draw the string with justify alignment + formatter.DrawString(text, font, brush, rect); // For checking purposes - renderer.DrawRectangle(translucentBrush, justifyrect); + renderer.DrawRectangle(translucentBrush, rect); + + + SaveDocument(document, outName); System.Console.WriteLine("Done!"); } // End Sub Main - } // End Class Program From 3c4ed9ddf025f661c4508deaf1f4eb77d3602f01 Mon Sep 17 00:00:00 2001 From: Chris Anderson Date: Sat, 27 Sep 2025 02:08:58 +0100 Subject: [PATCH 3/3] Ignore null block text when drawing --- PdfSharpCore/Drawing.Layout/XTextFormatter.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/PdfSharpCore/Drawing.Layout/XTextFormatter.cs b/PdfSharpCore/Drawing.Layout/XTextFormatter.cs index 73bb24e..749139c 100644 --- a/PdfSharpCore/Drawing.Layout/XTextFormatter.cs +++ b/PdfSharpCore/Drawing.Layout/XTextFormatter.cs @@ -229,12 +229,12 @@ public void DrawString(string text, XFont font, XBrush brush, XRect layoutRectan if (Alignment == XParagraphAlignment.Justify && lineIndex != lineCount - 1 && lineBlocks.First().Alignment == XParagraphAlignment.Default) { var locationX = dx; - var gapSize = (layoutRectangle.Width - lineBlocks.Select(l => l.Width).Sum()) / - (lineBlocks.Count() - 1); + var gapSize = (layoutRectangle.Width - lineBlocks.Select(l => l.Width).Sum()) / + (lineBlocks.Count(b => b.Width != 0) - 1); foreach (var block in lineBlocks) { - _gfx.DrawString(block.Text.Trim(), font, brush, locationX, dy + lineBlocks.First().Location.Y, - XStringFormats.TopLeft); + if(block.Width != 0) + _gfx.DrawString(block.Text.Trim(), font, brush, locationX, dy + lineBlocks.First().Location.Y, XStringFormats.TopLeft); locationX += block.Width + gapSize; } }