Creating a PDF in ASP.NET Core using MigraDoc PDFSharp

This article shows how to use MigraDoc in ASP.NET Core to create PDF documents. In the last blog post, Creating a PDF in ASP.NET Core, PDFSharp was used to create the PDF. MigraDoc is used on top on this, which makes it easy to create tables and other document layouts.

Code: https://github.com/damienbod/AspNetCorePdf

Setting up the projects

Again no NuGet package exists for MigraDoc as a .NET Standard 2.0 package. The github repository from empira was used as the source of the MigraDoc code. This was then ported to .NET Standard 2.0 and included in the github repository.

Using MigraDoc in the ASP.NET Core application

The .NET Standard version of the MigraDoc projects are added as a reference to the ASP.NET Core project.

<ItemGroup>
 <ProjectReference Include="..\MigraDoc.DocumentObjectModel\MigraDoc.DocumentObjectModel.csproj" />
 <ProjectReference Include="..\MigraDoc.Rendering\MigraDoc.Rendering.csproj" />
 <ProjectReference Include="..\PdfSharp.Charting\PdfSharp.Charting.csproj" />
 <ProjectReference Include="..\PdfSharp\PdfSharp.csproj" />
</ItemGroup>

A service was then created and added a scoped IoC instance.

services.AddScoped<IMigraDocService, MigraDocService>();

The service implements the default example from the MigraDocs samples. The following code shows the 2 top level methods in the service. See the source code for the full example.

public string CreateMigraDocPdf(PdfData pdfData)
{
	// Create a MigraDoc document
	Document document = CreateDocument(pdfData);
	string mdddlName = $"{_createdDocsPath}/{pdfData.DocumentName}-{DateTime.UtcNow.ToOADate()}.mdddl";
	string docName = $"{_createdDocsPath}/{pdfData.DocumentName}-{DateTime.UtcNow.ToOADate()}.pdf";

	MigraDoc.DocumentObjectModel.IO.DdlWriter.WriteToFile(document, mdddlName);

	PdfDocumentRenderer renderer = new PdfDocumentRenderer(true);
	renderer.Document = document;
	renderer.RenderDocument();
	renderer.PdfDocument.Save(docName);

	return docName;
}

private Document CreateDocument(PdfData pdfData)
{
	// Create a new MigraDoc document
	Document document = new Document();
	document.Info.Title = pdfData.DocumentTitle;
	document.Info.Subject = pdfData.Description;
	document.Info.Author = pdfData.CreatedBy;

	DefineStyles(document);

	DefineCover(document);
	DefineTableOfContents(document);

	DefineContentSection(document);

	DefineParagraphs(document);
	DefineTables(document);
	DefineCharts(document);

	return document;
}

...

An example of PDF charts was also added, like in the documentation.

private void DefineCharts(Document document)
{
	Paragraph paragraph = document.LastSection.AddParagraph("Chart Overview", "Heading1");
	paragraph.AddBookmark("Charts");

	document.LastSection.AddParagraph("Sample Chart", "Heading2");

	Chart chart = new Chart();
	chart.Left = 0;

	chart.Width = Unit.FromCentimeter(16);
	chart.Height = Unit.FromCentimeter(12);
	Series series = chart.SeriesCollection.AddSeries();
	series.ChartType = ChartType.Column2D;
	series.Add(new double[] { 1, 17, 45, 5, 3, 20, 11, 23, 8, 19 });
	series.HasDataLabel = true;

	series = chart.SeriesCollection.AddSeries();
	series.ChartType = ChartType.Line;
	series.Add(new double[] { 41, 7, 5, 45, 13, 10, 21, 13, 18, 9 });

	XSeries xseries = chart.XValues.AddXSeries();
	xseries.Add("A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N");

	chart.XAxis.MajorTickMark = TickMarkType.Outside;
	chart.XAxis.Title.Caption = "X-Axis";

	chart.YAxis.MajorTickMark = TickMarkType.Outside;
	chart.YAxis.HasMajorGridlines = true;

	chart.PlotArea.LineFormat.Color = Colors.DarkGray;
	chart.PlotArea.LineFormat.Width = 1;

	document.LastSection.Add(chart);
}

When the application is started, a 4 page PDF is created with the different examples of tables, layouts and charts.

The chart in the PDF:

Notes:

PDFSharp and MigraDoc work great and make it really easy to create PDF documents. One problem is that the code depends heavily on System.Drawing.Common which is not optimized for server usage.

It would be fantastic if this library could be ported to use ImageSharp.

Links:

https://github.com/empira/MigraDoc

http://www.pdfsharp.net/wiki/MigraDocSamples.ashx

http://www.pdfsharp.net/wiki/Graphics-sample.ashx

http://www.pdfsharp.net/wiki/PDFsharpSamples.ashx

http://www.pdfsharp.net/

https://odetocode.com/blogs/scott/archive/2018/02/14/pdf-generation-in-azure-functions-v2.aspx

http://fizzylogic.nl/2017/08/03/how-to-generate-pdf-documents-in-asp-net-core/

https://github.com/rdvojmoc/DinkToPdf

https://photosauce.net/blog/post/5-reasons-you-should-stop-using-systemdrawing-from-aspnet

https://github.com/YetaWF/PDFsharp-.netcoreapp2.0

https://github.com/SixLabors/ImageSharp

6 comments

  1. […] Creating a PDF in ASP.NET Core using MigraDoc PDFSharp (Damien Bowden) […]

  2. […] Creating a PDF in ASP.NET Core using MigraDoc PDFSharp – Damien Bowden […]

  3. I’m starting to use your library to parse pdf and extract data from it. This is working perfectly for windows, however, I’m not able to even compile with.Net core because of all the GUI library ( like Silverlight or Winform) dependency that the base library has.
    Do you think it would be possible to have a base pdfSharp?Core library that only contains parsing and creation of pdf?

    1. This is a problem at the moment. PDFSharp uses system.drawing…. and it does not seem like it will ever be updated, see the issues. The performance is a problem with this. At the moment, no solution exists. One dev tried to used ImageSharp, but this solution is far from stable. The PDF story in .NET Core is not solved.

      This solution worked for me, because I deployed to windows, and have very few user requests with the host web application.

      Greetings Damien

  4. I just downloaded the code & ran the solution in VS. The PDF creates fine with the PDFSharp link, but the MigraDoc link gets an exception on:
    FontResolverInfo fontResolverInfo = FontFactory.ResolveTypeface(familyName, fontResolvingOptions, typefaceKey);
    if (fontResolverInfo == null)
    {
    // No fallback – just stop.
    throw new InvalidOperationException(“No appropriate font found.”);
    }
    The familyName para was “Times New Roman”, and typefaceKey was “tk:times new roman/n/400/5”. I hadn’t changed any code. Is there something else I need?

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.