Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/text formatter size calculation and custom alignments #450

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion PdfSharpCore.Test/IO/LargePDFReadWrite.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
}
105 changes: 86 additions & 19 deletions PdfSharpCore/Drawing.Layout/XTextFormatter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -138,29 +138,32 @@ public void SetAlignment(TextFormatAlignment alignments)
/// <param name="font">The font.</param>
/// <param name="brush">The text brush.</param>
/// <param name="layoutRectangle">The layout rectangle.</param>
/// <param name="lineHeight">The line height.</param>
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);
}

/// <summary>
/// Draws the text.
/// Get the layout rectangle required.
/// </summary>
/// <param name="text">The text to be drawn.</param>
/// <param name="font">The font.</param>
/// <param name="brush">The text brush.</param>
/// <param name="layoutRectangle">The layout rectangle.</param>
/// <param name="format">The format. Must be <c>XStringFormat.TopLeft</c></param>
public void DrawString(string text, XFont font, XBrush brush, XRect layoutRectangle, XStringFormat format, XUnit? lineHeight = null)
/// <param name="lineHeight">The height of each line</param>
public XRect GetLayout(string text, XFont font, XBrush brush, XRect layoutRectangle,
XUnit? lineHeight = null)
{
if (text == null)
throw new ArgumentNullException("text");
if (font == null)
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;
Expand All @@ -169,36 +172,84 @@ 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;
}

/// <summary>
/// Draws the text.
/// </summary>
/// <param name="text">The text to be drawn.</param>
/// <param name="font">The font.</param>
/// <param name="brush">The text brush.</param>
/// <param name="layoutRectangle">The layout rectangle.</param>
/// <param name="alignments">The alignments.</c></param>
/// <param name="lineHeight">The height of each line.</param>
public void DrawString(string text, XFont font, XBrush brush, XRect layoutRectangle, TextFormatAlignment alignments,
XUnit? lineHeight = null)
{
if (alignments == null)
throw new ArgumentNullException(nameof(alignments));

if (text.Length == 0)
return;

GetLayout(text, font, brush, layoutRectangle, lineHeight);

SetAlignment(alignments);

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) / 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);
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 lineText = string.Join(" ", lineBlocks.Select(l => l.Text));
var locationX = dx;
if (Alignment == XParagraphAlignment.Center)
locationX = dx + layoutRectangle.Width / 2;
if (Alignment == XParagraphAlignment.Right)
locationX += layoutRectangle.Width;
_gfx.DrawString(lineText, font, brush, locationX, dy + lineBlocks.First().Location.Y, GetXStringFormat());
}
}
}

private static IEnumerable<IEnumerable<Block>> GetLines(List<Block> blocks)
{
return blocks.GroupBy(b => b.Location.Y);
}

void CreateBlocks()
{
_blocks.Clear();
Expand Down Expand Up @@ -378,6 +429,22 @@ void HorizontalAlignLine(int firstIndex, int lastIndex, double layoutWidth)
// - underline and strike-out variation
// - super- and sub-script
// - ...

private XStringFormat GetXStringFormat()
{
switch (Alignment)
{
case XParagraphAlignment.Center:
return XStringFormats.TopCenter;
case XParagraphAlignment.Right:
return XStringFormats.TopRight;
case XParagraphAlignment.Default:
case XParagraphAlignment.Justify:
case XParagraphAlignment.Left:
default:
return XStringFormats.TopLeft;
}
}
}

public class TextFormatAlignment
Expand Down
1 change: 1 addition & 0 deletions PdfSharpCore/PdfSharpCore.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ PdfSharpCore is a partial port of PdfSharp.Xamarin for .NET Core Additionally Mi
<summary>PdfSharp for .NET Core</summary>
<IsTrimmable>true</IsTrimmable>
<EnableTrimAnalyzer>true</EnableTrimAnalyzer>
<Version>1.0.1</Version>
</PropertyGroup>

<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
Expand Down
63 changes: 55 additions & 8 deletions SampleApp/Program.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@

using PdfSharpCore.Drawing;
using PdfSharpCore.Drawing.Layout;
using PdfSharpCore.Drawing.Layout.enums;
using PdfSharpCore.Pdf;

namespace SampleApp
{

Expand Down Expand Up @@ -33,19 +37,62 @@ 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)
"Testy Test Test"
, new XFont("Arial", 12)
, XBrushes.Black
, new XPoint(12, 12)
);

XTextFormatter? formatter = new XTextFormatter(renderer);

var font = new XFont("Arial", 12);
var brush = XBrushes.Black;

formatter.AllowVerticalOverflow = true;
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";
var rect = formatter.GetLayout(
anotherText,
font,
brush,
originalLayout);
rect.Location = new XPoint(50, 50);
formatter.AllowVerticalOverflow = false;

// 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
);
// 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
});

// For checking purposes
renderer.DrawRectangle(translucentBrush, rect);

SaveDocument(document, outName);

System.Console.WriteLine("Done!");
Expand Down