Skip to content

Commit d6ac8b0

Browse files
authored
Merge pull request #450 from raul-ruiz-gallardo/feature/text-formatter-size-calculation
Feature/text formatter size calculation and custom alignments
2 parents 314c063 + 668d423 commit d6ac8b0

File tree

4 files changed

+143
-28
lines changed

4 files changed

+143
-28
lines changed

PdfSharpCore.Test/IO/LargePDFReadWrite.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ private void AddAPage(PdfDocument document, XFont font)
5353
var height = page.Height.Value - 50 - y;
5454
var rect = new XRect(40, 50, width, height);
5555
renderer.DrawRectangle(XBrushes.SeaShell, rect);
56-
tf.DrawString(TestData.LoremIpsumText, font, XBrushes.Black, rect, XStringFormats.TopLeft);
56+
tf.DrawString(TestData.LoremIpsumText, font, XBrushes.Black, rect);
5757
}
5858
}
5959
}

PdfSharpCore/Drawing.Layout/XTextFormatter.cs

Lines changed: 86 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -138,29 +138,32 @@ public void SetAlignment(TextFormatAlignment alignments)
138138
/// <param name="font">The font.</param>
139139
/// <param name="brush">The text brush.</param>
140140
/// <param name="layoutRectangle">The layout rectangle.</param>
141+
/// <param name="lineHeight">The line height.</param>
141142
public void DrawString(string text, XFont font, XBrush brush, XRect layoutRectangle, XUnit? lineHeight = null)
142143
{
143-
DrawString(text, font, brush, layoutRectangle, XStringFormats.TopLeft, lineHeight);
144+
DrawString(text, font, brush, layoutRectangle, new TextFormatAlignment()
145+
{
146+
Horizontal = XParagraphAlignment.Justify, Vertical = XVerticalAlignment.Top
147+
}, lineHeight);
144148
}
145149

