JsonStatham JsonStatham - 23 days ago 6
C# Question

Positioning data using PDFsharp

I'm using PDFsharp to produce multiple page documents for each item in my model.

I have been given a rough mock-up of what the final PDF needs to look like:

enter image description here

To cut a long story short I have two different types of PDF I must produce, the one that I need to follow the template for is a multiple page PDF hence the variable

_numberOfPages
in the below code snippet.

I have got as far as actually producing information for each individual item for the multiple page PDF version. Depending on the number of items in the request I add a new page and then get the index of my items array for the current page and append the lines like that.

PdfDocument Class

public PdfDocument BuildPdfDocument()
{
if (_numberOfPages > 1)
{
PdfDocument pdfDocument = new PdfDocument();
for (int i = 0; i < _numberOfPages; i++)
{
PdfPage page = pdfDocument.AddPage();
page.Orientation = PageOrientation.Landscape;
XGraphics gfx = XGraphics.FromPdfPage(page);
XFont font = new XFont("Arial", 30, XFontStyle.Regular);
XTextFormatter tf = new XTextFormatter(gfx);
XRect rect = new XRect(0, (page.Height / 4), page.Width, page.Height);
gfx.DrawRectangle(XBrushes.White, rect);
tf.Alignment = XParagraphAlignment.Left;
tf.DrawString(_request.GetText(i), font, XBrushes.Black, rect);
}

return pdfDocument;
}

else
{
// stuff relating to the other pdfDocument that is OK
}
}


FileRequest class contains a method called GetText

public string GetText(int pageNo)
{
int[] fileRequestItems = this.FileRequestItems.Select(x => x.Id).ToArray();
var currentFileRequestItem = fileRequestItems[pageNo];
var items = FileRequestItems;

FileRequestItem item = items.FirstOrDefault(s => s.Id == currentFileRequestItem);

StringBuilder sb = new StringBuilder();

if (item.ObjectContextGetType() == typeof(FileRequestDocument)) // its a document so provide text for the PDF like this
{
var fileRequestDocument = item as FileRequestDocument;

sb.AppendLine("Date Requested: " + this.DateOfRequest.ToString("D"));
if (fileRequestDocument != null) sb.AppendLine("Box No: " + fileRequestDocument.Record.Box.BoxNumber);
sb.AppendLine("Location: " + this.Location.LocationName);
if (fileRequestDocument != null) sb.AppendLine("Description: " + fileRequestDocument.Record.Description);
sb.AppendLine("Requested By: " + this.Name);
sb.AppendLine("Tel: " + this.TelephoneNumber);
sb.AppendLine("Department: " + this.Department);
}

if (item.ObjectContextGetType() == typeof(FileRequestBox)) // its a box so provide text for the PDF like this
{
var fileRequestBox = item as FileRequestBox;

sb.AppendLine("Date Requested: " + this.DateOfRequest.ToString("D"));
if (fileRequestBox != null) sb.AppendLine("Box No: " + fileRequestBox.Box.BoxNumber);
sb.AppendLine("Location: " + this.Location.LocationName);
if (fileRequestBox != null) sb.AppendLine("Description: " + $"Entire Contents of Box Number {fileRequestBox.Box.BoxNumber}");
sb.AppendLine("Requested By: " + this.Name);
sb.AppendLine("Tel: " + this.TelephoneNumber);
sb.AppendLine("Department: " + this.Department);
}

return sb.ToString();
}


The result of all this is the below PDF, a file request which contains 5 items will be over 5 pages, with stuff like the Name/Date etc repeated but the description unique to that
FileRequestItem
or page.

enter image description here

As you can see, although the information is all there, the positioning does not match the required template.

How do I create the specified template or something similar for each page, and then position my data accordingly?

Answer

Nice try to pass a long multi-line string to XTextFormatter. Wrong approach if you need control about the positions.

To get full control over the positioning, call DrawString for individual items.

I would use MigraDoc: MigraDoc allows you to create a table and put the information in individual table cells.

MigraDoc uses PDFsharp to create PDF files, but offers a higher API level.
http://www.pdfsharp.net/wiki/Invoice-sample.ashx