C# PDF generation quick start guide
Read about how to generate PDF files like reports, invoices and receipts in your .NET projects. Using C# or VB.NET, create PDF documents easily by composing structural elements. The elements include headers, footers, containers, tables, paragraphs, images, and the like. The API automatically breaks content into pages.
Some other methods of generating PDF files described in the Create PDF documents in C# and VB.NET article.
We offer the layout API as a free add-on for Docotic.Pdf library. Both the library and Layout add-on are available on NuGet and from our site. Get the library, the add-on, and a free time-limited license key on the Download C# .NET PDF library page.
9.5.17615-dev 9.5.17615-dev14,813 passed Total NuGet downloads 4,924,084
Installation
Install the BitMiracle.Docotic.Pdf.Layout package from NuGet. This is the easiest and most convenient way to install Layout add-on.
As an alternative, you can download the ZIP with the library binaries on our site. To use Layout API, add references to the following DLLs from the ZIP package:
BitMiracle.Docotic.Pdf.dll
Layout add-on/BitMiracle.Docotic.Pdf.Layout.dll
The approach
The entry point is the PdfDocumentBuilder
class. To start generating a PDF, you call the
Generate method of the class. The
method requires a delegate of the Action
<Document>
type as one of its parameters. The library expects you to layout document contents in the delegate.
PdfDocumentBuilder.Create().Generate("output.pdf", (Document doc) =>
{
// build document contents here
});
Given a Document
object, the delegate should define layout. The complete layout consists of
smaller building blocks. Pages, headers and footers, containers, text blocks are examples of such
blocks.
Many method calls are chained together. The order of calls in a chain is important. Here is an example that shows how to set up pages content in a few chained calls.
PdfDocumentBuilder.Create()
.Generate("output.pdf", doc => doc.Pages(pages =>
{
pages.Content().Padding(24).AlignCenter().Text("Some text");
}));
Intellisense will help you with discovering the API. Try the API yourself. I hope you find it easy to use.
Hello, world!
Let's develop a simple application that shows common building blocks in action. The app will use the approach mentioned above.
I put the complete application code in our Docotic.Pdf sample code repository.
The code for this step is in the HelloWorld
class of the example app.
public void CreatePdf()
{
PdfDocumentBuilder.Create().Generate("hello.pdf", doc => doc.Pages(page =>
{
page.Content().Text("Hello, world!");
}));
}
Not much code, but what is happening here?
Long explanation for the short code
The code creates an instance of the document builder, asks the builder to generate hello.pdf
. The
builder delegates the job of document content layout to my code.
In the code of the delegate, I ask the document to build some pages. Now the document delegates the job of laying out the content of the pages to my code.
In the delegate for the pages, I access the layout container for the primary content of the pages. I do this by calling the Content method. The chained call to the Text adds the famous text span to the content of the pages.
Now it is time for the builder to generate the document with some pages according to the provided layout. The builder creates the exact number of pages required to contain the data I added into the layout container for the primary content. One page is enough in this case? Okay.
Prepare for future updates
I am going to add some more features to the code. To make it more convenient to develop the app further, I changed the code like this:
public void CreatePdf()
{
PdfDocumentBuilder.Create().Generate("hello.pdf", BuildDocument);
}
static void BuildDocument(Document doc)
{
doc.Pages(BuildPages);
}
static void BuildPages(PageLayout pages)
{
pages.Content().Text("Hello, world!");
}
It might look unnecessary, but splitting the code into methods at least makes it more readable.
The sample code for this step is in the HelloWorld2
class of the example app.
Related resources
Please check the following articles for more information about documents, pages and containers.
Fonts and colors
The default font is good, but I will show how to use another one. The main idea is to create a text style using a font. Then, if needed, apply some optional properties to the style and, finally, use the style on a text span.
You can use a font from the collection of fonts installed in the operating system or load a font from a file or a stream.
I recommend overriding the pre-defined styles by setting up typography for the document. But that is not required and you can use text styles directly.
The sample code for this step is in the HelloWorld3
class of the example app.
Below are the parts that changed from the previous step.
static void BuildDocument(Document doc)
{
doc.Typography(t =>
{
t.Document = doc.TextStyleWithFont(SystemFont.Family("Calibri"));
});
doc.Pages(BuildPages);
}
static void BuildPages(PageLayout pages)
{
pages.Size(PdfPaperSize.A6);
pages.Margin(10);
BuildTextContent(pages.Content());
}
static void BuildTextContent(LayoutContainer content)
{
var colorAccented = new PdfRgbColor(56, 194, 10);
var styleAccented = TextStyle.Parent.FontColor(colorAccented);
content.Text(t =>
{
t.Span("Hello, ").Style(styleAccented);
t.Span("world!").Style(styleAccented.Underline());
});
}
As you can see, I have updated the code for the BuildDocument
and the BuildPages
. I also added
a new method named BuildTextContent
.
In the BuildDocument
, I create a text style from the system font named "Calibri". Then I set this
text style as the default text style for the document.
The BuildPages
method now contains code for setting size and margin for all pages. Also, the
method calls the BuildTextContent
, passing the container for the primary content of the pages as
the parameter.
The way I construct the primary content is different now. I use two text spans and apply different text styles to each span. In effect, both spans use the accent color, but the second span also has an underline.
The code produces a PDF with the custom text font and color. If your result contains a trial message warning, please get a free time-limited license on the Docotic.Pdf library page.
Header and footer
Many real-world PDF documents like reports or invoices contain header and footer. Let me show how to add header and footer to a PDF.
The sample code for this step is in the HelloWorld4
class of the example app.
Below are the parts that changed from the previous step.
static void BuildPages(PageLayout pages)
{
pages.Size(PdfPaperSize.A6);
pages.Margin(10);
BuildPagesHeader(pages.Header());
BuildPagesFooter(pages.Footer());
BuildTextContent(pages.Content());
}
static void BuildPagesHeader(LayoutContainer header)
{
header.TextStyle(TextStyle.Parent.FontSize(8))
.AlignRight()
.Text(t =>
{
t.Line($"Created by: {Environment.UserName}");
t.Line($"Date: {DateTime.Now}");
});
}
static void BuildPagesFooter(LayoutContainer footer)
{
footer.Height(20)
.Background(new PdfGrayColor(95))
.AlignCenter()
.Text(t => t.CurrentPageNumber());
}
I changed the BuildPages
method to call the BuildPagesHeader
with the header container as the
parameter. The method also calls the BuildPagesFooter
passing the footer container to it.
The resulting PDF with header and footer definitely looks more like a professional PDF document.
The BuildPagesHeader
method sets a smaller font size for the text. I use two lines for the header
contents: one with the name of the current user and the second with the current date. The text is
right-aligned.
Please note that I do not specify any explicit size for the header. It will occupy the entire page width minus the left and the right margins. The height will depend on the text lines height.
The footer is similar, except that it has its height specified explicitly. And there is a background color applied to it. The interesting part is that to put the current page number in the PDF footer, I can just use the CurrentPageNumber method. All calculations happen inside the library.
Images
As they say, you can save a lot of words by using one picture. In a quote or receipt, you will probably use a logo of your company or some other important image. For this example code, I will use just a nice-looking one.
The file is on our site, for you to see how the resulting PDF with the beautiful image looks like.
To use an image, you would need to add it to the document first. The library can read the image
from a file or a stream. I changed the BuildDocument
method to show how to add an image to the
document.
The sample code for this step is in the HelloWorld5
class of the example app.
Below are the parts that changed from the previous step.
static void BuildDocument(Document doc)
{
doc.Typography(t =>
{
t.Document = doc.TextStyleWithFont(SystemFont.Family("Calibri"));
});
var imageFile = new FileInfo("red-flowers-at-butterfly-world.jpg");
var image = doc.Image(imageFile);
doc.Pages(pages => {
BuildPages(pages);
pages.Content().Column(c =>
{
BuildTextContent(c.Item());
BuildImageContent(c.Item(), image);
});
});
}
static void BuildPages(PageLayout pages)
{
pages.Size(PdfPaperSize.A6);
pages.Margin(10);
BuildPagesHeader(pages.Header());
BuildPagesFooter(pages.Footer());
}
static void BuildImageContent(LayoutContainer content, Image image)
{
content.AlignCenter()
.PaddingVertical(20)
.Image(image);
}
As you can see, there are more changes in the BuildDocument
method. Before, the code used the
Text method to set some text as the primary
content for pages. This still happens in the BuildTextContent
method. But now I also want an
image in the content.
To have both the text and the image in the primary content of the pages, I need a container. I use
the Column container to add the text and the image
vertically one after another. The Item method of the
column container provides a sub-container. I use one call to the Item
method to get a container
for the text and another call to get a container for the image.
As you can see, I didn't change the BuildTextContent
a bit. But, of course, I had to remove a
call to the BuildTextContent
from the BuildPages
method.
The BuildImageContent
does an important job in three lines. It adds the image with some padding
at the top and at the bottom. And it also centers the image on the page.
We have more information about the Column container and images in the following articles.
Lists
What is a list? Let's think of it as a group of numbered items written one below the other. This idea suggests a way to implement a list.
The sample code for this step is in the HelloWorld6
class of the example app.
Below are the parts that changed from the previous step.
static void BuildDocument(Document doc)
{
....
doc.Pages(pages => {
BuildPages(pages);
pages.Content().Column(c =>
{
BuildTextContent(c.Item());
BuildImageContent(c.Item(), image);
BuildListContent(c.Item());
});
});
}
static void BuildListContent(LayoutContainer content)
{
var dayNames = DateTimeFormatInfo.InvariantInfo.DayNames;
var dayNamesSpain = DateTimeFormatInfo.GetInstance(new CultureInfo("es-ES")).DayNames;
content.Column(column =>
{
for (int i = 0; i < dayNames.Length; i++)
{
column.Item().Row(row =>
{
row.Spacing(5);
row.AutoItem().Text($"{i + 1}.");
row.RelativeItem().Text(t => {
t.Line(dayNames[i]);
t.Line($"In Spain they call it {dayNamesSpain[i]}");
});
});
}
});
}
The only change in the BuildDocument
is the new method call. I added the call to the
BuildListContent
after the BuildImageContent
call.
The BuildListContent
creates the list as a column of rows. In each row, there are two items. The
first (left) one is for the item number. Another (right) one is for the item text. The code
explicitly sets the spacing between the items in the row.
To arrange items, the Row container needs to know beforehand or calculate the size of each item. I use the AutoItem and the RelativeItem methods in this example. As the result, the row container will calculate the width required to contain the first item. Then the container will use the remaining available width for the second item.
After adding the list, the contents of the resulting PDF no longer fit one page. Layout API automatically adds the second page and puts the list on it. You can see that API repeated the header and footer on the new page in the PDF document with multiple pages.
We have more information about lists in the Compound containers article.
Tables
Many PDF documents contain tables. There is no surprise here because tables enhance clarity and organization of data. Let me show how to create a table in PDF using Layout API.
The sample code for this step is in the HelloWorld7
class of the example app.
Below are the parts that changed from the previous step.
static void BuildDocument(Document doc)
{
....
doc.Pages(pages => {
BuildPages(pages);
pages.Content().Column(c =>
{
BuildTextContent(c.Item());
BuildImageContent(c.Item(), image);
BuildListContent(c.Item());
BuildTableContent(c.Item());
});
});
}
static void BuildTableContent(LayoutContainer content)
{
var color = new PdfGrayColor(75);
content.PaddingTop(20).Table(t =>
{
t.Columns(c =>
{
c.RelativeColumn(4);
c.RelativeColumn(1);
c.RelativeColumn(4);
});
t.Header(h =>
{
h.Cell().Background(color).Text("Month in 2024");
h.Cell().Background(color).Text("Days");
h.Cell().Background(color).Text("First Day");
});
for (int i = 0; i < 12; i++)
{
var stats = GetMonthStats(2024, i);
t.Cell().Text(stats.Item1);
t.Cell().Text(stats.Item2);
t.Cell().Text(stats.Item3);
}
});
}
static (string, string, string) GetMonthStats(int year, int monthIndex)
{
return (
DateTimeFormatInfo.InvariantInfo.MonthNames[monthIndex],
DateTime.DaysInMonth(year, monthIndex + 1).ToString(),
new DateTime(year, monthIndex + 1, 1).DayOfWeek.ToString()
);
}
The call to the BuildTableContent
is the only change in the BuildDocument
.
In the BuildTableContent
, I create a simple table. The table displays trivial information about
months in the year 2024. For a start, I define three columns with relative widths. The left- and
the rightmost columns will be 4 times wider than the column in the middle.
The code also defines the header for the table. It does so by adding header cells and specifying text and background color for each cell. When the table does not fit onto one page, the header gets repeated on each page occupied by the table. You can see this in the resulting PDF document with table. In the document, the table starts on the second page and continues on the third one.
I am using the simple loop to make up rows. The code in the loop starts by retrieving the information about each month. Then it adds three cells making up a row of information.
The Table container article explains all the Table container features in greater detail.
Sample code
Above, I presented some of the more popular features of PDF generation in C#. I suggest you continue discovering the API by reading the following article. Also try the sample code from Docotic.Pdf.Samples repository on GitHub. The sample code for Layout API is in the Layout folder of the repository.
You can use the sample code as a code playground. This way you can try some ideas without starting from scratch each time.
The same sample code is in the Samples
folder of the ZIP package. There are two solution files.
The SamplesCSharp
is for sample projects that use C# language. And the SamplesVB.NET
is for the
VB.NET versions.