146150
/// <summary>
147-
/// Draws the text.
151+
/// Get the layout rectangle required.
148152
/// </summary>
149153
/// <param name="text">The text to be drawn.</param>
150154
/// <param name="font">The font.</param>
151155
/// <param name="brush">The text brush.</param>
152156
/// <param name="layoutRectangle">The layout rectangle.</param>
153-
/// <param name="format">The format. Must be <c>XStringFormat.TopLeft</c></param>
154-
public void DrawString(string text, XFont font, XBrush brush, XRect layoutRectangle, XStringFormat format, XUnit? lineHeight = null)
157+
/// <param name="lineHeight">The height of each line</param>
158+
public XRect GetLayout(string text, XFont font, XBrush brush, XRect layoutRectangle,
159+
XUnit? lineHeight = null)
155160
{
156161
if (text == null)
157162
throw new ArgumentNullException("text");
158163
if (font == null)
159164
throw new ArgumentNullException("font");
160165
if (brush == null)
161166
throw new ArgumentNullException("brush");
162-
if (format.Alignment != XStringAlignment.Near || format.LineAlignment != XLineAlignment.Near)
163-
throw new ArgumentException("Only TopLeft alignment is currently implemented.");
164167

165168
Text = text;
166169
Font = font;
@@ -169,36 +172,84 @@ public void DrawString(string text, XFont font, XBrush brush, XRect layoutRectan
169172
_lineHeight = lineHeight?.Point ?? _lineSpace;
170173

171174
if (text.Length == 0)
172-
return;
175+
return new XRect(layoutRectangle.Location.X, layoutRectangle.Location.Y, 0, 0);
173176

174177
CreateBlocks();
175178

176179
CreateLayout();
177180

181+
return _layoutRectangle;
182+
}
183+
184+
/// <summary>
185+
/// Draws the text.
186+
/// </summary>
187+
/// <param name="text">The text to be drawn.</param>
188+
/// <param name="font">The font.</param>
189+
/// <param name="brush">The text brush.</param>
190+
/// <param name="layoutRectangle">The layout rectangle.</param>
191+
/// <param name="alignments">The alignments.</c></param>
192+
/// <param name="lineHeight">The height of each line.</param>
193+
public void DrawString(string text, XFont font, XBrush brush, XRect layoutRectangle, TextFormatAlignment alignments,
194+
XUnit? lineHeight = null)
195+
{
196+
if (alignments == null)
197+
throw new ArgumentNullException(nameof(alignments));
198+
199+
if (text.Length == 0)
200+
return;
201+
202+
GetLayout(text, font, brush, layoutRectangle, lineHeight);
203+
204+
SetAlignment(alignments);
205+
178206
double dx = layoutRectangle.Location.X;
179-
double dy = layoutRectangle.Location.Y + _cyAscent;
180-
207+
double dy = layoutRectangle.Location.Y;
208+
209+
var lines = GetLines(_blocks).ToArray();
210+
181211
if (VerticalAlignment == XVerticalAlignment.Middle)
182212
{
183-
dy += layoutRectangle.Height / 2 - _layoutRectangle.Height / 2 - _cyDescent;
213+
dy += (layoutRectangle.Height - _layoutRectangle.Height) / 2;
184214
}
185215
else if (VerticalAlignment == XVerticalAlignment.Bottom)
186216
{
187-
dy = layoutRectangle.Location.Y + layoutRectangle.Height - _layoutRectangle.Height + _lineHeight - _cyDescent;
217+
dy = layoutRectangle.Location.Y + layoutRectangle.Height - _layoutRectangle.Height + _lineHeight -
218+
_cyDescent;
188219
}
189-
220+
190221
int count = _blocks.Count;
191-
for (int idx = 0; idx < count; idx++)
222+
foreach (var line in lines)
192223
{
193-
Block block = _blocks[idx];
194-
if (block.Stop)
195-
break;
196-
if (block.Type == BlockType.LineBreak)
197-
continue;
198-
_gfx.DrawString(block.Text, font, brush, dx + block.Location.X, dy + block.Location.Y);
224+
var lineBlocks = line as Block[] ?? line.ToArray();
225+
if (Alignment == XParagraphAlignment.Justify)
226+
{
227+
var locationX = dx;
228+
var gapSize = (layoutRectangle.Width - lineBlocks.Select(l => l.Width).Sum())/ (lineBlocks.Count() - 1);
229+
foreach (var block in lineBlocks)
230+
{
231+
_gfx.DrawString(block.Text.Trim(), font, brush, locationX, dy + lineBlocks.First().Location.Y, XStringFormats.TopLeft);
232+
locationX += block.Width + gapSize;
233+
}
234+
}
235+
else
236+
{
237+
var lineText = string.Join(" ", lineBlocks.Select(l => l.Text));
238+
var locationX = dx;
239+
if (Alignment == XParagraphAlignment.Center)
240+
locationX = dx + layoutRectangle.Width / 2;
241+
if (Alignment == XParagraphAlignment.Right)
242+
locationX += layoutRectangle.Width;
243+
_gfx.DrawString(lineText, font, brush, locationX, dy + lineBlocks.First().Location.Y, GetXStringFormat());
244+
}
199245
}
200246
}
201247

248+
private static IEnumerable<IEnumerable<Block>> GetLines(List<Block> blocks)
249+
{
250+
return blocks.GroupBy(b => b.Location.Y);
251+
}
252+
202253
void CreateBlocks()
203254
{
204255
_blocks.Clear();
@@ -378,6 +429,22 @@ void HorizontalAlignLine(int firstIndex, int lastIndex, double layoutWidth)
378429
// - underline and strike-out variation
379430
// - super- and sub-script
380431
// - ...
432+
433+
private XStringFormat GetXStringFormat()
434+
{
435+
switch (Alignment)
436+
{
437+
case XParagraphAlignment.Center:
438+
return XStringFormats.TopCenter;
439+
case XParagraphAlignment.Right:
440+
return XStringFormats.TopRight;
441+
case XParagraphAlignment.Default:
442+
case XParagraphAlignment.Justify:
443+
case XParagraphAlignment.Left:
444+
default:
445+
return XStringFormats.TopLeft;
446+
}
447+
}
381448
}
382449

383450
public class TextFormatAlignment

PdfSharpCore/PdfSharpCore.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ PdfSharpCore is a partial port of PdfSharp.Xamarin for .NET Core Additionally Mi
1818
<summary>PdfSharp for .NET Core</summary>
1919
<IsTrimmable>true</IsTrimmable>
2020
<EnableTrimAnalyzer>true</EnableTrimAnalyzer>
21+
<Version>1.0.1</Version>
2122
</PropertyGroup>
2223

2324
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">

SampleApp/Program.cs

Lines changed: 55 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1-

1+
using PdfSharpCore.Drawing;
2+
using PdfSharpCore.Drawing.Layout;
3+
using PdfSharpCore.Drawing.Layout.enums;
4+
using PdfSharpCore.Pdf;
5+
26
namespace SampleApp
37
{
48

@@ -33,19 +37,62 @@ public static void Main(string[] args)
3337

3438
const string outName = "test1.pdf";
3539

36-
PdfSharpCore.Pdf.PdfDocument? document = new PdfSharpCore.Pdf.PdfDocument();
40+
PdfDocument? document = new PdfDocument();
3741

38-
PdfSharpCore.Pdf.PdfPage? pageNewRenderer = document.AddPage();
42+
PdfPage? pageNewRenderer = document.AddPage();
3943

40-
PdfSharpCore.Drawing.XGraphics? renderer = PdfSharpCore.Drawing.XGraphics.FromPdfPage(pageNewRenderer);
44+
XGraphics? renderer = XGraphics.FromPdfPage(pageNewRenderer);
4145

4246
renderer.DrawString(
43-
"Testy Test Test"
44-
, new PdfSharpCore.Drawing.XFont("Arial", 12)
45-
, PdfSharpCore.Drawing.XBrushes.Black
46-
, new PdfSharpCore.Drawing.XPoint(12, 12)
47+
"Testy Test Test"
48+
, new XFont("Arial", 12)
49+
, XBrushes.Black
50+
, new XPoint(12, 12)
51+
);
52+
53+
XTextFormatter? formatter = new XTextFormatter(renderer);
54+
55+
var font = new XFont("Arial", 12);
56+
var brush = XBrushes.Black;
57+
58+
formatter.AllowVerticalOverflow = true;
59+
var originalLayout = new XRect(0, 30, 120, 120);
60+
var text = "More and more text boxes to show alignment capabilities"; // " with addipional gline";
61+
var anotherText =
62+
"Text to determine the size of the box I would like to place the text I'm goint to test";
63+
var rect = formatter.GetLayout(
64+
anotherText,
65+
font,
66+
brush,
67+
originalLayout);
68+
rect.Location = new XPoint(50, 50);
69+
formatter.AllowVerticalOverflow = false;
70+
71+
// Prepare brush to draw the box that demostrates the text fits and aligns correctly
72+
var translucentBrush = new XSolidBrush(XColor.FromArgb(20, 0, 0, 0));
73+
74+
// Draw the string with default alignments
75+
formatter.DrawString(
76+
text,
77+
font,
78+
brush,
79+
rect
4780
);
81+
// For checking purposes
82+
renderer.DrawRectangle(translucentBrush, rect);
4883

84+
rect.Location = new XPoint(300, 50);
85+
86+
// Draw the string with custom alignments
87+
formatter.DrawString(text, font, brush, rect, new TextFormatAlignment()
88+
{
89+
Horizontal = XParagraphAlignment.Center,
90+
Vertical = XVerticalAlignment.Middle
91+
});
92+
93+
// For checking purposes
94+
renderer.DrawRectangle(translucentBrush, rect);
95+
4996
SaveDocument(document, outName);
5097

5198
System.Console.WriteLine("Done!");

0 commit comments

Comments
 (0)