Recently, one of my projects called for a PDF generation tool. I was developing an MVC web application that allowed a user to fill out a web form. The requirements stated that the user response should be saved in a PDF document for evidence, in case the date requested didn’t turn out the way it was supposed to.
PDF generation using MigraDoc
So that I don't violate my non-disclosure agreement, let’s just say it was a dating app that allowed users to enter characteristics of their own, along with some characteristics they would like their date to have.
Something like this:
Take note of the two columns and the unequal tables within, they will be important later on!
Choosing a pdf generator
After quickly searching for NuGet packages that implemented PDF generation, I realised I could go with one of two approaches: either render a view to a PDF document or generate a PDF document using code, possibly using some sort of template. The first approach is great if you want the user to download a PDF. However, since I wanted my business logic to create the PDF, and obviously didn’t want to call my presentation logic from my business logic, the latter was the way to go.
Next came the final choice: a few generators stood out, due to their popularity and the number of search hits they produced. Since I didn’t have a lot of time, the PDF generator should be easy to use. In the end, I selected MigraDoc since it looked really simple and promised to come with decent documentation. Like its advanced big brother PDFsharp, MigraDoc is a product of empira Software GmbH, and is published under the MIT License.
Creating the document
So, I had selected my PDF generator. The examples included in the source code were easy enough to follow and made sense if you thought of how a Word document is structured. Create a MigraDoc document, add a Section, add a Paragraph and add some text and pictures:
// Create a new MigraDoc document Document document = new Document(); // Add a section to the document Section section = document.AddSection(); // Add a paragraph to the section Paragraph paragraph = section.AddParagraph(); // Add some text and an image to the paragraph paragraph.AddFormattedText("Hello, World!", TextFormat.Italic); paragraph.AddImage("SomeImage.png");
Next, create a PdfDocumentRenderer, assign the generated document to it, render it and save the result as a PDF document:
// Create a renderer for the MigraDoc document. PdfDocumentRenderer pdfRenderer = new PdfDocumentRenderer(); // Associate the MigraDoc document with a renderer pdfRenderer.Document = document; // Layout and render document to PDF pdfRenderer.RenderDocument(); // Save the document... pdfRenderer.PdfDocument.Save("HelloWorld.pdf");
That's all there is to it. You can also define new styles or change existing ones (similar to Word), and add images, tables and text frames. MigraDoc has an internal document description language (.mdddl) that can be saved and viewed in a text editor. In the samples provided with the source code, a document viewer is supplied to view these .mdddl files. This is great when you want to fine-tune the document's layout.
Since I wanted my PDF document to look like the web form which contained a number of panels with data, I was definitely going to need to add tables, and that was pretty easy to do too:
// Create table Table table = section.AddTable(); // Define two columns Column column = table.AddColumn(); column = table.AddColumn(); // Create two rows with content Row row = table.AddRow(); row.Cells.AddParagraph("Text row 0, column 0"); row.Cells.AddParagraph("Text in row 0, column 1"); row = table.AddRow(); row.Cells.AddParagraph("Text in row 1, column 0"); row.Cells.AddParagraph("Text in row 1, column 1");
And of course, as with all elements, you can also set all kinds of properties like margin, padding, colour and font.
However, as it turned out, the tables could not be nested. That is to say, I couldn’t do this:
// Create table for actual content within the left panel Cell cell = row.Cells; Table innerTableLeft = cell.AddTable();// <- Cell does not have a method AddTable()
This produces a compile error: ‘Cell’ does not contain a definition for ‘AddTable’ and no extension method ‘Add’ accepting a first argument of type ‘Cell’ could be found (are you missing a using directive or an assembly reference?)
Too bad, since another option, combining tables and text boxes didn’t work well on dynamic content: one or the other would not resize properly, which resulted in overlapping content. Then, during a Google search, I stumbled upon this great hidden feature. You can add a table within a table by adding it to the Elements collection of the cell in the outer table:
// Nifty trick to get nested table Table innerTableLeft = cell.Elements.AddTable();
Now I could do all of my layout using tables! From there, it was a walk in the park to get my PDF document just the way I wanted it: