.NET PDF generator

With the help of Docotic.Pdf, you can generate PDF documents by composing them from structural elements such as pages, containers, text spans, images, links, headers, footers, tables, lists, and more. These elements are available through the high-level Layout API, provided by the free Layout add-on for Docotic.Pdf. The API also supports reusable custom components.

Illustration of how Docotic.Pdf generates PDFs: you arrange elements such as images, tables, and text, and the library produces a PDF from that layout

The Layout API lets you define documents entirely in C# or VB.NET code using a fluent approach. Based on this description, the PDF generator provided by the add-on can produce documents with arbitrarily complex layouts, ranging from simple pages to highly structured PDF reports.

PDF generation basics

To generate PDF documents using the Layout API, you need the free Layout add-on. A license key is also required to use the core library and add-ons. You can use either a free trial key or a purchased key.

Install the add-on

The recommended way is to install the add-on from NuGet.

Install-Package BitMiracle.Docotic.Pdf.Layout

The package manager will automatically handle dependencies.

If you prefer to install the add-on manually, start by downloading the ZIP archive with the Docotic.Pdf binaries. Unpack the archive and add references to the following DLLs:

  • BitMiracle.Docotic.Pdf.dll
  • BitMiracle.Docotic.Pdf.Layout.dll from the Layout add-on subfolder.

Get a license key

To try the library, request a free time-limited license key by filling out the form on the Docotic.Pdf download page. If you have already purchased a license, use the code provided to you after the purchase.

The Layout add-on is free and does not require an additional license. You can use the Layout API with the Docotic.Pdf license you already have.

Hello, world! with the Layout API

Here is sample code that uses the API to generate a PDF with the classic "Hello, world!" phrase:

BitMiracle.Docotic.LicenseManager.AddLicenseData("PUT-LICENSE-HERE");

PdfDocumentBuilder.Create().Generate("hello.pdf", doc => doc.Pages(pages =>
{
    pages.Content().Text("Hello, world!");
}));

This code produces a single-page PDF with the text in the top-left corner.

Understanding the code sample

The sample starts by adding a license key. Without a license, the Docotic.Pdf library will not generate anything. The PDF producing code begins on the next line.

The code creates an instance of the document builder by calling the static PdfDocumentBuilder.Create() method. The call to the Generate method starts the PDF generation process. This method accepts two parameters: the name of the file to produce and a delegate of type Action<Document>. The add-on generates hello.pdf as the result of the Generate call.

To know what layout the PDF should have, the document builder calls the delegate with a Document instance. The code of the delegate composes the document contents. In this sample, the delegate uses the Pages method to provide the builder with another delegate that defines the layout of the document pages.

The builder calls the delegate provided to the Pages method with a PageLayout instance. This instance represents one or more pages in the document. The exact number of pages depends on the content added to them.

The page delegate calls the Content method to access the layout container for the primary content of the page(s). A chained call to the container's Text method adds the sample text span to the page. See the guide on layout containers for a detailed explanation of how they work.

The document builder automatically splits content across pages, creating exactly as many pages as needed to contain all data added to the primary content layout container. In this sample, a single page is sufficient, so the output contains exactly one page.

Common tasks beyond the basic sample

The sample code first creates a PdfDocumentBuilder instance and then calls the Generate method to produce a PDF. You can configure the builder before it starts generating the PDF.

For example, you can produce an encrypted PDF by providing an encryption handler to the builder. You can also specify metadata for the builder to include in the generated document. For more details on how to customize the builder, refer to the separate article.

The sample code uses only the layout container of the primary content slot, but other content slots are available as well. Here is the full list of PageLayout methods for accessing content slots:

  • Background() - returns the background layer, which is covered by other content.
  • Header() - returns the common header for all pages.
  • Content() - returns the main page content slot.
  • Footer() - returns the common footer for all pages.
  • Foreground() - returns the foreground layer, which appears above other content.

Only the content in the main content slot affects the number of pages in the generated PDF. The document builder repeats all slots other than the one provided by Content() on every page. For more information about content slots, see the article on page layout.

Many documents use different layouts on different pages. For example, the first page may have a cover-style design, while subsequent pages use a simpler layout. Some documents include special pages for tables or apply different backgrounds depending on the section. To generate such documents with Docotic.Pdf and the Layout add-on, call the Document.Pages method multiple times. You can find a working example in our samples repository.

Organizing the code

For short code, like in the sample, it is fine to chain calls and use nested lambdas. When you build something larger, it may be more convenient to split the code into separate methods. This makes the code easier to read and maintain.

Here is how the Hello, world! sample looks when its code is split into methods.

public void GeneratePdf()
{
    BitMiracle.Docotic.LicenseManager.AddLicenseData("PUT-LICENSE-HERE");

    PdfDocumentBuilder.Create().Generate("hello.pdf", BuildDocument);
}

private void BuildDocument(Document doc)
{
    doc.Pages(BuildPages);
}

private void BuildPages(PageLayout pages)
{
    pages.Content().Text("Hello, world!");
}

Why generate PDFs with the Docotic.Pdf Layout API

The Layout API is a developer-friendly, modern way to generate PDFs from composable building blocks without needing to understand PDF format internals. It lays out PDFs with high performance and with deterministic, predictable behavior. You can use the API in high-volume document-generation scenarios.

The API is available when using Docotic.Pdf together with the free Layout add-on. Both Docotic.Pdf and the Layout add-on are 100% managed code DLLs without unsafe code blocks. The Layout API is implemented without any third-party external dependencies such as a browser or Skia binaries, resulting in a lightweight, low-overhead solution that is easy to deploy and maintain.

API overview

The Layout API is a fluent, pleasant-to-use API. You describe the layout of your document entirely in code using flexible layout elements, and the generator flows your content, paginates it automatically, and renders the result as a PDF.

The Layout add-on uses a declarative, flow-based layout system. Its layout elements include lists, columns, rows, tables, images, text spans, headers and footers, containers, and more. Instead of manually placing elements at exact coordinates, you describe how elements should behave. The document builder then computes the final layout and creates the PDF.

The flow-based layout system ensures that the layout adapts to page size and content. Thanks to this system, the add-on excels at complex, structured, rule-based layouts. You can nest different types of containers and build sophisticated structures without sacrificing readability.

When working with the Layout API, you can chain most calls together. This leads to more compact and expressive code than with traditional APIs. The order of calls in a chain is important. Everything is strongly typed, providing compile-time safety and making the code refactor-friendly. You can also unit-test the code that uses the Layout API.

To make your layout implementation even more concise, you can extend the API with your own methods and build a clean, expressive DSL around it.

For more information on positioning and DSL creation, refer to the article explaining how to control the size, position, alignment, and rendering behavior of containers.

.NET versions and platform support

You can use the Layout API in projects targeting .NET Standard 2.1 and newer frameworks. In other words, the Layout add-on is compatible with .NET 5 through .NET 10. Additionally, .NET Core 3.0+ is supported.

You can generate PDFs with the Layout API in ASP.NET Core, MAUI apps, Unity, Xamarin, and console applications. Docotic.Pdf with the Layout add-on can produce PDFs on Windows, macOS, and Linux.

Cloud platforms and Docker images

Docotic.Pdf with the Layout add-on can run in Azure and AWS cloud environments, including serverless setups. The library and add-on fully support dynamic hardware changes, autoscaling, and other cloud-native runtime features.

In most cloud scenarios, an unbound license is required. The License FAQ explains how to choose the appropriate license for cloud applications.

The Layout API just works in Docker containers. You don't need any special configuration to generate PDFs when running the library and the Layout add-on inside a container.

Adding text to PDF files

Text is a fundamental part of any PDF document. You can use Text methods of the LayoutContainer class to add text to a content slot.

Illustration of document pages surrounded by examples of text styles such as Title, Caption, Plain, Hyperlink, Strong, and Footnote

Text spans

In the Hello, world sample, I used the LayoutContainer.Text(string) overload to add text to the page's primary content. Now let's look at another overload of the Text method.

public static void GenerateTextPdf()
{
    PdfDocumentBuilder.Create().Generate("text-spans.pdf", doc => doc.Pages(pages =>
    {
        pages.Content().Text(AddTextSpans);
    }));
}

private static void AddTextSpans(TextContainer text)
{
    text.Line("About VB.NET")
        .Style(t => t.Strong);

    text.Span("VB.NET is a multi-paradigm, object-oriented language ");
    text.Span("that runs on .NET, Mono, and the ");
    text.Hyperlink(
        ".NET Framework",
        new Uri("https://dotnet.microsoft.com/download/dotnet-framework"));
    text.Line(".");

    text.Span("Released by Microsoft in 2002, ");
    text.Line("it continues the lineage of the original Visual Basic language.");
}

This code uses the Span and Line methods of the TextContainer class to add text to the current line. The Line method additionally completes the current line. The sample code also uses the Hyperlink method to attach an external resource link to a specific text span.

Note how the sample code applies strong formatting to the first line using the Style method. Let's explore the concept of text styles in more detail.

Text styles

Text styles allow you to customize the appearance of text. The TextStyle class provides methods for changing font size, letter spacing, colors, and other text properties. TextStyle objects are immutable, so each method call produces a new style instance. You can apply text styles at different layout levels.

PdfDocumentBuilder.Create().Generate("text-styles.pdf", doc =>
{
    doc.Pages(pages =>
    {
        pages.TextStyle(TextStyle.Parent.FontSize(30));

        pages.Content()
            .TextStyle(TextStyle.Parent.FontColor(new PdfRgbColor(0, 0, 255)))
            .Text(text =>
            {
                text.Span("Hello,");

                text.Span("World!")
                    .Style(TextStyle.Parent.Underline());
            });
    });
});

The TextStyle.Parent property returns a special style in which all text properties are undefined. In the sample above, the code draws “Hello, World!” in blue, with the second word underlined, using a 30-point font size.

This happens because the code:

  • sets the font size to 30 points at the page level,
  • then sets the text color to blue for the primary content slot,
  • then applies the underline style to the last text span.

Each subsequent style inherits the previous ones through the TextStyle.Parent property.

Using the methods of the TextStyle class, you can, among other things, change the text direction to right-to-left. See another example of using text style inheritance in our samples repository.

Typography

The TextStyle class supports customizing all text properties except the associated font. To change the default font, use the Document.TextStyleWithFont methods to create a text style based on a specific font. You can use a font installed in the operating system or load one from a file or stream.

After creating a font-based style, you can apply additional optional properties and then use the resulting style on a text span. This C# sample demonstrates how to use a system font.

PdfDocumentBuilder.Create().Generate("text-style-with-font.pdf", doc =>
{
    var font = SystemFont.Family("Calibri");
    var style = doc.TextStyleWithFont(font).FontSize(30);

    doc.Pages(pages =>
    {
        pages.Content().Text(text =>
        {
            text.Span("Hello,");

            text.Span("World!")
                .Style(style);
        });
    });
});

The TextStyleWithFont method includes an optional parameter that specifies how the font should be embedded in the generated document. By default, the library:

  • embeds used glyphs for TrueType/OpenType fonts,
  • embeds all glyphs for Type1 and CFF fonts,
  • does not embed glyphs for built-in PDF fonts (Base14 fonts).

Because of these defaults, documents that use TrueType and OpenType fonts may remain small in size even when large fonts are used. You can also specify a custom font loader, fallback fonts, and a handler for missing glyphs. See the sample code in our examples repository for details on managing fonts in PDF documents.

The Layout API provides a collection of predefined styles, which you can access and modify through the Typography class.

PdfDocumentBuilder.Create().Generate("typography.pdf", doc =>
{
    doc.Typography(t =>
    {
        var fontsPath = Environment.GetFolderPath(Environment.SpecialFolder.Fonts);
        var arialFont = new FileInfo(Path.Combine(fontsPath, "arial.ttf"));

        t.Document = doc.TextStyleWithFont(arialFont);
        t.Header = t.Parent.FontSize(20).FontColor(new PdfGrayColor(20));
        t.Footer = t.Footnote;
    });

    doc.Pages(pages =>
    {
        pages.Header().AlignCenter().Text("Header");

        pages.Content().Text(t =>
        {
            t.Line("Title").Style(t => t.Title);
            t.Line("Heading 1").Style(t => t.Heading1);
            t.Line("Regular");
        });

        pages.Footer()
            .Height(20)
            .AlignCenter()
            .Text(t => t.CurrentPageNumber());
    });
});

I recommend overriding the predefined styles by configuring typography for the entire document, although this is not required and you can still apply text styles at the text-span level.

With the Typography class, you do not need to store text style references in variables. Instead, you register the required styles using the Document.Typography methods and later access them through the Typography object's properties. Refer to the Typography sample to see how to use predefined and custom text styles when generating PDF documents.

Many real-world PDF documents, such as reports or invoices, include headers and footers. This section shows how to add both to a PDF.

public static void GeneratePdfWithHeaderAndFooter()
{
    PdfDocumentBuilder.Create()
        .Generate("header-footer.pdf", doc => doc.Pages(pages =>
    {
        pages.Size(PdfPaperSize.A6).Margin(10);

        BuildPagesHeader(pages.Header());
        BuildPagesFooter(pages.Footer());

        pages.Content().Text("Hello, world!");
    }));
}

public 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}");
        });
}

public static void BuildPagesFooter(LayoutContainer footer)
{
    footer.AlignRight().Text(text =>
    {
        text.Style(t => t.Parent.FontColor(new PdfRgbColor(255, 0, 0)));

        text.CurrentPageNumber();
        text.Span(" / ");
        text.PageCount();
    });
}

The BuildPagesHeader method sets a smaller font size for the header text. It uses two lines for the header content: one with the current user's name and another with the current date. The text is right-aligned.

Notice that the code does not specify any explicit size for the header. It occupies the full page width minus the left and right margins, and its height depends on the height of the text lines.

The BuildPagesFooter method demonstrates how to place the current page number in the PDF footer. The library calculates the current page number and total page count automatically. You can access these values using the CurrentPageNumber and PageCount methods of a TextContainer object.

The Layout API also provides a way to format page numbers. For example, you can draw hexadecimal page numbers like this:

text.CurrentPageNumber().Format(p => "0x" + p?.ToString("x2"));

Our sample repository contains another example of adding a header and footer to a PDF. That example formats page numbers as Roman numerals.

Inserting images

As the saying goes, a single picture can replace many words. In quotes or receipts, you will often include your company's logo or another important image. For this example, I will use a simple, nice-looking one.

To use an image, you must first add it to the document. The library can load images from a file or a stream. Only raster formats are supported: PNG, JPEG, JPEG 2000, BMP, GIF, and TIFF.

Once you have an Image object, you can set it as the content of one or more layout containers by calling the container's Image method. You can scale, rotate, add padding, and arrange the image just like any other content.

PdfDocumentBuilder.Create().Generate("image-with-text.pdf", doc =>
{
    var imageFile = new FileInfo("red-flowers-at-butterfly-world.jpg");
    var image = doc.Image(imageFile);

    doc.Pages(pages =>
    {
        pages.Size(PdfPaperSize.A6);
        pages.Content().Column(c =>
        {
            c.Spacing(20);

            c.Item()
                .AlignCenter()
                .Text("Hello, world!")
                .FontSize(20);

            c.Item()
                .AlignCenter()
                .MaxWidth(200)
                .Image(image);
        });
    });
});

The sample code uses both text and an image in the primary content slot. However, a layout container can contain only text or only an image. To include both in the page's primary content slot, you need a compound container.

I use a Column container to place the text and the image vertically, one after the other. The Item method of the column container provides a sub-container. One call to Item creates a container for the text, and another creates a container for the image. The compound container with all its sub-containers becomes the main content.

To run the sample code, download the flower image from our samples repository and place it in your app's working directory.

Online images

If you have only an image URL rather than a file, download the image into a memory stream and create an Image from that stream. The following example shows how to use online images with the Layout API.

public static async Task AddImageWithTextFromUrl()
{
    using var http = new HttpClient();
    using var stream = await http.GetStreamAsync("url/to/image");

    var memoryStream = new MemoryStream();
    await stream.CopyToAsync(memoryStream);
    memoryStream.Position = 0;

    PdfDocumentBuilder.Create().Generate("online-image-with-text.pdf", doc =>
    {
        var image = doc.Image(memoryStream);

        doc.Pages(pages =>
        {
            pages.Size(PdfPaperSize.A6);
            pages.Content().Column(c =>
            {
                c.Spacing(20);

                c.Item()
                    .AlignCenter()
                    .Text("Hello, world!")
                    .FontSize(20);

                c.Item()
                    .AlignCenter()
                    .MaxWidth(200)
                    .Image(image);
            });
        });
    });
}

Because the method performs asynchronous work (downloading an image from a URL), it is declared as async Task rather than void. Callers must await it to ensure that PDF generation completes correctly. In your own code, a similar method may also return a value, in which case it would be declared as async Task<TResult>.

Creating lists

What is a list? You can think of it as a group of numbered items written one below the other. The Layout API does not provide a special container type for lists, but it is easy to implement one using other compound containers.

var dayNames = DateTimeFormatInfo.InvariantInfo.DayNames;
var dayNamesSpain = DateTimeFormatInfo.GetInstance(new CultureInfo("es-ES")).DayNames;

PdfDocumentBuilder.Create().Generate("list.pdf", doc => doc.Pages(pages =>
{
    pages.Size(PdfPaperSize.A6);
    pages.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 sample code creates the list as a column of rows. Each row contains two items: the first (on the left) holds the item number, and the second (on the right) holds the item text. The code explicitly sets the spacing between the items in each row.

To arrange items, the Row container must either have the size of each item specified explicitly or calculate it. In this example, the AutoItem and RelativeItem methods are used. As a result, the row container calculates the width required for the first item and then uses the remaining available width for the second item.

By using this approach and adjusting the appearance of the rows as needed, you can create a list that best fits your document's layout and style.

Building tables

Many PDF documents contain tables, which is no surprise because tables improve the clarity and organization of data. This section shows how to create a table in a PDF using the Layout API.

public static void AddTable()
{
    PdfDocumentBuilder.Create().Generate("table.pdf", doc => doc.Pages(pages =>
    {
        pages.Size(PdfPaperSize.A6);

        var color = new PdfGrayColor(75);
        pages.Content().Padding(20).Table(t =>
        {
            t.Columns(c =>
            {
                c.RelativeColumn(4);
                c.RelativeColumn(1);
                c.RelativeColumn(4);
            });

            t.Header(h =>
            {
                h.Cell().Background(color).Text("Month");
                h.Cell().Background(color).Text("Days");
                h.Cell().Background(color).Text("First Day");
            });

            var year = DateTime.Now.Year;
            for (int yearDiff = 0; yearDiff < 4; yearDiff++)
            {
                for (int i = 11; i >= 0; i--)
                {
                    var stats = GetMonthStats(year - yearDiff, i);

                    t.Cell().Text(stats.Item1);
                    t.Cell().Text(stats.Item2);
                    t.Cell().Text(stats.Item3);
                }
            }
        });
    }));
}

private static (string, string, string) GetMonthStats(int year, int monthIndex)
{
    return (
        $"{DateTimeFormatInfo.InvariantInfo.MonthNames[monthIndex]} {year}",
        DateTime.DaysInMonth(year, monthIndex + 1).ToString(),
        new DateTime(year, monthIndex + 1, 1).DayOfWeek.ToString()
    );
}

The sample code creates a simple table that displays basic information about the months in the current year and the previous three years. The code defines three columns with relative widths. The leftmost and rightmost columns are four times wider than the middle column.

The code also defines the table header by adding header cells and specifying the text and background color for each cell. When a table does not fit on a single page, the header is repeated on every page the table occupies. You can see this behavior in the PDF generated by the sample code.

Two simple loops are used to build the rows. The outer loop iterates over the years, and the inner loop iterates over the months in reverse order. The inner loop retrieves information about each month and then adds three cells to form a row.

For more details, you can read the article that explains the features of the Table container in depth.

PDF documents support internal links that allow readers to jump to another location within the same file. In a PDF viewer, these links appear as clickable text or image elements. They function like hyperlinks, but instead of pointing to an external website, they navigate within the document itself.

Illustration of internal links in a PDF, showing connected document sections with link and target icons

Many PDF documents use internal links for bookmarks or a table of contents, helping readers move quickly between sections. Here is how you can add a link to a document section using the Layout API:

PdfDocumentBuilder.Create().Generate("link.pdf", doc =>
{
    doc.Pages(pages =>
    {
        pages.Content().Column(c =>
        {
            const string SectionName = "Chapter 1";
            c.Item().SectionLink(SectionName).Text("Link");

            c.Item().PageBreak();

            c.Item().Section(SectionName).Text("Target");
        });
    });
});

The sample code makes the text on the first page a link to the section with the specified name. It does this by calling the SectionLink method on the layout container that contains the text. The section may or may not exist yet at this point. The text becomes clickable in a PDF viewer.

The code then marks the text on the second page as the start of the section with the same name. Its appearance and behavior do not change, but it becomes the target for the link on the first page. This is done by calling the Section method on the corresponding layout container.

In this example, both the link and its target are text spans, but you can create sections and links on any layout container. It may be a container with an image, a table container, or a container you construct yourself.

See an example of how to create a PDF table of contents in our samples repository.

Designing complex PDF layouts

The Layout API provides a variety of layout containers that you can combine to produce arbitrarily complex PDF documents. You can also extend the API with your own custom components.

The examples on this page are intentionally simple, designed to create straightforward documents so you can focus on the core ideas without getting lost in details. If you'd like to see how different containers can be combined to generate a more complex PDF, take a look at the corresponding example in our samples repository on GitHub.

In addition to using built-in containers, you can define and use custom layout components. These components are especially useful when you want a single class to encapsulate both the data and the layout logic. Keeping everything in one place makes the component easier to understand, modify, and reuse, while also giving you a flexible way to handle complex layouts.

To create a custom layout component, implement the ILayoutComponent interface in your class. When generating a PDF, the library calls the interface's Compose method and provides a LayoutContext object. Your code can use this object to create layout elements and access information about the document being generated.

To add a custom layout component to your layout, call the Component method of the LayoutContainer class. In terms of sizing and positioning, a custom component behaves just like text or images.

For an example of implementing a custom component with the ILayoutComponent interface, see the Layout components sample.

Other ways to create PDF

Docotic.Pdf offers multiple ways to create PDFs, each suited to different scenarios. This section explains when to rely on the Layout API and when other approaches may serve you better.

When to prefer the Layout API

The Docotic.Pdf Layout API is a powerful way to generate PDFs from structured layouts. It lets you build documents by composing text, images, containers, and tables into arbitrarily complex, nested structures. The generation process is fast, uses a reasonable amount of memory, and behaves predictably.

Docotic.Pdf and the Layout add-on form a lightweight, fully self-contained set. The API does not require external libraries or browsers to generate PDFs. This makes it a solid choice for .NET microservices, cloud applications (especially serverless ones), and other environments where footprint size matters.

Because the document layout is defined in code, you can unit test any part of it. Layout logic can be reused like any other code, and you can encapsulate both data and layout behavior in custom components.

Alternatives to the Layout API

A dedicated article provides a detailed comparison of all the ways to create PDFs with Docotic.Pdf. Below are some of the most commonly used alternatives.

  • HTML to PDF conversion
    Reuses existing HTML/CSS templates. Choose the HTML-to-PDF approach when your team already produces documents in HTML/CSS and you need PDF versions of those documents.

  • Low-level PDF generation
    Provides the maximum control available in the library over PDF structure and content. Choose this when you need pixel-level positioning or complex vector graphics. This approach is recommended for performance-critical scenarios or when the smallest possible footprint is required.

  • Template-based PDF generation
    Offers a fast, predictable way to fill documents without designing or arranging their elements. Choose this when you have a predefined PDF structure, such as approved or compliance-controlled templates, and only need to change text fields, replace placeholders, attach related documents, and perform similar tasks.

  • PDF merging and composition
    Lets you create a PDF from existing pieces instead of building it from scratch. Choose this when you have images, scanned pages, or other PDFs that need to be combined into a single file.

Comparison with other PDF generation solutions

This section contains two comparison tables: one with key takeaways and another with detailed, structured information about how Docotic.Pdf with the Layout add-on compares to other popular solutions for generating PDFs.

Key comparison takeaways

There are strong options for generating PDFs, including free ones. However, capabilities like signing, encrypting, or merging documents vary, which can limit which tools truly fit your needs.

Solution When to Use Best For
Docotic.Pdf with the Layout add-on When you want a high-quality, high-performance layout engine capable of producing optimized PDFs, with advanced support for signatures, encryption, and PDF editing across platforms High-quality generation of invoices, reports, statements, and similar documents, with an excellent developer experience. Ideal when you need enterprise-grade PDF processing and professional support
PDFsharp + MigraDoc When you want a free, MIT-licensed library for basic PDF generation and do not require digital signatures or modern encryption algorithms Simple document creation in open-source or budget-constrained projects
QuestPDF When you want a modern layout engine dedicated solely to PDF generation and do not need editing, signatures, or encryption High-quality PDF generation with an MIT or low-cost commercial license, provided that all non-generation features are handled elsewhere
iText When you need a mature, feature-rich PDF toolkit for both generating and processing PDFs, and you are prepared to either open-source your solution under the AGPL/GPLv3 license or purchase an expensive commercial license Teams already familiar with the iText API and therefore unaffected by its steep learning curve

Detailed comparison

Review the table to understand the broader context and form your own conclusions.

  Docotic.Pdf with Layout PDFsharp + MigraDoc QuestPDF iText
PDF capabilities Full-featured PDF library PDF generation and limited editing PDF generation only Extensive PDF functionality
Rendering Model Modern, declarative, retained-mode layout engine Box-based layout engine Modern, declarative, retained-mode layout engine Box-based layout engine + renderer tree
API kind Fluent API Imperative API Fluent API Imperative API
Developer Experience Excellent. API is clean, modern, and intuitive Good. API design is conventional Excellent. API is clean, modern, and intuitive Satisfactory. API is verbose and overly complex
Font Subsetting Supported; only used glyphs are embedded by default Not supported; may result in unnecessarily large PDFs Supported; only used glyphs are embedded by default Supported; only used glyphs are embedded by default
Right-to-left (RTL) content direction Supported Not supported Supported Supported
Digital signatures Supported, including LTV and external signatures Not supported Not supported Supported, including LTV and external signatures
Encryption / permissions Fully supported RC4 encryption only, no support for AES or certificates Not supported Fully supported
External dependecies None None SkiaSharp / Skia-based components None
Support Professional support for prospects and customers; priority support with top-tier licenses Community support; professional support can be purchased separately Community support via GitHub Community support for AGPL version; professional support for commercial license holders
License Commercial, with free licenses for eligible use cases MIT MIT for individuals and small companies; commercial license required for larger businesses AGPL for open-source use, expensive commercial license for proprietary projects
Developer licensing Unlimited developers with all licenses Unlimited developers with all licenses Unlimited developers with MIT; 10 developers with Professional; unlimited developers with Enterprise Unlimited developers with AGPL version; per-developer licensing with commercial license

Conclusion

Docotic.Pdf with the Layout add-on provides a modern, high-performance, high-quality way to generate PDFs in C# and VB.NET. The library produces reports, statements, invoices, and similar documents. Its well-designed, fluent API offers an excellent developer experience. You can rely on the professional support that Bit Miracle provides for Docotic.Pdf and its add-ons.

Unlike some other PDF generation solutions, Docotic.Pdf is a full-featured PDF API. The library can sign generated PDFs with digital signatures, including LTV-enabled signatures. Docotic.Pdf is capable of using certificates stored on secure hardware such as USB tokens and smart cards. Cloud-based Hardware Security Modules (HSMs), such as Microsoft Azure Key Vault and AWS Key Management Service (KMS), are also supported.

With Docotic.Pdf, you can attach supporting documents such as spreadsheets or voice notes to the generated PDFs. To display documents on a web page or in a similar interface, you can create thumbnail images from one or more of their pages.

Next steps:

  • Explore the code samples for the Layout API.
  • Compare PDF generation from composable building blocks with other approaches to creating PDFs.
  • Contact us with questions, feedback, or edge cases.
Photo of Sergey Bobrovsky
Written by

Sergey is a PDF expert and the co‑founder of Bit Miracle. He has been developing PDF libraries since 2005. Docotic.Pdf is his best library to date. Customers praise it as a high‑quality, robust component that is well‑designed and memory‑efficient.