--- url: /quick-start.md --- # Quick start QuestPDF is a modern C# library for PDF generation that provides a dedicated layout engine optimized specifically for creating PDF documents. Its component-based architecture lets you compose simple elements (such as text, images, tables, and grids) into sophisticated layouts through an intuitive, declarative API. Because it's pure C#, you get full access to familiar programming constructs, strong typing, and seamless IDE support. ## Installation QuestPDF is available as a NuGet package. You can install it through your IDE by searching for phrase `QuestPDF`. If you are not familiar how to do that, please refer to the following guides: * [Visual Studio](https://learn.microsoft.com/en-us/nuget/consume-packages/install-use-packages-visual-studio) * [Visual Code](https://code.visualstudio.com/docs/csharp/package-management) * [JetBrains Rider](https://www.jetbrains.com/help/rider/Using_NuGet.html) Or use the following command in your terminal: ```bash dotnet add package QuestPDF ``` ## Implementation QuestPDF's minimal API makes it incredibly easy to create and prototype PDF documents. Here's a simple example that demonstrates its intuitive syntax: ```csharp using QuestPDF.Fluent; using QuestPDF.Helpers; using QuestPDF.Infrastructure; // TODO: set your license here: // QuestPDF.Settings.License = LicenseType.Evaluation; Document.Create(container => { container.Page(page => { page.Size(PageSizes.A4); page.Margin(2, Unit.Centimetre); page.PageColor(Colors.White); page.DefaultTextStyle(x => x.FontSize(20)); page.Header() .Text("Hello PDF!") .SemiBold().FontSize(36).FontColor(Colors.Blue.Medium); page.Content() .PaddingVertical(1, Unit.Centimetre) .Column(x => { x.Spacing(20); x.Item().Text(Placeholders.LoremIpsum()); x.Item().Image(Placeholders.Image(200, 100)); }); page.Footer() .AlignCenter() .Text(x => { x.Span("Page "); x.CurrentPageNumber(); }); }); }) .GeneratePdf("hello.pdf"); ``` This code generates a PDF document with the following layout: ![example](/minimal-api.png) ## License ::: warning SUSTAINABLE AND FAIR LICENSE By offering free access to most users and premium licenses for larger organizations, the project maintains its commitment to excellence while ensuring sustainable, long-term development for all. The library is free to use for any individual or business with less than 1 million USD annual gross revenue, or operates as a non-profit organization, or is a FOSS project. More details can be found on the [QuestPDF Pricing](/pricing) and [QuestPDF License](/license/) pages. ::: ::: tip For learning and evaluation, you can use the QuestPDF Evaluation license type. ::: ## Are you ready for more? QuestPDF's Fluent API scales seamlessly with your document complexity. To explore its full potential, check out [the In-Depth Invoice Tutorial](/invoice-tutorial), where you'll learn to create a professional invoice in less than 200 lines of code. ![invoice](/getting-started/invoice.png) --- --- url: /invoice-tutorial.md --- # In-Depth Invoice Tutorial QuestPDF is a modern .NET library for PDF document generation that emphasizes clean architecture and developer productivity. In this tutorial, we'll build a professional invoice document while exploring the core concepts that make QuestPDF powerful and intuitive to use. By the end, you'll have a fully functional, paginated invoice generator that looks like this: ![invoice](/getting-started/invoice.png) ::: tip INSTALLATION Before starting this tutorial, please familiarize yourself with [the Quick Start tutorial](/quick-start). It will guide you through the installation process and provide a basic understanding of the library's architecture. ::: ::: tip SOURCE CODE You can download, review, and compile the complete example from [this GitHub repository](https://github.com/QuestPDF/QuestPDF-ExampleInvoice). ::: ## Suggested architecture QuestPDF recommends a clear three-layer architecture for both maintainability and clarity: 1. **Document Models** - define the raw data that appears in your PDF, such as invoice details or report content. These classes remain free of business logic and focus solely on representing structured information. 2. **Data Source** - handle asynchronous data fetching, transformations, and calculations. Here, you perform database queries, map domain entities to the document models, and load external resources (Images) to prepare all the information needed to render the document. 3. **Template** - use C# features (such as loops, conditional logic, helper methods) and QuestPDF Fluent API to design the visual layout and appearance of your document. ## Document models layer First, let's define the data structure for our invoice. These models capture all the information we need to display: ```csharp public class InvoiceModel { public int InvoiceNumber { get; set; } public DateTime IssueDate { get; set; } public DateTime DueDate { get; set; } public Address SellerAddress { get; set; } public Address CustomerAddress { get; set; } public List Items { get; set; } public string Comments { get; set; } } public class OrderItem { public string Name { get; set; } public decimal Price { get; set; } public int Quantity { get; set; } } public class Address { public string CompanyName { get; set; } public string Street { get; set; } public string City { get; set; } public string State { get; set; } public object Email { get; set; } public string Phone { get; set; } } ``` ## Data source layer Next, implement a class that retrieves and prepares your invoice data. In a real application, this might query a database, download images from storage, or call an external API. For this tutorial, we'll use a sample data generator: ```csharp using QuestPDF.Helpers; public static class InvoiceDocumentDataSource { private static Random Random = new Random(); public static InvoiceModel GetInvoiceDetails() { var items = Enumerable .Range(1, 8) .Select(i => GenerateRandomOrderItem()) .ToList(); return new InvoiceModel { InvoiceNumber = Random.Next(1_000, 10_000), IssueDate = DateTime.Now, DueDate = DateTime.Now + TimeSpan.FromDays(14), SellerAddress = GenerateRandomAddress(), CustomerAddress = GenerateRandomAddress(), Items = items, Comments = Placeholders.Paragraph() }; } private static OrderItem GenerateRandomOrderItem() { return new OrderItem { Name = Placeholders.Label(), Price = (decimal) Math.Round(Random.NextDouble() * 100, 2), Quantity = Random.Next(1, 10) }; } private static Address GenerateRandomAddress() { return new Address { CompanyName = Placeholders.Name(), Street = Placeholders.Label(), City = Placeholders.Label(), State = Placeholders.Label(), Email = Placeholders.Email(), Phone = Placeholders.PhoneNumber() }; } } ``` ## Template layer With data ready, focus on how it should appear in the final PDF. QuestPDF’s layout engine uses a fluent API to define pages, headers, footers, and content sections. ### Basic page structure As the first step, we’ll implement a single page with a simple header, content area, and footer. The class below implements the `IDocument` interface and uses the `Compose` method to define the document’s structure. Each fluent API call creates a container with its own style, size, alignment constraints and layout behavior — making their order important. While most elements are simple containers holding a single child, some advanced elements offer multiple slots to accommodate more complex layouts. ```csharp{17-28} using QuestPDF.Fluent; using QuestPDF.Helpers; using QuestPDF.Infrastructure; public class InvoiceDocument : IDocument { public InvoiceModel Model { get; } public InvoiceDocument(InvoiceModel model) { Model = model; } public DocumentMetadata GetMetadata() => DocumentMetadata.Default; public DocumentSettings GetSettings() => DocumentSettings.Default; public void Compose(IDocumentContainer container) { container .Page(page => { page.Margin(50); page.Header().Height(100).Background(Colors.Grey.Lighten1); page.Content().Background(Colors.Grey.Lighten3); page.Footer().Height(50).Background(Colors.Grey.Lighten1); }); } } ``` Then, use the following code to generate the document: ```csharp{10-12} using System.IO; using QuestPDF.Fluent; using QuestPDF.Infrastructure; static void Main(string[] args) { // TODO: set your license here: // QuestPDF.Settings.License = LicenseType.Evaluation; var model = InvoiceDocumentDataSource.GetInvoiceDetails(); var document = new InvoiceDocument(model); document.GeneratePdfAndShow(); // document.GeneratePdf("invoice.pdf"); } ``` This initial scaffolding sets up basic sections. You’ll refine them in the following steps. ![example](/getting-started/step-1.webp) ### Implementing header and footer Implement header and footer of the document using the most common QuestPDF visual, positional, and layout components. The code also uses local methods to define the header and content sections. This approach produces cleaner code and makes it easier to maintain and understand. ::: tip Please hover your cursor over the code to see the explanation of various API calls. ::: ```csharp public class InvoiceDocument : IDocument { /* code omitted */ public void Compose(IDocumentContainer container) { container .Page(page => { page.Margin(50); page.Header().Element(ComposeHeader); page.Content().Element(ComposeContent); page.Footer().AlignCenter().Text(x => { x.CurrentPageNumber(); x.Span(" / "); x.TotalPages(); }); }); } void ComposeHeader(IContainer container) { container.Row(row => { row.RelativeItem().Column(column => { column.Item() .Text($"Invoice #{Model.InvoiceNumber}") .FontSize(20).SemiBold().FontColor(Colors.Blue.Medium); column.Item().Text(text => { text.Span("Issue date: ").SemiBold(); text.Span($"{Model.IssueDate:d}"); }); column.Item().Text(text => { text.Span("Due date: ").SemiBold(); text.Span($"{Model.DueDate:d}"); }); }); row.ConstantItem(100).Height(50).Placeholder(); }); } void ComposeContent(IContainer container) { container .PaddingVertical(40) .Height(250) .Background(Colors.Grey.Lighten3) .AlignCenter() .AlignMiddle() .Text("Content").FontSize(16); } } ``` The code above generates the following output: ![example](/getting-started/step-2.webp) ### Content implementation Define general structure of the primary content. Please note that you can freely use C# features such as conditions and loops. ```csharp public class InvoiceDocument : IDocument { /* code omitted */ void ComposeContent(IContainer container) { container.PaddingVertical(40).Column(column => { column.Spacing(5); column.Item().Element(ComposeTable); if (!string.IsNullOrWhiteSpace(Model.Comments)) column.Item().PaddingTop(25).Element(ComposeComments); }); } void ComposeTable(IContainer container) { container .Height(250) .Background(Colors.Grey.Lighten3) .AlignCenter() .AlignMiddle() .Text("Table").FontSize(16); } void ComposeComments(IContainer container) { container.Background(Colors.Grey.Lighten3).Padding(10).Column(column => { column.Spacing(5); column.Item().Text("Comments").FontSize(14); column.Item().Text(Model.Comments); }); } } ``` Here's the result generated by the code snippet above: ![example](/getting-started/step-3.webp) ### Table generation Table is one of the most flexible and powerful elements in QuestPDF. Begin by defining the number, position, and size of your columns. After that, add cells which can be either auto-arranged by the layout engine or explicitly placed at specific rows and columns. You can even have cells span multiple columns or rows. Note the use of the CellStyle local function, which applies consistent styling to cells in a single, reusable manner. ```csharp public class InvoiceDocument : IDocument { /* code omitted */ void ComposeTable(IContainer container) { container.Table(table => { table.ColumnsDefinition(columns => { columns.ConstantColumn(25); columns.RelativeColumn(3); columns.RelativeColumn(); columns.RelativeColumn(); columns.RelativeColumn(); }); table.Header(header => { header.Cell().Element(CellStyle).Text("#"); header.Cell().Element(CellStyle).PaddingBottom(5).Text("Product"); header.Cell().Element(CellStyle).AlignRight().Text("Unit price"); header.Cell().Element(CellStyle).AlignRight().Text("Quantity"); header.Cell().Element(CellStyle).AlignRight().Text("Total"); static IContainer CellStyle(IContainer container) { return container.DefaultTextStyle(x => x.SemiBold()).PaddingVertical(5).BorderBottom(1).BorderColor(Colors.Black); } }); foreach (var item in Model.Items) { table.Cell().Element(CellStyle).Text(Model.Items.IndexOf(item) + 1); table.Cell().Element(CellStyle).Text(item.Name); table.Cell().Element(CellStyle).AlignRight().Text($"{item.Price}$"); table.Cell().Element(CellStyle).AlignRight().Text(item.Quantity); table.Cell().Element(CellStyle).AlignRight().Text($"{item.Price * item.Quantity}$"); static IContainer CellStyle(IContainer container) { return container.BorderBottom(1).BorderColor(Colors.Grey.Lighten2).PaddingVertical(5); } } }); } /* code omitted */ } ``` ![example](/getting-started/step-4.webp) ### Address component To prevent duplication and improve maintainability, move recurring sections into reusable components. For example, addresses often appear multiple times with the same layout. By implementing IComponent, you can pass arguments and reuse this logic throughout your project. This approach is similar to extracting code into methods, but it goes further. Components reside in their own classes and files, making it simple to provide arguments and fully encapsulate their layout logic. ```csharp public class AddressComponent : IComponent { private string Title { get; } private Address Address { get; } public AddressComponent(string title, Address address) { Title = title; Address = address; } public void Compose(IContainer container) { container.Column(column => { column.Spacing(2); column.Item().BorderBottom(1).PaddingBottom(5).Text(Title).SemiBold(); column.Item().Text(Address.CompanyName); column.Item().Text(Address.Street); column.Item().Text($"{Address.City}, {Address.State}"); column.Item().Text(Address.Email); column.Item().Text(Address.Phone); }); } } ``` The code below demonstrates how to integrate and use the newly created component: ```csharp{11-16} public class InvoiceDocument : IDocument { /* code omitted */ void ComposeContent(IContainer container) { container.PaddingVertical(40).Column(column => { column.Spacing(5); column.Item().Row(row => { row.RelativeItem().Component(new AddressComponent("From", Model.SellerAddress)); row.ConstantItem(50); row.RelativeItem().Component(new AddressComponent("For", Model.CustomerAddress)); }); column.Item().Element(ComposeTable); var totalPrice = Model.Items.Sum(x => x.Price * x.Quantity); column.Item().AlignRight().Text($"Grand total: {totalPrice}$").FontSize(14).SemiBold(); if (!string.IsNullOrWhiteSpace(Model.Comments)) column.Item().PaddingTop(25).Element(ComposeComments); }); } /* code omitted */ } ``` ![example](/getting-started/step-5.webp) ## License For learning and evaluation, you can use the free QuestPDF Evaluation license. ```csharp QuestPDF.Settings.License = LicenseType.Evaluation; ``` ::: warning SUSTAINABLE AND FAIR LICENSE By offering free access to most users and premium licenses for larger organizations, the project maintains its commitment to excellence while ensuring sustainable, long-term development for all. The library is free to use for any individual or business with less than 1 million USD annual gross revenue, or operates as a non-profit organization, or is a FOSS project. More details can be found on the [QuestPDF Pricing](/pricing) and [QuestPDF License](/license/) pages. ::: --- --- url: /license/configuration.md --- # License configuration QuestPDF uses a hybrid license, which is a model that benefits everyone. Commercial licenses provide businesses with legal safety and long-term stability, while funding a feature-complete, unrestricted library for the open-source community. ::: tip The library is free for individuals, non-profits, all FOSS projects, and organizations under $1M in annual revenue. More details can be found on the [QuestPDF Pricing](/pricing) and [QuestPDF License](/license/) pages. ::: ## Software Activation We trust our users and clients. Therefore, the software does not require any license key. Instead, you can select and configure the appropriate license in your code. Please put one of the following lines at the startup of your application: ```csharp QuestPDF.Settings.License = LicenseType.Community; // or QuestPDF.Settings.License = LicenseType.Professional; // or QuestPDF.Settings.License = LicenseType.Enterprise; ``` The library does not perform any network calls, does not send any data to external servers, and does not collect any personal information. All computations are performed locally on your machine. ::: tip Please ensure that you are eligible for the chosen license before using it in your project. By choosing the right license, you help ensure that our project remains transparent, sustainable, and continuously improving for everyone. Thank you for supporting QuestPDF! ❤️ ::: --- --- url: /roadmap.md --- # Roadmap QuestPDF is built to be a dependable, long-term foundation for generating PDF documents in code. This page outlines where the library is heading, including the capabilities we are actively building and our strategic direction over the coming releases. This roadmap is a living document. Because we prioritize quality over rigid deadlines, we do not attach fixed dates to these items. It reflects our current intent rather than a binding delivery schedule. ## In progress What we're actively building right now. * **Support for more platforms and languages.** Foundational work is underway to bring the QuestPDF API to runtimes and languages beyond .NET, so more teams can rely on the same document engine regardless of their technology stack. * **Native AOT compilation support.** Full compatibility with .NET Native AOT. AOT compilation delivers faster startup times, smaller self-contained deployments, and a lower memory footprint — increasingly important for serverless functions, containerized services, and high-density cloud workloads. This removes a key adoption barrier for teams standardizing on AOT-first architectures. * **Introductory video and learning materials.** A concise video walkthrough of QuestPDF fundamentals, from your first document to real-world layouts. The goal is to shorten the path from evaluation to productive use — particularly for developers and teams adopting the library for the first time. ## Up next Confirmed direction for upcoming releases. These items are planned and prioritized; exact timing depends on scope and dependencies. * **Sample gallery with ready-to-use code.** A curated gallery of copy-and-paste code samples covering the most common document types — invoices, reports, certificates, and more — with complete, working implementations. Less boilerplate, faster implementation, and a proven starting point instead of a blank page. * **New layout elements, options, and enhancements.** Ongoing expansion of the layout engine with new elements, richer configuration options, and refinements to existing components. A broader, more expressive set of building blocks means fewer custom workarounds and more document designs that can be described directly and cleanly in code. * **Expanded and improved documentation.** Continued investment in documentation: broader coverage, clearer explanations, more end-to-end examples, and deeper guidance for advanced scenarios. Strong documentation lowers onboarding cost and reduces day-to-day friction for every team using QuestPDF. ## Future Future development plans include: * **Built-in PDF validation with veraPDF.** A first-class, built-in way to validate your generated documents against conformance standards using veraPDF — giving teams a straightforward, automated path to verify compliance directly within their own build and QA pipelines. * **Increased test coverage.** Ongoing expansion of the automated test suite across more layouts, edge cases, and rendering scenarios. Higher coverage translates directly into greater stability and predictability from release to release — a core reason teams trust QuestPDF in production-critical systems. * **PDF/A-4 and PDF/UA-2 conformance.** Support for the latest archival (PDF/A-4) and accessibility (PDF/UA-2) standards, building directly on the existing PDF/A-2, PDF/A-3, and PDF/UA-1 support. Essential for regulated industries, the public sector, and any organization with long-term archival or accessibility obligations. * **Content translation support.** Tooling to streamline generating the same document across multiple languages, making QuestPDF easier to adopt for teams serving international audiences and multi-market operations. * **PDF signing with X.509 certificates.** Built-in support for digitally signing documents using `X509Certificate` certificates. Digital signatures provide authenticity and tamper-evidence — a common requirement for contracts, invoices, and official documents across finance, legal, and government. * **Further performance and resource-efficiency improvements.** Continued, deliberate investigation into generation speed alongside CPU and memory usage, with the goal of pushing throughput higher and resource consumption lower. This matters most for high-volume, latency-sensitive, and cost-conscious workloads running at scale. * **Basic AcroForm support.** Programmatic creation of interactive form fields — text inputs, checkboxes, and similar controls — along with the ability to read submitted values back from existing form documents. This opens up fillable PDFs for use cases such as applications, surveys, and onboarding paperwork, and enables automated data capture from completed forms. * **PDF content inspection.** Reading and inspecting the contents of existing PDF files — extracting text and examining document structure programmatically. This extends QuestPDF beyond document creation into analysis, supporting scenarios such as content extraction, verification, and post-processing of documents your systems receive. ## Recently delivered QuestPDF is under active, continuous development. A selection of recent milestones: * **Enterprise-ready licensing and documentation.** Substantially revised legal documents to better support enterprise procurement and compliance requirements. * **Windows ARM64 native support.** Native execution on `win-arm64` environments. * **Companion App.** A visual companion for development: live document preview, layout-problem debugging, navigation from rendered output straight to the originating code, and content inspection — making it fast and intuitive to understand and fix exactly what your document is doing. * **Tagged PDF and semantic structure.** Automatic semantic tagging of document content, the foundation for accessible, machine-readable PDFs. * **PDF/UA-1, PDF/A-2, and PDF/A-3 conformance.** Support for accessibility and archival conformance standards across the PDF/A-2 and PDF/A-3 conformance levels and PDF/UA-1. * **Automated conformance and e-invoice validation.** QuestPDF's own output is continuously validated against conformance standards using veraPDF, and against ZUGFeRD / Factur-X requirements using the Mustang project. * **Advanced graphics capabilities.** Native support for linear gradients, rounded corners, customizable dash patterns for lines, and a dedicated shadow element with configurable blur, color, offset, and spread. Documents gain a polished, modern visual finish directly from code, with no external tooling required. * **Document operations API.** A dedicated API for working with existing PDF files: merge and split documents, apply password protection, add overlays and underlays (for watermarks, stationery, or background templates), attach external files, manage metadata, and embed e-invoice data. * **Custom text and graphics engine.** A custom Skia-based native layer replacing the previous SkiaSharp dependency, making QuestPDF a self-contained library with a rendering stack we control and update on a predictable cadence (currently tracking Skia M150). The same engine powers advanced typography (complex text shaping, right-to-left and bidirectional scripts, and automatic font subsetting), native SVG rendering, and document compression that meaningfully reduces output file size. --- --- url: /acknowledgements.md --- # Acknowledgements ::: details Six Labors Thank you for developing a fantastic graphics library for the .NET platform. Special thanks to James Jackson-South for so openly sharing his experience and know-how regarding the licensing opportunities for open-source projects. [Link to the official webpage](https://sixlabors.com/) ::: ::: details SkiaSharp license details Copyright (c) 2015-2016 Xamarin, Inc. \ Copyright (c) 2017-2018 Microsoft Corporation. [Link to the repository webpage](https://github.com/mono/SkiaSharp) \ [Link to the license](https://github.com/mono/SkiaSharp/blob/main/LICENSE.md) ::: ::: details Skia license details Copyright (c) 2024 Google BSD 3-Clause "New" or "Revised" License [Link to the repository webpage](https://github.com/google/skia/tree/main) \ [Link to the license](https://github.com/google/skia/blob/main/LICENSE) ::: ::: details qpdf license details Copyright (c) 2015-2016 Xamarin, Inc. \ Copyright (c) 2017-2018 Microsoft Corporation. [Link to the repository webpage](https://github.com/qpdf/qpdf) \ [Link to the license](https://github.com/qpdf/qpdf/blob/main/LICENSE.txt) ::: ::: details Vue license Copyright (c) 2013-present, Yuxi (Evan) You [Link to the repository webpage](https://github.com/vuejs/vue) \ [Link to the license](https://github.com/vuejs/vue/blob/dev/LICENSE) ::: ::: details VitePress license Copyright (c) 2018-present, Yuxi (Evan) You [Link to the repository webpage](https://github.com/vuejs/vitepress) \ [Link to the license](https://github.com/vuejs/vitepress/blob/main/LICENSE) ::: ::: details NUnit license Copyright (c) 2021 Charlie Poole, Rob Prouse [Link to the repository webpage](https://github.com/nunit/nunit) \ [Link to the license](https://github.com/nunit/nunit/blob/master/LICENSE.txt) ::: ::: details FluentAssertions license Copyright (c) 2021 Dennis Doomen [Link to the repository webpage](https://github.com/fluentassertions/fluentassertions) \ [Link to the license](https://github.com/fluentassertions/fluentassertions/blob/master/LICENSE) ::: ::: details .NET platform ecosystem Copyright (c) .NET Foundation and Contributors [Link to the repository webpage](https://github.com/dotnet) ::: --- --- url: /companion/usage.md --- # Companion App ## Introduction The QuestPDF Companion application is a tool designed to simplify and speed up your development lifecycle. First, it shows a preview of your document. But the real magic starts with the hot-reload capability! It observes your code and updates the preview every time you change the implementation. Get real-time results without the need of code recompilation. Save time and enjoy the task! ![Application screenshot](/companion/application-light.webp){.companion-screenshot .light-only} ![Application screenshot](/companion/application-dark.webp){.companion-screenshot .dark-only} :::info Read more about features availble in the Companion App in the [Features](/companion/features) section. ::: ## Installation The Companion App is available for download on Windows, MacOS, and Linux. | Operating System | Download link | |------------------------------------|--------------------------------------------------------------------------------------------------------------------------------| | Windows | [Download](https://github.com/QuestPDF/QuestPDF.Companion/releases/download/2026.2.2/questpdf_companion-2026.2.2-windows.msix) | | MacOS (64-bit Intel and Apple ARM) | [Download](https://github.com/QuestPDF/QuestPDF.Companion/releases/download/2026.2.2/QuestPDF.Companion.2026.2.2.dmg) | | Linux Debian | [Download](https://github.com/QuestPDF/QuestPDF.Companion/releases/download/2026.2.2/questpdf_companion-2026.2.2-linux.deb) | | Linux Fedora | [Download](https://github.com/QuestPDF/QuestPDF.Companion/releases/download/2026.2.2/questpdf_companion-2026.2.2-linux.rpm) | :::info To access older versions of the Companion App, visit the [Download](/companion/download) section. ::: ## Changes in your code To preview your document, you need to slightly modify your code. ```csharp{19,22} using QuestPDF.Fluent; using QuestPDF.Helpers; using QuestPDF.Infrastructure; using QuestPDF.Companion; // code in your main method var document = Document.Create(container => { container.Page(page => { // page content }); }); // instead of the standard way of generating a PDF file document.GeneratePdf("hello.pdf"); // use the following invocation document.ShowInCompanion(); // optionally, you can specify an HTTP port to communicate with the previewer host (default is 12500) document.ShowInCompanion(12345); ``` :::warning The QuestPDF Companion integration requires the library version **2024.10** or newer. If you cannot update, please use the legacy [QuestPDF Previewer application](/document-previewer.html). ::: ## How to use hot-reload ### Visual Studio Start your application in the DEBUG mode with the 'Hot Reload on Save' flag enabled. On every file save, the document will be refreshed. ![example](/companion/hot-reload-visual-studio.png) ### JetBrains Rider Start your application without debugger attached. To apply code changes, click on the `Apply changes` button displayed on the top bar, or use the `Alt+F10` shortcut. ![example](/companion/hot-reload-jetbrains-rider.png) ### Terminal Start your application using the following command: ```shell dotnet watch dotnet watch --project YourSampleProject ``` For unit tests: ```shell dotnet watch --project YourProjectWithTests test --filter "YourClassWithTests.TestMethodName" ``` --- --- url: /companion/download.md --- # Companion App: Downloads The QuestPDF Companion application is available for download on Windows, MacOS, and Linux. The application version is not tightly coupled with the library version. The application is backward compatible with older library versions, but it may not support all features of the latest library version. | Companion App version | Supported library versions | Download links | |-----------------------|----------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | **2026.2.2** (latest) | 2024.10.0 and newer | [Windows](https://github.com/QuestPDF/QuestPDF.Companion/releases/download/2026.2.2/questpdf_companion-2026.2.2-windows.msix) [MacOS](https://github.com/QuestPDF/QuestPDF.Companion/releases/download/2026.2.2/QuestPDF.Companion.2026.2.2.dmg) [Linux Debian](https://github.com/QuestPDF/QuestPDF.Companion/releases/download/2026.2.2/questpdf_companion-2026.2.2-linux.deb) [Linux Fedora](https://github.com/QuestPDF/QuestPDF.Companion/releases/download/2026.2.2/questpdf_companion-2026.2.2-linux.rpm) | | 2026.2.1 | 2024.10.0 - 2026.2.3 | [Windows](https://github.com/QuestPDF/QuestPDF.Companion/releases/download/2026.2.1/questpdf_companion-2026.2.1-windows.msix) [MacOS](https://github.com/QuestPDF/QuestPDF.Companion/releases/download/2026.2.1/QuestPDF.Companion.2026.2.1.dmg) [Linux Debian](https://github.com/QuestPDF/QuestPDF.Companion/releases/download/2026.2.1/questpdf_companion-2026.2.1-linux.deb) [Linux Fedora](https://github.com/QuestPDF/QuestPDF.Companion/releases/download/2026.2.1/questpdf_companion-2026.2.1-linux.rpm) | | 2024.10.8 | 2024.10.0 - 2026.2.3 | [Windows](https://github.com/QuestPDF/QuestPDF.Companion/releases/download/2024.10.8/QuestPDF.Companion.2024.10.8.exe) [MacOS](https://github.com/QuestPDF/QuestPDF.Companion/releases/download/2024.10.8/QuestPDF.Companion.2024.10.8.app.zip) [Linux](https://github.com/QuestPDF/QuestPDF.Companion/releases/download/2024.10.8/QuestPDF.Companion.2024.10.8.deb) | --- --- url: /companion/features.md --- # Companion: Features The companion app provides a preview of the document. The preview is interactive and allows you to navigate the document, select elements, and measure distances. ![Application screenshot](/companion/application-light.webp){.companion-screenshot .light-only} ![Application screenshot](/companion/application-dark.webp){.companion-screenshot .dark-only} ## Document hierarchy Document hierarchy is a tree structure that represents the document content. The hierarchy is displayed in the left panel of the companion app. The hierarchy allows you to quickly navigate the document content and select elements. The tree-structure uses a similar compact concept as C# Fluent API. Each node in the hierarchy represents an element in the document. Please note that certain API calls may produce more advanced hierarchy structures. The hierarchy is interactive, and you can expand and collapse nodes to navigate the document content. | Shortcut | Description | |------------|--------------------------------| | ctrl + W | Hide / show document hierarchy | ![Document hierarchy hidden](/companion/hierarchy-hidden-light.webp){.companion-screenshot .light-only} ![Document hierarchy hidden](/companion/hierarchy-hidden-dark.webp){.companion-screenshot .dark-only} ## Document preview The document preview section (on the right side of the screen) displays the document content. You can interact with the preview in many ways, such as moving the preview, zooming in and out, and measuring distances. | Shortcut | Description | |----------------------|---------------------------------------------------------------| | click and drag | Move preview | | scroll wheel | Scroll vertically | | shift + scroll wheel | Scroll horizontally | | ctrl + scroll wheel | Zoom | | 1 | Magnifier | | 2 | Show coordinates | | 3 | Measure vertically | | 4 | Measure horizontally | | double click | Select element | | alt + click | Open link (hyperlink or section link) | | ctrl + click | Show implementation of selected area in code editor | | ctrl + E | Fit entire page on the screen (hover cursor over target page) | ## Magnifier Use the magnifier feature (shortcut: `key 1`) to quickly see document's structure details without the need of zooming and adjusting the preview. ![Document preview magnifier](/companion/magnifier-light.webp){.companion-screenshot .light-only} ![Document preview magnifier](/companion/magnifier-dark.webp){.companion-screenshot .dark-only} ## Coordinate picker The coordinate picker feature (shortcut: `key 2`) allows you to pick the coordinates of the selected element. This feature is useful when you need to know the position of an element in the document. ![Document content coordinate picker](/companion/measurement-point-light.webp){.companion-screenshot .light-only} ![Document content coordinate picker](/companion/measurement-point-dark.webp){.companion-screenshot .dark-only} ## Size measurement This feature allows you to measure the size of visual elements in the document, as well as the distance between elements. You can measure the size of the content vertically (shortcut: `key 3`) or horizontally (shortcut: `key 4`). ![Document content vertical measurement](/companion/measurement-vertical-light.webp){.companion-screenshot .light-only} ![Document content vertical measurement](/companion/measurement-vertical-dark.webp){.companion-screenshot .dark-only} ![Document content horizontal measurement](/companion/measurement-horizontal-light.webp){.companion-screenshot .light-only} ![Document content horizontal measurement](/companion/measurement-horizontal-dark.webp){.companion-screenshot .dark-only} ## Element selection To select an element in the document, click on it in the document hierarchy section, or double-click on the content displayed in the document preview section. The selected element is highlighted in the preview. Once the element is selected, you can review its details in the appropriate panel, such as configuration, position and size. If the element is visible on multiple pages, you can use arrows keys to navigate between all occurrences. | Shortcut | Description | |------------|-----------------------------| | arrow up | Previous element occurrence | | arrow down | Next element occurrence | | esc | Clear element selection | ![Selected element](/companion/selection-light.webp){.companion-screenshot .light-only} ![Selected element](/companion/selection-dark.webp){.companion-screenshot .dark-only} ## Content searching Quickly navigate the document content by searching for a specific phrase. To search for a phrase, press `ctrl + F`. The search bar appears at the top of the structure tree view. Enter the phrase you want to search for. The selected search result is highlighted in both structure tree view and on the document's preview. You can navigate between the search results using the arrow keys (`up` and `down`). | Shortcut | Description | |------------|------------------------| | ctrl + F | Search by phrase | | esc | Exit search mode | | arrow up | Previous found element | | arrow down | Next found element | ![Content searching feature](/companion/search-light.webp){.companion-screenshot .light-only} ![Content searching feature](/companion/search-dark.webp){.companion-screenshot .dark-only} ## Go to implementation The companion app allows you to quickly navigate to the implementation of the desired area in the code editor. To do this, hold the `ctrl` key and click on the desired area. The code editor will open with the implementation of the selected area. ::: warning The hot-reload feature may limit the accuracy of this feature. The first document load produces the most accurate results. Hot-reloaded documents provide less precise navigation. ::: ## Document links The companion app allows you to open links in the document. To open a link, hold the `alt` key and click on the link. There are two types of links in the document: * Hyperlinks: links to web pages open in a new browser tab, * Section links: links to sections in the document move the preview to the target section. ## Runtime exceptions The companion app provides a detailed view of runtime exceptions that occur during document generation. The exception details are displayed in the error panel. You can review the exception message, stack trace, and the source code that caused the exception. ![Runtime exception visualization](/companion/generic-exception-light.webp){.companion-screenshot .light-only} ![Runtime exception visualization](/companion/generic-exception-dark.webp){.companion-screenshot .dark-only} ## Layout issue debugging In case of layout issues, the companion app provides a set of tools to help you identify and resolve the problem. If a document contains multiple layout issues, you can navigate between them using the arrow buttons (`up` and `down`). Each element in the document structure view will be annotated with a color-coded dot with the following meaning. | Color | Meaning | Description | |----------------------------------------------------------------------------|-------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | | Layout issue root cause | Element that is likely the root cause of the layout issue based on library heuristics and prediction. | | | Wrapped | Element that cannot be drawn due to the provided layout constraints. This element likely causes the layout issue, or one of its descendant children is responsible for the problem. | | | Partially rendered | Element that can be partially drawn on the page and will also be rendered on the consecutive page. In more complex layouts, this element may also cause issues or contain a child that is the actual root cause. | | | Fully rendered | Element that is successfully and completely drawn on the page. | | | Empty | Element that has been drawn on the faulty page but took no space. | | | Not rendered | Element that has not been drawn on the faulty page. Its children are omitted. | When an element is selected, additional information about the layout issue is displayed in the element details panel. You can review the reason for the layout wrap or layout overflow. Use that hint and your knowledge about structure elements behavior to resolve the layout issue. ![Layout error visualization](/companion/layout-error-light.webp){.companion-screenshot .light-only} ![Layout error visualization](/companion/layout-error-dark.webp){.companion-screenshot .dark-only} ## Settings The companion app provides a set of customization options to adjust the appearance and behavior of the previewer, including: * Light and dark theme, * Default IDE for code navigation, * Changing port number on which the application will communicate. ![Settings page](/companion/settings-light.webp){.companion-screenshot .light-only} ![Settings page](/companion/settings-dark.webp){.companion-screenshot .dark-only} --- --- url: /companion/warnings.md --- # Companion App: Warning Messages ## Complex document **Reason:** This warning message is displayed when the document contains complex content. The hot-reload performance may be impacted. **Solution:** Please consider using simpler and shorter content while working on the document's design. ## Hot-reload **Reason:** This warning message is displayed when the content preview is refreshed using hot-reload. Modern dotnet hot-reload feature has certain limitations that may impact accuracy of a stack trace collection. As a result, any feature related to code navigation may not be as precise as expected. **Solution:** If code navigation is crucial, please consider using the dotnet watch command instead of hot-reload: `dotnet watch --no-hot-reload`. --- --- url: /document-previewer.md --- # Document previewer ::: danger LEGACY TOOL The QuestPDF Previewer has been replaced by the [Companion App](/companion/usage), introduced in version 2024.10. This page is kept for users of older library versions. If you are using QuestPDF 2024.10 or newer, please use the Companion App instead — the Previewer is no longer maintained and does not support features added since 2024.10. Note that the `ShowInCompanion()` method requires the Companion App. It will not work with the Previewer tool, even though both use the same default port. ::: ## Introduction The QuestPDF Previewer is a tool designed to simplify and speed up your development lifecycle. First, it shows a preview of your document. But the real magic starts with the hot-reload capability! It observes your code and updates the preview every time you change the implementation. Get real-time results without the need of code recompilation. Save time and enjoy the task! ::: warning The hot-reload feature is available only in the .NET 6 environment and beyond. ::: ## Installation The Previewer tool is available as a NuGet tool. Therefore, it is installed on your local development environment and does not change your project. 📁 To install the QuestPDF Previewer, please execute the following command on your PC: ```csharp dotnet tool install QuestPDF.Previewer --global ``` 🚀 Optional: you can start an independent previewer application: ``` questpdf-previewer // specify HTTP port on which previewer will communicate (default is 12500) questpdf-previewer 12345 ``` 🔁 To update the tool, please use: ```csharp dotnet tool update questpdf.previewer --global ``` 🗑️ And to remove: ```csharp dotnet tool uninstall questpdf.previewer --global ``` ### Changes in your code To preview your document, you need to slightly modify your code. ```csharp{17-18} using QuestPDF.Fluent; using QuestPDF.Helpers; using QuestPDF.Infrastructure; using QuestPDF.Previewer; // code in your main method var document = Document.Create(container => { container.Page(page => { // page content }); }); // instead of the standard way of generating a PDF file document.GeneratePdf("hello.pdf"); // use the following invocation document.ShowInPreviewer(); // optionally, you can specify an HTTP port to communicate with the previewer host (default is 12500) document.ShowInPreviewer(12345); ``` ## How to use hot-reload ### Visual Studio Start your application in the DEBUG mode with the 'Hot Reload on Save' flag enabled. On every file save, the document will be refreshed. ![example](/previewer/visual-studio.png) ### JetBrains Rider Start your application without debugger attached. To apply code changes, click on the "Apply changes" button displayed on the top bar, or use the `Alt+F10` shortcut. ![example](/previewer/jetbrains-rider.png) ### Terminal Start your application using the following command: ``` dotnet watch ``` --- --- url: /concepts/generating-output.md --- # Generating output The primary goal of the QuestPDF library is to generate PDF files. However, it also supports other output formats such as XPS, SVG and images. ::: warning Please be aware that certain features may not be available on formats other than PDF. ::: ## Generating PDF files There are several overloads for generating PDF files: ```csharp var document = Document.Create(document => { document.Page(page => { page.Content().Text("Your invoice content"); }); }); // generate PDF and save it to a file document.GeneratePdf("document.pdf"); // generate PDF and return it as a byte array var byteArray = document.GeneratePdf(); // generate PDF and save it to a stream using var stream = new FileStream("document.pdf", FileMode.Create); document.GeneratePdf(stream); ``` ## Generating XPS files The library also supports generating XPS files: ```csharp // generate XPS and save it to a file document.GenerateXps("document.xps"); // generate XPS and return it as a byte array var byteArray = document.GenerateXps(); // generate XPS and save it to a stream using var stream = new FileStream("document.xps", FileMode.Create); document.GenerateXps(stream); ``` ::: warning Please note that generating XPS files is only supported on Windows operating systems. ::: ## Generating SVG files The library also supports generating SVG files. Each page is represented as a separate SVG file. ```csharp ICollection svgFiles = document.GenerateSvgFiles(); ``` ## Generating images The library also supports generating images. Each page is represented as a separate image file. ```csharp // generate images and return them as byte arrays IEnumerable imagesAsByteArrays = document.GenerateImages(); // save images to files document.GenerateImages(imageIndex => $"image{imageIndex}.png"); ``` Optionally, you can provide additional generation settings: | Property | Description | |------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | **ImageFormat** | The file format used to encode the images. Default is `PNG`. | | **ImageCompressionQuality** | Encoding quality controls the trade-off between size and quality. The default value is `high`. | | **RasterDpi** | The DPI (pixels-per-inch) at which the document will be rasterized. This controls the resolution of produced images. Higher DPI results in superior image quality but may increase the output file size. Default value is `288`. | | **UseTransparentBackground** | When enabled, the generated images have a transparent background instead of a white one. Applies only to image formats that support transparency (`PNG` and `WEBP`). | ```csharp{3-9} using QuestPDF.Infrastructure; var imageGenerationSettings = new ImageGenerationSettings { ImageFormat = ImageFormat.Png, ImageCompressionQuality = ImageCompressionQuality.High, RasterDpi = 288, UseTransparentBackground = false }; IEnumerable imagesAsByteArrays = document.GenerateImages(imageGenerationSettings); // or document.GenerateImages(imageIndex => $"image{imageIndex}.png", imageGenerationSettings); ``` --- --- url: /concepts/global-settings.md --- # Settings QuestPDF provides several configurable settings to fine-tune the document generation process. These settings are accessible via the static `QuestPDF.Settings` class. ## Caching This flag generates additional document elements to cache layout calculation results. In the vast majority of cases, this significantly improves performance, while slightly increasing memory consumption. ```csharp // enabled by default QuestPDF.Settings.EnableCaching = true; ``` ## Debugging This flag generates additional document elements to improve layout debugging experience. When the provided content contains size constraints impossible to meet, the library generates an enhanced exception message with additional location and layout measurement details. ```csharp // by default, enabled only when debugger is attached QuestPDF.Settings.EnableDebugging = false; ``` ## Checking Font Glyph Availability This flag enables checking the font glyph availability. If your text contains glyphs that are not present in the specified font: * when this flag is **enabled**: the `DocumentDrawingException` is thrown. * when this flag is **disabled**: placeholder characters are visible in the produced PDF file. ::: info Enabling this flag may slightly decrease document generation performance. However, it provides hints that used fonts are not sufficient to produce correct results. ::: ```csharp // by default, enabled only when debugger is attached QuestPDF.Settings.CheckIfAllTextGlyphsAreAvailable = false; ``` ## Using System Fonts Decides whether the application should use the fonts available in the environment: * when this flag is **enabled**: the application will use the fonts installed on the system where it is running. This is the default behavior. * when this flag is **disabled**: the application will only use the fonts that have been registered using the `FontManager` class in the QuestPDF library. This property is useful when you want to control the fonts used by your application, especially in cases where the environment might not have the necessary fonts installed. ```csharp // enabled by default QuestPDF.Settings.UseEnvironmentFonts = true; ``` ## Font Discovery Paths Specifies the collection of paths where the library will automatically search for font files to register. By default, this collection contains the application files path. You can add additional paths to this collection to include more directories for automatic font registration. ```csharp QuestPDF.Settings.FontDiscoveryPaths.Add("/custom/font/directory"); ``` --- --- url: /concepts/document-metadata.md --- # Document metadata It is possible to include additional information about the PDF document. This metadata is stored in the PDF file and can be viewed in the document properties. ```csharp{9-19} Document .Create(document => { document.Page(page => { page.Content().Text("Your invoice content"); }); }) .WithMetadata(new DocumentMetadata { Title = "Invoice", Author = "John Doe", Subject = "Invoice for services", Keywords = "invoice, services, payment", Creator = "MyApplication", Producer = "PdfRpt", Language = "en-US", CreationDate = DateTimeOffset.Now, ModifiedDate = DateTimeOffset.Now }) .GeneratePdf("document.pdf"); ``` | Property | Description | |------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------| | **Title** | Represents the main heading or name of the document, often displayed as a prominent identifier or label in PDF metadata. | | **Author** | Specifies the individual or entity responsible for creating the document. | | **Subject** | Provides a brief description or main topic related to the document content. | | **Keywords** | Defines a collection of terms or phrases that describe the document's content or purpose, improving categorization and searchability. | | **Creator** | Identifies the software or system that generated the document. | | **Producer** | Specifies the name of the application or library that generated the document. | | **Language** | Specifies the language of the document content, defined using language tags such as "en-US" for American English. Should use the ISO 639-2 Specification. | | **CreationDate** | Represents the date and time when the document was created. This property is used to specify the creation timestamp. | | **ModifiedDate** | Stores the most recent date and time when the content or metadata of the document was updated, providing information about the last revision of the document. | --- --- url: /concepts/document-settings.md --- # Document Settings QuestPDF provides comprehensive control over the document generation process through the `DocumentSettings` class. These settings allow you to fine-tune various aspects of your PDF output, including compliance standards, compression, image quality, and content direction. ```csharp Document .Create(document => { document.Page(page => { page.Content().Text("Your document content"); }); }) .WithSettings(new DocumentSettings { PDFA_Conformance = PDFA_Conformance.PDFA_3B, PDFUA_Conformance = PDFUA_Conformance.None, CompressDocument = true, ImageCompressionQuality = ImageCompressionQuality.High, ImageRasterDpi = 288, ContentDirection = ContentDirection.LeftToRight }) .GeneratePdf("document.pdf"); ``` | Property | Description | |-----------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | **PDFA\_Conformance** | Specifies the PDF/A archival standard compliance level. Note: This setting makes the document non-reproducible. | | **PDFUA\_Conformance** | Sets the PDF/UA (Universal Accessibility) compliance level for accessibility standards. Note: This setting makes the document non-reproducible. | | **CompressDocument** | Indicates whether the generated document should be additionally compressed. Compression may significantly reduce file size with a minor increase in generation time. | | **ImageCompressionQuality** | Controls the trade-off between file size and image quality. If the image is opaque, it is encoded using JPEG with the selected quality setting. If the image contains an alpha channel, it is always encoded in PNG format, ignoring this setting. | | **ImageRasterDpi** | Defines the DPI (dots per inch) at which images and non-PDF-supported features are rasterized. A higher DPI results in better fidelity but increases file size and memory usage. This setting also controls the resolution of generated images. Default is `288 DPI`. | | **ContentDirection** | Specifies the default text direction for content layout (e.g., Left-to-Right or Right-to-Left). | --- --- url: /concepts/merging-documents.md --- # Merging documents QuestPDF makes it easy to generate multiple PDF documents and then combine them into one. You can merge documents while either preserving each document’s original page numbers or creating a continuous page numbering sequence throughout the merged output. QuestPDF supports two merging strategies regarding page numbering. Choose the one that best suits your needs. ::: warning This feature can be used only while generating PDF documents. To merge existing files, please use the [Document Operations](/concepts/document-operations) feature. ::: ## Generating sample document Before merging, you need to create individual documents. The sample code below defines a helper method that creates a report with a header, content area, and a footer displaying page numbers. ```csharp static Document GenerateReport(string title, int itemsCount) { return Document.Create(document => { document.Page(page => { page.Size(PageSizes.A5); page.Margin(0.5f, Unit.Inch); page.Header() .Text(title) .Bold() .FontSize(24) .FontColor(Colors.Blue.Accent2); page.Content() .PaddingVertical(20) .Column(column => { column.Spacing(10); foreach (var i in Enumerable.Range(0, itemsCount)) { column .Item() .Width(200) .Height(50) .Background(Colors.Grey.Lighten3) .AlignMiddle() .AlignCenter() .Text($"Item {i}") .FontSize(16); } }); page.Footer() .AlignCenter() .PaddingVertical(20) .Text(text => { text.DefaultTextStyle(TextStyle.Default.FontSize(16)); text.CurrentPageNumber(); text.Span(" / "); text.TotalPages(); }); }); }); } ``` ## Original page numbers Documents maintain their own page numbers upon merging, without continuity between them. As a result, APIs related to page numbers reflect individual documents, not the cumulative count. All documents are simply be merged together. **Example**: Merging a two-page document with a three-page document results in a sequence: 1, 2, 1, 2, 3. ```csharp{6} Document .Merge( GenerateReport("Short Document 1", 5), GenerateReport("Medium Document 2", 10), GenerateReport("Long Document 3", 15)) .UseOriginalPageNumbers() .GeneratePdf("merged.pdf"); ``` ## Continuous page numbers Consolidates the content from every document, creating a continuous seamless one. Page number APIs return a consecutive numbering for this unified document. Merging a two-page document with a three-page document results in a sequence: 1, 2, 3, 4, 5. ```csharp{6} Document .Merge( GenerateReport("Short Document 1", 5), GenerateReport("Medium Document 2", 10), GenerateReport("Long Document 3", 15)) .UseContinuousPageNumbers() .GeneratePdf("merged.pdf"); ``` --- --- url: /concepts/common-exceptions.md --- # Exceptions While developing with QuestPDF, you might encounter issues during the PDF rendering process. Understanding the potential sources of these exceptions, their root causes, and the appropriate fixes is crucial. QuestPDF categorizes exceptions into three distinct groups: ::: tip For enhanced development and debugging experience, please consider using [the QuestPDF Companion App](/companion/usage). ::: ## DocumentComposeException This exception arises during the document composition phase, where you use the Fluent API to assemble various elements and create the final layout. Common tasks in this phase include working with your input data, applying conditions, iterating through loops, and calling additional methods. #### Possible Causes: * Invalid or unexpected data being processed. * Incorrect or incomplete API usage. * Errors in custom logic such as conditions or loops. #### Resolution: * Review the stack trace to identify the problematic code. * Validate input data before composing the document. * Debug your composition logic for potential issues in method calls or API interactions. ## DocumentDrawingException This exception occurs during the document generation phase, when the rendering engine converts the layout tree into drawing commands. Unlike DocumentComposeException, this type typically stems from internal issues or problems with custom components. ::: info If you encounter this exception, it could indicate a bug in the QuestPDF library. Please reach out to our support team with the error details, and we’ll work to resolve it promptly. ::: ::: warning If you are using [Dynamic Components](/concepts/code-patterns/dynamic-components), all exceptions thrown there are going to bubble up as this type of exception. In such case, please review the implementation of your dynamic components. ::: ## DocumentLayoutException This exception can be challenging to resolve as it occurs with valid document trees that impose constraints impossible to satisfy. For instance, attempting to draw a rectangle larger than the available page space triggers the rendering engine to wrap the content, hoping sufficient space will be available on the next page. ### Enhanced Debugging Context When the `QuestPDF.Settings.EnableDebugging` is set to `true`, or the debugger is attached, the library provides additional information to help you diagnose and resolve layout issues. ### Example The code below contains conflicting size constraints. ```csharp{2,8} .Padding(10) .Width(100) .Background(Colors.Grey.Lighten3) .DebugPointer("Example debug pointer") .Column(x => { x.Item().Text("Test"); x.Item().Width(150); // requires 150pt width where only 100pt is available }); ``` And generates the following exception: ``` The provided document content contains conflicting size constraints. For example, some elements may require more space than is available. The layout issue is likely present in the following part of the document: -> Document -> Page -> Page -> Content -> Content -> In method: content Called from: Render Source path: /Users/marcinziabek/RiderProjects/QuestPDF/Source/QuestPDF.Examples/Engine/RenderingTest.cs Line number: 100 -> Example debug pointer To learn more, please analyse the document measurement of the problematic location: 🔴 Column ========== Available Space: (Width: 100,000, Height: 340,000) Space Plan: Wrap Wrap Reason: The available space is not sufficient for even partially rendering a single item. ---------- ⚪️ TextBlock ============= Alignment: Start Content Direction: LeftToRight Line Clamp: - Line Clamp Ellipsis: - Paragraph Spacing: 0 Paragraph First Line Indentation: 0 Text: Test 🚨 Constrained 🚨 ================== Available Space: (Width: 100,000, Height: 340,000) Space Plan: Wrap Wrap Reason: The available horizontal space is less than the minimum width. ------------------ Content Direction: LeftToRight Min Width: 150 Max Width: 150 Min Height: - Max Height: - Enforce Size When Empty: False 🟢 Empty ========= Available Space: (Width: 0,000, Height: 0,000) Space Plan: FullRender (Width: 0,000, Height: 0,000) --------- Legend: 🚨 - Element that is likely the root cause of the layout issue based on library heuristics and prediction. 🔴 - Element that cannot be drawn due to the provided layout constraints. This element likely causes the layout issue, or one of its descendant children is responsible for the problem. 🟡 - Element that can be partially drawn on the page and will also be rendered on the consecutive page. In more complex layouts, this element may also cause issues or contain a child that is the actual root cause. 🟢 - Element that is successfully and completely drawn on the page. ⚪️ - Element that has not been drawn on the faulty page. Its children are omitted. ``` --- --- url: /concepts/document-operations.md --- # PDF Document Operations The Document Operations API provides functionality for performing various operations on PDF documents, including loading, merging, overlaying, underlaying, selecting specific pages, adding attachments, and applying encryption settings. :::info Features presented in this sections are created using the [qpdf](https://github.com/qpdf/qpdf) library, available under the "Apache-2.0" license. We extend our thanks to the authors of qpdf for their contributions to the open-source community. The code of qpdf library has been extended by QuestPDF to support important PDF/A-3b compliance requirements as well ZUGFeRD metadata extension. ::: :::warning Features presented in this section are available starting from the **2024.12.0** version of the library. ::: ## Loading Documents The `LoadFile` method loads a PDF file for processing, enabling operations such as merging, overlaying or underlaying content, selecting pages, adding attachments, and encrypting. ```csharp // Load an unprotected document and save DocumentOperation .LoadFile("input.pdf") .Save("output.pdf"); // Load a password-protected document and save var operation = DocumentOperation .LoadFile("protected.pdf", "password123") .Save("unprotected-output.pdf"); ``` ## Page Selection The `TakePages` method selects specific pages from the current document based on the provided page selector, marking them for further operations. ```csharp // Select specific pages DocumentOperation .LoadFile("input.pdf") .TakePages("1,3,5-10") .Save("selected-pages.pdf"); ``` ## Page Range Format | Example | Description | |--------------------|---------------------------------------------------------------------------------| | 1 | Single page numbers start from 1 (first page) | | r2 | Prefix `r` counts from end - second-to-last page | | z | Letter `z` represents the last page (same as `r1`) | | 1,6,4 | Pages can appear in any order when separated by commas | | 3-7 | Pages 3 through 7 in ascending order (inclusive range) | | 7-3 | Pages 7 through 3 in descending order (when first number is higher) | | 1-z | All pages in ascending order | | z-1 | All pages in descending order | | 1,3,5-9,15-12 | Pages 1, 3, 5-9 ascending, then 15-12 descending | | r3-r1 | Last three pages | | 1-20:even | Suffix `:even` or `:odd` selects only pages in those positions from final range | | 1-10,x3-4 | Prefix `x` excludes specified pages from previous range | | 4-10,x7-9,12-8,xr5 | Pages 4-10 (except 7-9), then 12-8 descending (except 5th from end) | ## Document Linearization Linearization creates web-optimized output files. Linearized files are structured to allow compliant PDF readers to begin displaying content before the entire file is downloaded. Normally, a PDF reader requires the entire file to be present to render content, as essential cross-reference data typically appears at the file's end. ```csharp DocumentOperation .LoadFile("input.pdf") .Linearize() .Save("web-optimized.pdf"); ``` ## Merging Documents The `MergeFile` method merges pages from the specified PDF file into the current document, according to the provided page selection. Simple example of merging two documents: ```csharp DocumentOperation .LoadFile("document1.pdf") .MergeFile("document2.pdf") .Save("merged.pdf"); ``` Example of merging multiple documents at once: ```csharp DocumentOperation .LoadFile("document1.pdf") .MergeFile("document2.pdf") .MergeFile("document3.pdf") // more files... .Save("merged.pdf"); ``` Advanced example where two documents are merged with specific page selections: ```csharp DocumentOperation .LoadFile("document1.pdf") .TakePages("1-5") // Take first 5 pages from document1 .MergeFile("document2.pdf", "1,3,5") // Take specific pages from document2 .Save("merged-selected.pdf"); ``` ## Overlays and Underlays ### Configuration Options | Option | Description | |-------------------|---------------------------------------------------------------------------------------------------------------------------------| | **FilePath** | The file path of the overlay/underlay PDF file to be used | | **TargetPages** | Specifies the range of pages in the output document where the overlay/underlay will be applied | | **SourcePages** | Specifies the range of pages in the overlay/underlay file to be used initially | | **RepeatSourcePages** | Specifies an optional range of pages in the overlay/underlay file that will repeat after the initial source pages are exhausted | A simple overlay example: ```csharp DocumentOperation .LoadFile("input.pdf") .OverlayFile(new LayerConfiguration { FilePath = "watermark.pdf" }) .Save("output-with-watermark.pdf"); ``` More complex overlay example with specific page selections: ```csharp DocumentOperation .LoadFile("input.pdf") .OverlayFile(new LayerConfiguration { FilePath = "watermak.pdf", TargetPages = "1-z", // Apply to all pages SourcePages = "1", // Use first page initially RepeatSourcePages = "1" // Repeat first page throughout }) .Save("output-with-watermark.pdf"); ``` ## Document Encryption ### Base Encryption Settings | Setting | Description | |---------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | UserPassword | The user password for the PDF, allowing restricted access based on encryption settings. May be left null to enable opening the PDF without a password | | OwnerPassword | The owner password for the PDF, granting full access to all document features. An empty owner password is considered insecure, as is using the same value for both user and owner passwords | ### Encryption Types and Permissions | Permission | 40-bit | 128-bit | 256-bit | Description | |------------------------|--------|---------|---------|-------------------------------------------------------------------------------------------------------------------| | AllowAnnotation | ✓ | ✓ | ✓ | Allows adding or modifying text annotations | | AllowContentExtraction | ✓ | ✓ | ✓ | Allows copying text and graphics | | AllowModification | ✓ | ✓ | ✓ | Allows modifying the document content. Independent of the annotation, form filling, and page assembly permissions | | AllowPrinting | ✓ | ✓ | ✓ | Allows printing the document | | AllowAssembly | - | ✓ | ✓ | Allows inserting, rotating, or deleting pages and creating bookmarks | | AllowFillingForms | - | ✓ | ✓ | Allows filling in form fields | | EncryptMetadata | - | ✓ | ✓ | Controls whether document metadata is encrypted | Starting with the 2026.7.2 version, the `AllowModification` permission is also supported for 128-bit and 256-bit encryption. All of the properties listed above default to `true`, so you only need to set the ones you want to change. 40-bit encryption example: ```csharp DocumentOperation .LoadFile("input.pdf") .Encrypt(new Encryption40Bit { UserPassword = "user123", OwnerPassword = "owner456", AllowPrinting = true, AllowModification = false, AllowContentExtraction = false, AllowAnnotation = true }) .Save("encrypted-40bit.pdf"); ``` 128-bit encryption example: ```csharp DocumentOperation .LoadFile("input.pdf") .Encrypt(new Encryption128Bit { UserPassword = "user123", OwnerPassword = "owner456", AllowPrinting = true, AllowModification = false, AllowContentExtraction = false, AllowFillingForms = true, AllowAssembly = false, AllowAnnotation = true, EncryptMetadata = true }) .Save("encrypted-128bit.pdf"); ``` 256-bit encryption example: ```csharp DocumentOperation .LoadFile("input.pdf") .Encrypt(new Encryption256Bit { UserPassword = "user123", OwnerPassword = "owner456", AllowPrinting = true, AllowModification = false, AllowContentExtraction = false, AllowFillingForms = true, AllowAssembly = false, AllowAnnotation = true, EncryptMetadata = true }) .Save("encrypted-256bit.pdf"); ``` ## Document Decryption Removes any existing encryption from the current PDF document, effectively making it accessible without a password or encryption restrictions. It is also possible to remove security restrictions associated with digitally signed PDF files. ```csharp DocumentOperation .LoadFile("input.pdf", "password") .Decrypt() .RemoveRestrictions() .Save("encrypted-256bit.pdf"); ``` ## File Attachments A simple attachment example: ```csharp DocumentOperation .LoadFile("input.pdf") .AddAttachment(new DocumentAttachment { FilePath = "data.csv", Description = "Supporting data table", MimeType = "text/csv" }) .Save("with-attachment.pdf"); ``` ### Configuration Options | Option | Description | |------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------| | **Key** | Sets the key for the attachment, specific to the PDF format. Defaults to the file name without its path | | **FilePath** | The file path of the attachment. The specified file must exist | | **AttachmentName** | Specifies the display name for the attachment. This name is typically shown to the user and used by most graphical PDF viewers when saving the file | | **CreationDate** | Specifies the creation date of the attachment. Defaults to the file's creation time | | **ModificationDate** | Specifies the modification date of the attachment. Defaults to the file's last modified time | | **MimeType** | Specifies the MIME type of the attachment (e.g., "text/plain", "application/pdf", "image/png") | | **Description** | Sets a description for the attachment, which may be displayed by some PDF viewers | | **Replace** | Indicates whether to replace an existing attachment with the same key | | **Relationship** | Specifies the relationship of the embedded file to the document for PDF/A-3b compliance | ### Attachment Relationships | Relationship | Description | |--------------|------------------------------------------------------------------------------------------| | **Data** | Indicates data files relevant to the document (e.g., supporting datasets or data tables) | | **Source** | Represents a source file directly used to create the document | | **Alternative** | An alternative representation of the document content (e.g., XML, HTML) | | **Supplement** | A file supplementing the content, like additional resources | | **Unspecified** | No specific relationship is defined | ### PDF/A-3b Compliant Attachment The PDF/A-3b standard requires that all attachments provide more information about their relationship to the document. The `Relationship` property is used to specify the attachment's relationship to the document content. ```csharp DocumentOperation .LoadFile("input.pdf") .AddAttachment(new DocumentAttachment { FilePath = "invoice-data.xml", Description = "Invoice XML data", MimeType = "application/xml", Relationship = DocumentAttachmentRelationship.Alternative, CreationDate = DateTime.UtcNow, ModificationDate = DateTime.UtcNow, Replace = true }) .Save("pdfa3b-compliant.pdf"); ``` ## XMP Metadata Extension The `ExtendMetadata` method extends the current document's XMP metadata by adding content within the `rdf:Description` tag. This allows for adding additional descriptive metadata to the PDF, which is useful for compliance standards like PDF/A or for industry-specific metadata (e.g., ZUGFeRD). ::: warning Please ensure that the document is PDF/A-3b compliant before extending its metadata. ::: ```csharp var path = "test.pdf"; // step 1: generate a PDF document Document .Create(document => { document.Page(page => { page.Content().Text("Your invoice content"); }); }) .WithSettings(new DocumentSettings() { PdfA = true }) // important .GeneratePdf(path); // step 2: extend the metadata of the PDF document var metadata = @" Example metadata "; DocumentOperation .LoadFile(path) .ExtendMetadata(metadata) .Save("testWithMeta.pdf"); ``` ## Advanced Examples ### Combining Multiple Operations ```csharp DocumentOperation .LoadFile("input.pdf") .TakePages("1-10") // Select first 10 pages .MergeFile("appendix.pdf", "1-z") // Append all pages from appendix .OverlayFile(new LayerConfiguration { FilePath = "watermark.pdf", TargetPages = "1-z" }) .AddAttachment(new DocumentAttachment { FilePath = "metadata.xml", Relationship = DocumentAttachmentRelationship.Supplement }) .Encrypt(new Encryption256Bit { OwnerPassword = "owner456", AllowPrinting = true, AllowContentExtraction = false }) .Linearize() // Optimize for web .Save("final-document.pdf"); ``` --- --- url: /concepts/prototyping.md --- # Prototyping Placeholders in QuestPDF let you quickly generate random text, numbers, colors, and images. They are useful for prototyping document layouts or creating test data when real information is not yet available. This guide outlines how to use each type of placeholder. ## Text QuestPDF provides a range of text placeholders that cover common scenarios: ```csharp using QuestPDF.Helpers; Placeholders.LoremIpsum(); Placeholders.Label(); Placeholders.Sentence(); Placeholders.Question(); Placeholders.Paragraph(); Placeholders.Paragraphs(); Placeholders.Email(); Placeholders.Name(); Placeholders.PhoneNumber(); Placeholders.WebpageUrl(); Placeholders.Time(); Placeholders.ShortDate(); Placeholders.LongDate(); Placeholders.DateTime(); Placeholders.Integer(); Placeholders.Decimal(); Placeholders.Percent(); ``` #### Example ```csharp .Column(column => { column.Spacing(15); AddItem("Name", Placeholders.Name()); AddItem("Email", Placeholders.Email()); AddItem("Phone", Placeholders.PhoneNumber()); AddItem("Date", Placeholders.ShortDate()); AddItem("Time", Placeholders.Time()); void AddItem(string label, string value) { column.Item().Text(text => { text.Span($"{label}: ").Bold(); text.Span(value); }); } }); ``` ![example](/patterns-and-practices/placeholders-text.webp) ## Colors QuestPDF can produce random colors based on the Material Design palette, returning them as a string in the `#RRGGBB` format. ```csharp // bright color (lighten-2) Placeholders.BackgroundColor(); // medium intensity color Placeholders.Color(); ``` #### BackgroundColor example ```csharp{11} .Grid(grid => { grid.Columns(5); grid.Spacing(5); foreach (var _ in Enumerable.Range(0, 25)) { grid.Item() .Height(50) .Width(50) .Background(Placeholders.BackgroundColor()); } }); ``` ![example](/patterns-and-practices/placeholders-color-background.webp) #### Color example ```csharp{7} .Column(column => { column.Spacing(10); foreach (var i in Enumerable.Range(0, 5)) { column.Item() .Text(Placeholders.Sentence()) .FontColor(Placeholders.Color()); } }); ``` ![example](/patterns-and-practices/placeholders-color.webp) ## Image The image `Placeholders.Image` method generates a soft color gradient. It returns a byte array in JPEG format and can be embedded directly in QuestPDF elements. Use these placeholders to simulate images in your layout, ensuring you can test image placement, sizing, and alignment before real images become available. ```csharp .Width(200) .Column(column => { column.Spacing(10); // provide an exact image resolution column.Item() .Image(Placeholders.Image(100, 50)); // specify physical width and height of the image column.Item() .Width(200) .Height(150) .Image(Placeholders.Image); // specify target physical width and aspect ratio column.Item() .Width(200) .AspectRatio(3 / 2f) .Image(Placeholders.Image); }); ``` ![example](/patterns-and-practices/placeholders-image.webp) --- --- url: /concepts/colors.md --- # Colors QuestPDF supports multiple color formats. ```csharp{6,7,11,14} using QuestPDF.Helpers; container .Padding(20) .Border(1) .BorderColor("#03A9F4") .Background(Colors.LightBlue.Lighten5) .Padding(20) .Text("Blue text") .Bold() .FontColor(Colors.LightBlue.Darken4) .Underline() .DecorationWavy() .DecorationColor(0xFF0000); ``` ![example](/patterns-and-practices/colors.webp) ## Color definitions ### HEX Colors A hexadecimal color is specified with: `#RRGGBB`, where the RR (red), GG (green) and BB (blue) hexadecimal integers specify the components of the color. All values range from 00 to FF, and are case-insensitive. ### Alpha channel To specify an alpha channel, add two more hexadecimal digits in front of the color code: `#AARRGGBB` where AA is the alpha channel. The alpha channel defines the transparency of a color and ranges from 00 (fully transparent) to FF (fully opaque). ### Shorthand HEX You can use shorthand HEX codes with 3 or 4 digits. The library will automatically expand them to the full 6 or 8-digit format. For example, `#123` will be expanded to `#112233` and `#89AB` to `#8899AABB`. You can also omit the hash sign (`#`) at the beginning of the color code. ::: warning Please be aware that in some software the alpha channel is specified at the end of the color code, e.g. `#RRGGBBAA`. ::: ## Examples ## Material Design colors For your convenience, QuestPDF provides a list of colors from the Google Material Design palette. | Variant | Recommended Use | |-------------------------|-------------------------------------------------------------------------------------| | **Medium Shade (Base)** | Base color for the palette | | **Lighter Shades** | Large background areas or surfaces | | **Darker Shades** | Text, headlines, or elements requiring higher contrast against a lighter background | | **Accent Swatches** | Small elements where user attention is needed | --- --- url: /concepts/length-unit-types.md --- # Length unit types Following the PDF specification, QuestPDF uses points as its default measurement unit. Most Fluent API methods accept an optional unit parameter for specifying alternative measurement units. ## Available units | Unit | Size | |---------------------|------------------| | Unit.**Point** | 1/72 inch | | Unit.**Meter** | 100 centimeters | | Unit.**Inch** | 2.54 centimeters | | Unit.**Centimeter** | 10 millimeters | | Unit.**Feet** | 12 inches | | Unit.**Mil** | 1/1000 inch | | Unit.**Inch** | 72 points | ## Example Unit types can be optionally specified in most of length-related API methods. As an example, the following code snippets are equivalent: ```csharp using QuestPDF.Infrastructure; .Padding(72) .Padding(1, Unit.Inch) .Padding(1/12f, Unit.Feet) .Padding(1000, Unit.Mill) ``` --- --- url: /concepts/accessibility.md --- # Accessibility For software developers, PDF accessibility means programmatically creating documents that everyone, including people with disabilities, can use effectively. ## Introduction At its core, it's about ensuring your generated PDFs—like invoices, reports, or statements—work seamlessly with assistive technologies such as screen readers, braille displays, and navigation software. An accessible PDF isn't defined by its visual appearance, but by the hidden logical structure you build into the file. This structure, which you create with your code, dictates the correct reading order, identifies headings, describes tables, and explains images. A screen reader cannot interpret a document based on visual layout alone; it relies entirely on the structural information you provide. ### Tagged PDF This is the foundation of accessibility. A tagged PDF contains hidden structural metadata (tags) that define the document's logical structure. This is very similar to semantic HTML elements (e.g., `

`, `

`, ``). These tags describe the *meaning* of your content, allowing assistive technologies to navigate and read the document correctly. ### PDF/A (Archival) This standard (ISO 19005) is primarily focused on the long-term preservation of electronic documents, ensuring a file can be opened and viewed reliably many years in the future. While its main goal isn't accessibility, several of its levels (like PDF/A-2a and PDF/A-3a) require the document to also be a Tagged PDF, thus incorporating accessibility as part of the archival requirements. ### PDF/UA (Universal Accessibility) This is the gold standard for PDF accessibility. PDF/UA (ISO 14289) is a formal standard that specifies exactly how a PDF must be structured to be considered fully accessible. Achieving PDF/UA-1 compliance ensures your document provides the best possible experience for all users. When you enable accessibility features in QuestPDF, this is the standard you are working to meet. ## Compliance Tools While QuestPDF handles the technical implementation of accessibility tags, it's crucial to validate your output. Generating a compliant document is a critical step, and several excellent tools are available to help you ensure your PDFs meet the necessary standards. ### VeraPDF VeraPDF is an open-source, industry-supported tool designed specifically to validate PDF files against the PDF/A (Archival) and PDF/UA (Universal Accessibility) standards. Developed with support from the PDF Association, VeraPDF performs a deep, technical analysis of a file's structure to confirm it fully conforms to the complex ISO specifications. It's the definitive tool for proving formal compliance, which is often a requirement for legal or archival purposes. You can download VeraPDF from the [official website](https://verapdf.org/software/). Once installed, you can use its command-line interface to check a document: ```bash verapdf document.pdf ``` ### PAC (PDF Accessibility Checker) PAC (PDF Accessibility Checker) is a free Windows tool that focuses on the practical aspects of accessibility. While VeraPDF checks for strict conformance to the standard, PAC checks how usable the document is for people relying on assistive technologies. This tool is invaluable for developers because it simulates how a screen reader will interpret your document. It provides clear, actionable reports that highlight issues like incorrect reading order, missing image descriptions, or improperly tagged tables. Using PAC helps you move beyond simple compliance to ensure you're delivering a genuinely good experience for all users. You can download PAC from the [official website](https://pac.pdf-accessibility.org/en). ## Minimal Example Generating accessible PDF documents with QuestPDF is a straightforward process. Beyond just creating the visual layout, accessibility requires a few key considerations, all demonstrated in the example below. First, you must apply a semantic structure to the content. This tells assistive technologies what is a header, paragraph, or image, creating a logical reading order. Second, it's essential to provide complete document metadata, such as the document's title and language. Finally, you need to enable the correct conformance settings to formally declare the document as PDF/A and PDF/UA compliant. ```csharp{11,23,28,41,46,53,59-69} Document .Create(document => { document.Page(page => { page.Size(PageSizes.A5); page.Margin(30); page.Header() .PaddingBottom(15) .SemanticHeader1() .Text("Accessibility Test Document") .FontColor(Colors.Blue.Darken3) .FontSize(24) .Bold(); page.Content() .Column(column => { column.Spacing(20); column.Item() .SemanticSection() .Column(column => { column.Item() .PaddingBottom(10) .SemanticHeader2() .Text("Section with text content") .FontColor(Colors.Blue.Darken1) .FontSize(16); column.Item() .Text(Placeholders.Paragraphs()) .FontSize(12) .ParagraphSpacing(8); }); column.Item() .PreventPageBreak() .SemanticSection() .Column(column => { column.Item() .PaddingBottom(10) .SemanticHeader2() .Text("Section with image") .FontColor(Colors.Blue.Darken1) .FontSize(16); column.Item() .Width(250) .SemanticImage("Image showing a laptop") .Image("Resources/product.jpg"); }); }); }); }) .WithMetadata(new DocumentMetadata { Language = "en-US", Title = "Accessibility Test", Subject = "This document shows how easy it is to create accessible PDF documents with QuestPDF" }) .WithSettings(new DocumentSettings { PDFA_Conformance = PDFA_Conformance.PDFA_3A, PDFUA_Conformance = PDFUA_Conformance.PDFUA_1 }) .GeneratePdf("accessibility-minimal-example.pdf"); ``` ## Semantic Elements `Semantic` extension methods allow you to add logical structure to your PDF document, which is essential for accessibility and content extraction. By wrapping your layout elements (containers) with these methods, you are tagging the content according to its meaning, such as a heading, paragraph, or figure. This "semantic tree" is used by assistive technologies, like screen readers, to navigate and understand the document's structure, making your content accessible to all users. It also improves content reflow and copy-paste behavior. ### Document Structure These methods define the high-level organization of your document, which is essential for accessibility and creating a logical flow. | Method | Description | |----------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------| | **SemanticSection** | Groups a set of related content. A section typically includes a heading (e.g., `SemanticHeader2`) and its corresponding content. Sections can be nested. | | **SemanticArticle** | Marks a self-contained body of text that forms a single narrative, such as a blog post or news story. | | **SemanticDivision** | Marks a generic block-level container for grouping elements, similar to an HTML `
`. Use this when a more specific tag doesn't apply. | ### Headings Headings are fundamental for document navigation and outlining the content's structure. Using them correctly is crucial for accessibility and allowing a PDF reader to generate a navigable Table of Contents. | Method | Description | |---------------------|-------------------------------------------------------------------------------------------| | **SemanticHeader1** | Marks the content as a level 1 heading (H1), the highest level in the document hierarchy. | | **SemanticHeader2** | Marks the content as a level 2 heading (H2). | | **SemanticHeader3** | Marks the content as a level 3 heading (H3). | | **SemanticHeader4** | Marks the content as a level 4 heading (H4). | | **SemanticHeader5** | Marks the content as a level 5 heading (H5). | | **SemanticHeader6** | Marks the content as a level 6 heading (H6), the lowest level. | ::: tip Document Outline QuestPDF uses these semantic headers to automatically generate the document outline (often called a Table of Contents) in PDF readers. This allows end-users to quickly browse and jump to different parts of your document, significantly improving usability. ::: ### Text Content These methods are used to tag common block-level text elements, distinguishing them from other structural elements like headings or lists. | Method | Description | |----------------------------|-----------------------------------------------------------------------------------------------------------------------------------------| | **SemanticParagraph** | Marks a container as a paragraph. This is one of the most common tags for organizing text. | | **SemanticBlockQuotation** | Designates a block of text that is a quotation, typically consisting of one or more paragraphs. For inline quotes, use `SemanticQuote`. | ### Lists Use this set of methods to properly structure ordered or unordered lists. Correctly tagging list components is vital for screen readers to announce the list structure correctly. | Method | Description | |--------------------------|-----------------------------------------------------------------------------------------| | **SemanticList** | Marks a container as a list. Its direct children should be `SemanticListItem` elements. | | **SemanticListItem** | Marks an individual item within a `SemanticList`. | | **SemanticListLabel** | Marks the label of a list item. This holds the bullet, number (e.g., '1.'), or term. | | **SemanticListItemBody** | Marks the body or descriptive content of a `SemanticListItem`. | Here is a practical example of how to build a semantically correct list: ```csharp{1,9,13,17} .SemanticList() .Column(listColumn => { listColumn.Spacing(10); foreach (var i in Enumerable.Range(2, 5)) { listColumn.Item() .SemanticListItem() .Row(row => { row.ConstantItem(20) .SemanticListLabel() .Text($"{i}."); row.RelativeItem() .SemanticListItemBody() .Text(Placeholders.Sentence()); }); } }); ``` ### Tables Tables are complex structural elements, and conformance standards require detailed tagging. This process is essential to ensure the document is accessible, especially for users who rely on screen readers to navigate and understand the data relationships. QuestPDF simplifies this by automatically tagging tables for accessibility when you use the SemanticTable helper method. ```csharp{1} .SemanticTable() .Table(table => { // table content }); ``` It is also possible to mark specific cells as headers. Horizontal headers (often called Row Headers) provide a title or description for the data presented in their respective rows. Tagging them is crucial for accessibility, as it allows screen readers to correctly associate data cells with their corresponding row titles. For example, a screen reader can announce "Position: Senior Developer" rather than just "Senior Developer." You can apply this tag using the AsSemanticHorizontalHeader method: ```csharp{23,28,33,38} Document .Create(document => { document.Page(page => { page.Margin(60); page.Content() .Shrink() .Border(1) .BorderColor(Colors.Grey.Darken1) .SemanticTable() .Table(table => { table.ColumnsDefinition(columns => { columns.RelativeColumn(); columns.RelativeColumn(); columns.RelativeColumn(); }); // Row 1: Name table.Cell().AsSemanticHorizontalHeader().Element(HeaderCellStyle).Text("Name"); table.Cell().Element(CellStyle).Text("John Smith"); table.Cell().Element(CellStyle).Text("Jane Doe"); // Row 2: Position table.Cell().AsSemanticHorizontalHeader().Element(HeaderCellStyle).Text("Position"); table.Cell().Element(CellStyle).Text("Senior Developer"); table.Cell().Element(CellStyle).Text("UX Designer"); // Row 3: Department table.Cell().AsSemanticHorizontalHeader().Element(HeaderCellStyle).Text("Department"); table.Cell().Element(CellStyle).Text("Engineering"); table.Cell().Element(CellStyle).Text("Design"); // Row 4: Experience table.Cell().AsSemanticHorizontalHeader().Element(HeaderCellStyle).Text("Experience"); table.Cell().Element(CellStyle).Text("5 years"); table.Cell().Element(CellStyle).Text("3 years"); IContainer HeaderCellStyle(IContainer container) => container .Border(1) .BorderColor(Colors.Grey.Lighten2) .Background(Colors.Grey.Lighten3) .Padding(8) .AlignMiddle() .DefaultTextStyle(x => x.Bold()); IContainer CellStyle(IContainer container) => container .Border(1) .BorderColor(Colors.Grey.Lighten2) .Padding(8); }); });![img.png](img.png) }); ``` ### Table of Contents Use accessibility tags to identify navigational aids within your document, such as a Table of Contents (TOC) or an Index. Properly tagging these sections is crucial for assistive technologies, allowing them to understand the document's structure and provide effective navigation for users. QuestPDF provides the following methods for tagging these specific elements: | Method | Description | |---------------------------------|------------------------------------------------------------------------------------------------------------------| | **SemanticTableOfContents** | Marks a container as a Table of Contents (TOC). It should be composed of `SemanticTableOfContentsItem` elements. | | **SemanticTableOfContentsItem** | Marks an individual item within a `SemanticTableOfContents`. | | **SemanticIndex** | Marks a section of the document as an index, which typically holds a sequence of entries and references. | The example below demonstrates how to build a fully functional and accessible Table of Contents. It generates a list of entries, and each entry links to a corresponding section later in the document. The page numbers for each entry are automatically resolved by referencing the target section. ```csharp{47-67} Document .Create(document => { document.Page(page => { page.Margin(60); page.Content() .PaddingVertical(30) .Column(column => { column.Item() .ExtendVertical() .AlignMiddle() .SemanticHeader1() .Text("Conformance Test:\nTable of Contents") .FontSize(36) .Bold() .FontColor(Colors.Blue.Darken2); column.Item().PageBreak(); column.Item().Element(GenerateTableOfContentsSection); column.Item().PageBreak(); column.Item().Element(GeneratePlaceholderContentSection); }); }); }); static void GenerateTableOfContentsSection(IContainer container) { container .SemanticSection() .Column(column => { column.Spacing(15); column .Item() .Text("Table of Contents") .Bold() .FontSize(20) .FontColor(Colors.Blue.Medium); column.Item() .SemanticTableOfContents() .Column(column => { column.Spacing(5); foreach (var i in Enumerable.Range(1, 10)) { column.Item() .SemanticTableOfContentsItem() .SemanticLink($"Link to section {i}") .SectionLink($"section-{i}") .Row(row => { row.ConstantItem(25).Text($"{i}."); row.AutoItem().Text(Placeholders.Label()); row.RelativeItem().PaddingHorizontal(2).OffsetY(11).LineHorizontal(1).LineDashPattern([1, 3]); row.AutoItem().Text(text => text.BeginPageNumberOfSection($"section-{i}")); }); } }); }); } static void GeneratePlaceholderContentSection(IContainer container) { container .Column(column => { foreach (var i in Enumerable.Range(1, 10)) { column.Item() .SemanticSection() .Section($"section-{i}") .Column(column => { column.Spacing(15); column.Item() .SemanticHeader2() .Text($"Section {i}") .Bold() .FontSize(20) .FontColor(Colors.Blue.Medium); column.Item().Text(Placeholders.Paragraph()); foreach (var j in Enumerable.Range(1, i)) { column.Item() .SemanticIgnore() .Width(200) .Height(150) .CornerRadius(10) .Background(Placeholders.BackgroundColor()); } }); if (i < 10) column.Item().PageBreak(); } }); } ``` ### Inline Elements These methods apply semantic meaning to a portion of text, often within a Text element's span. This helps assistive technologies, like screen readers, understand the structure and type of content, even when it's mixed with other text. | Method | Description | |-------------------|---------------------------------------------------------------------------------------------------------------------------------------| | **SemanticSpan** | Marks a generic inline portion of text, similar to an HTML ``. `alternativeText` can provide an expansion for an abbreviation. | | **SemanticQuote** | Marks an inline portion of text as a quote. This differs from `SemanticBlockQuotation`, which is for block-level content. | | **SemanticCode** | Marks a fragment of text as computer code. | | **SemanticLink** | Marks the content as a hyperlink. **Alternative text is essential** to describe the link's purpose or destination for screen readers. | ### Illustrations and Media Use these methods to identify non-text content. Providing clear and descriptive alternative text for these elements is one of the most important aspects of creating an accessible document. | Method | Description | |---------------------|--------------------------------------------------------------------------------------------------------------------------------| | **SemanticFigure** | Marks a container as a figure (e.g., a chart, diagram, or photograph). **Alternative text is essential** for accessibility. | | **SemanticImage** | An alias for `SemanticFigure`. Marks the content as an image. **Alternative text is essential**. | | **SemanticFormula** | Marks the content as a mathematical formula. It is treated like a figure and **requires alternative text** (e.g., "E = mc^2"). | | **SemanticCaption** | Identifies a brief caption or description for a table, figure, or image. It should be placed near the element it describes. | ```csharp{1,9} .SemanticImage("Sample image description") .Column(column => { column.Item().Image(imageData); column.Item() .PaddingTop(5) .AlignCenter() .SemanticCaption() .Text("Sample image caption"); }); ``` ### Language This method applies a language attribute to a container, specifying the natural language of its content (e.g., "en-US", "fr-FR", "es-ES"). This is crucial for accessibility, as it allows screen readers to switch to the correct pronunciation rules, ensuring the text is read clearly and accurately. > **Example:** > > ```csharp > .SemanticLanguage("es-ES") > .Text("Hola, Mundo!"); > ``` ### Ignoring Content This method excludes a container and all its children from the PDF's semantic (accessibility) tree. This is essential for decorative elements—such as background shapes, ornamental borders, or layout helper lines—that add visual flair but provide no structural or informational value. Ignoring these elements prevents screen readers from announcing confusing, non-essential content, leading to a much cleaner and more understandable experience for the user. ```csharp{17,24} Document .Create(document => { document.Page(page => { page.Margin(60); page.Content() .PaddingVertical(30) .Column(column => { column.Spacing(25); column.Item().Text("This photo has semantic meaning:"); column.Item() .SemanticImage("A beautiful landscape") .Image("Resources/photo.jpeg"); column.Item().Text("While this one doesn't:"); column.Item() .SemanticIgnore() .Image("Resources/decoration-image.jpeg"); }); }); }); ``` --- --- url: /concepts/code-patterns/execution-order.md --- # Execution order QuestPDF uses a fluent API with method chaining to define your document's structure and appearance. The execution order of these chained methods is strict, meaning that rearranging them may lead to different visual outcomes. ```csharp container.Column(column => { column.Spacing(25); column.Item() .Border(1) .Background(Colors.Blue.Lighten4) .Padding(15) .Text("border → background → padding"); column.Item() .Border(1) .Padding(15) .Background(Colors.Blue.Lighten4) .Text("border → padding → background"); column.Item() .Background(Colors.Blue.Lighten4) .Padding(15) .Border(1) .Text("background → padding → border"); column.Item() .Padding(15) .Border(1) .Background(Colors.Blue.Lighten4) .Text("padding → border → background"); }); ``` ![example](/patterns-and-practices/code-pattern-execution-order.webp) --- --- url: /concepts/code-patterns/document-structure.md --- # Code pattern: document code structure Organizing your code effectively is crucial for maintainability and readability. A recommended approach is to encapsulate your document generation within a single class, while breaking down the document structure into well-named private methods. This pattern allows you to separate the overall document structure from the implementation details of individual sections, making your code more modular and easier to maintain as your documents grow in complexity. ```csharp{16-25} public class MyReport { public byte[] GenerateReport() { return Document .Create(document => { document.Page(page => { page.Size(PageSizes.A5); page.DefaultTextStyle(x => x.FontSize(20)); page.Margin(25); page.Content() .PaddingBottom(15) .Column(column => { column.Item().Element(ReportTitle); column.Item().PageBreak(); column.Item().Element(RedSection); column.Item().PageBreak(); column.Item().Element(GreenSection); column.Item().PageBreak(); column.Item().Element(BlueSection); }); page.Footer().AlignCenter().Text(text => text.CurrentPageNumber()); }); }) .GeneratePdf(); } private void ReportTitle(IContainer container) { container.Extend() .AlignCenter() .AlignMiddle() .Text("Multi-section report") .FontSize(48) .Bold(); } // dumb implementation of different document sections private void RedSection(IContainer container) { container.Grid(grid => { grid.Columns(3); grid.Spacing(15); grid.Item(3 ).Text("Red section") .FontColor(Colors.Red.Darken2).FontSize(32).Bold(); grid.Item(3).Text(Placeholders.Paragraph()).Light(); foreach (var i in Enumerable.Range(0, 6)) grid.Item().AspectRatio(4 / 3f).Background(Colors.Red.Lighten4); }); } private void GreenSection(IContainer container) { container.Grid(grid => { grid.Columns(3); grid.Spacing(15); grid.Item(3).Text("Green section") .FontColor(Colors.Green.Darken2).FontSize(32).Bold(); grid.Item(3).Text(Placeholders.Paragraph()).Light(); foreach (var i in Enumerable.Range(0, 12)) grid.Item().AspectRatio(4 / 3f).Background(Colors.Green.Lighten4); }); } private void BlueSection(IContainer container) { container.Grid(grid => { grid.Columns(3); grid.Spacing(15); grid.Item(3).Text("Blue section") .FontColor(Colors.Blue.Darken2).FontSize(32).Bold(); grid.Item(3).Text(Placeholders.Paragraph()).Light(); foreach (var i in Enumerable.Range(0, 18)) grid.Item().AspectRatio(4 / 3f).Background(Colors.Blue.Lighten4); }); } } ``` --- --- url: /concepts/code-patterns/local-helpers.md --- # Code pattern: local helpers When building complex document layouts, you might find yourself repeating similar code structures. C# local functions offer an elegant solution to this challenge, allowing you to encapsulate reusable layout logic directly within your document generation method. Local functions help maintain clean, readable code by defining specialized helper methods exactly where they're needed. This approach keeps related code together, improving both readability and maintainability without polluting your class with single-use methods. ```csharp container.Column(column => { column.Spacing(15); column.Item().Text("Business details:").FontSize(24).Bold().FontColor(Colors.Blue.Darken2); AddContactItem("Resources/Icons/phone.svg", Placeholders.PhoneNumber()); AddContactItem("Resources/Icons/email.svg", Placeholders.Email()); AddContactItem("Resources/Icons/web.svg", Placeholders.WebpageUrl()); void AddContactItem(string iconPath, string label) { column.Item().Row(row => { row.ConstantItem(32).AspectRatio(1).Svg(iconPath); row.ConstantItem(15); row.AutoItem().AlignMiddle().Text(label); }); } }); ``` ![example](/patterns-and-practices/code-pattern-local-helpers.webp) --- --- url: /concepts/code-patterns/content-styling.md --- # Code pattern: content styling When designing PDF documents, many elements often share similar styles. Instead of repeating styling code for each element, encapsulating the styling logic into reusable functions can improve maintainability and readability. By defining local functions, you can ensure consistency across multiple elements while reducing redundancy. Below is an example demonstrating this approach: ```csharp container.Table(table => { table.ColumnsDefinition(columns => { columns.ConstantColumn(50); columns.RelativeColumn(1); columns.RelativeColumn(2); }); table.Header(header => { header.Cell().Element(Style).Text("#"); header.Cell().Element(Style).Text("Product Name"); header.Cell().Element(Style).Text("Description"); IContainer Style(IContainer container) { return container .Background(Colors.Blue.Lighten5) .Padding(10) .DefaultTextStyle(TextStyle.Default.FontColor(Colors.Blue.Darken4).Bold()); } }); foreach (var i in Enumerable.Range(1, 5)) { table.Cell().Element(Style).Text(i.ToString()); table.Cell().Element(Style).Text(Placeholders.Label()); table.Cell().Element(Style).Text(Placeholders.Sentence()); } IContainer Style(IContainer container) { return container .BorderTop(2) .BorderColor(Colors.Blue.Lighten3) .Padding(10); } }); ``` ![example](/patterns-and-practices/code-pattern-content-styling.webp) --- --- url: /concepts/code-patterns/extension-methods.md --- # Code pattern: extension methods When you find yourself implementing the same styling or content patterns repeatedly across different documents, extension methods offer the perfect solution. They allow you to define your styling once and reuse it throughout your codebase, ensuring visual consistency while reducing duplication. #### Defining extension methods ```csharp public static class TableExtensions { private static IContainer TableCellStyle(this IContainer container, string backgroundColor) { return container .Border(1) .BorderColor(Colors.Black) .Background(backgroundColor) .Padding(10); } public static void TableLabelCell(this IContainer container, string text) { container .TableCellStyle(Colors.Grey.Lighten3) .Text(text) .Bold(); } public static IContainer TableValueCell(this IContainer container) { return container.TableCellStyle(Colors.Transparent); } } ``` #### Using extension methods ```csharp container .Border(1) .Table(table => { table.ColumnsDefinition(columns => { columns.RelativeColumn(2); columns.RelativeColumn(3); columns.RelativeColumn(2); columns.RelativeColumn(3); }); table.Cell().TableLabelCell("Product name"); table.Cell().TableValueCell().Text(Placeholders.Label()); table.Cell().TableLabelCell("Description"); table.Cell().TableValueCell().Text(Placeholders.Sentence()); table.Cell().TableLabelCell("Price"); table.Cell().TableValueCell().Text(Placeholders.Price()); table.Cell().TableLabelCell("Date of production"); table.Cell().TableValueCell().Text(Placeholders.ShortDate()); table.Cell().ColumnSpan(2).TableLabelCell("Photo of the product"); table.Cell().ColumnSpan(2).TableValueCell().AspectRatio(16 / 9f).Image(Placeholders.Image); }); ``` ![example](/patterns-and-practices/code-pattern-extension-methods.webp) --- --- url: /concepts/code-patterns/components.md --- # Code pattern: components Components in QuestPDF provide a powerful abstraction mechanism for creating reusable content across multiple document types. By encapsulating specific content generation logic in standalone classes, you can significantly improve the modularity and maintainability of your PDF generation codebase. Components follow a clean separation of concerns principle, ensuring that your document structure remains organized and consistent across various implementations. ## Example: address component This example demonstrates how to create a reusable address component and integrate it into document structure. #### Component definition ```csharp public class Address { public string CompanyName { get; set; } public string PostalCode { get; set; } public string Country { get; set; } public string City { get; set; } public string Street { get; set; } } public class AddressComponent : IComponent { private Address Address { get; } public AddressComponent(Address address) { Address = address; } public void Compose(IContainer container) { container.Column(column => { column.Spacing(10); AddItem("Company name", Address.CompanyName); AddItem("Postal code", Address.PostalCode); AddItem("Country", Address.Country); AddItem("City", Address.City); AddItem("Street", Address.Street); void AddItem(string label, string value) { column.Item().Text(text => { text.Span($"{label}: ").Bold(); text.Span(value); }); } }); } } ``` #### Component usage By simply passing an Address object to the component, all formatting and layout concerns are delegated to the component itself. ```csharp var address = new Address { CompanyName = "Apple", PostalCode = "95014", Country = "United States", City = "Cupertino", Street = "One Apple Park Way" }; Document .Create(document => { document.Page(page => { page.MinSize(new PageSize(0, 0)); page.MaxSize(new PageSize(600, 1200)); page.DefaultTextStyle(x => x.FontSize(20)); page.Margin(25); page.Content() .Component(new AddressComponent(address)); }); }) .GeneratePdf("report.pdf"); ``` ![example](/patterns-and-practices/code-pattern-address-components.webp) ## Complex example A more advanced example involves a configurable section component that can hold multiple fields, each defined by a label and its own content. This approach provides flexibility for various data types and layout requirements while retaining an organized, maintainable structure. #### Component definition The component exposes methods for adding text, images, and custom content fields. ```csharp using QuestPDF.Infrastructure; public class SectionComponent : IComponent { private List<(string Label, IContainer Content)> Fields { get; set; } = []; public SectionComponent() { } public void Compose(IContainer container) { container .Border(1) .Column(column => { foreach (var field in Fields) { column.Item().Row(row => { row.RelativeItem() .Border(1) .BorderColor(Colors.Grey.Medium) .Background(Colors.Grey.Lighten3) .Padding(10) .Text(field.Label); row.RelativeItem(2) .Border(1) .BorderColor(Colors.Grey.Medium) .Padding(10) .Element(field.Content); }); } }); } public void Text(string label, string text) { Custom(label).Text(text); } public void Image(string label, string imagePath) { Custom(label).Image(imagePath); } public IContainer Custom(string label) { var content = IContainer.Empty; Fields.Add((label, content)); return content; } } ``` #### Component usage Please note how easy it is to create a new section with multiple fields. The layout and styling are encapsulated within the component, ensuring consistency across different sections. ```csharp Document .Create(document => { document.Page(page => { page.MinSize(new PageSize(0, 0)); page.MaxSize(new PageSize(600, 1200)); page.DefaultTextStyle(x => x.FontSize(20)); page.Margin(25); page.Content() .Column(column => { column.Item().Component(BuildSampleSection()); // more usages of the section component }); }); } .GeneratePdf("report.pdf"); IComponent BuildSampleSection() { var section = new SectionComponent(); section.Text("Product name", Placeholders.Label()); section.Text("Description", Placeholders.Sentence()); section.Text("Price", Placeholders.Price()); section.Text("Date of production", Placeholders.ShortDate()); section.Image("Photo of the product", "Resources/product.jpg"); section.Custom("Status").Text("Accepted").FontColor(Colors.Green.Darken2).Bold(); return section; } ``` ![example](/patterns-and-practices/code-pattern-configurable-component.webp) --- --- url: /concepts/code-patterns/dynamic-components.md --- # Code pattern: dynamic components Dynamic components provide a powerful way to generate conditional or varying content on each page of your PDF document. Unlike standard components that render once for the entire document, dynamic components' Compose method is invoked separately for each page where the component appears. This page-specific rendering gives you access to crucial context information like the current page number, total page count, and available space. With this information, you can create sophisticated layouts that adapt to their position within the document. ## Simple examples #### Alternating side of page numbers This component places page numbers on alternating sides of the page - left for odd pages and right for even pages. It demonstrates how to use the page number information to conditionally format content. ```csharp public class PageNumberSideComponent : IDynamicComponent { public DynamicComponentComposeResult Compose(DynamicContext context) { var content = context.CreateElement(element => { element .Element(x => context.PageNumber % 2 == 0 ? x.AlignRight() : x.AlignLeft()) .Text(text => { text.Span("Page "); text.CurrentPageNumber(); }); }); return new DynamicComponentComposeResult { Content = content, HasMoreContent = false }; } } ``` #### Progressbar This component creates a visual progress bar indicating how far the reader has advanced through the document. ```csharp public class PageProgressbarComponent : IDynamicComponent { public DynamicComponentComposeResult Compose(DynamicContext context) { var content = context.CreateElement(element => { var width = context.AvailableSize.Width * context.PageNumber / context.TotalPages; element .Background(Colors.Blue.Lighten3) .Height(5) .Width(width) .Background(Colors.Blue.Darken2); }); return new DynamicComponentComposeResult { Content = content, HasMoreContent = false }; } } ``` #### Usage The following example demonstrates how to incorporate both dynamic components into a document structure. The progress bar appears in the header, while the alternating page numbers display in the footer. ```csharp{22,40} Document .Create(document => { document.Page(page => { page.Size(PageSizes.A4); page.Margin(50); page.DefaultTextStyle(x => x.FontSize(20)); page.Header().Column(column => { column.Item() .Text("MyBrick Set") .FontSize(48).FontColor(Colors.Blue.Darken2).Bold(); column.Item() .Text("Building Instruction") .FontSize(24); column.Item().Height(15); column.Item().Dynamic(new PageProgressbarComponent()); }); page.Content().PaddingVertical(25).Column(column => { column.Spacing(25); foreach (var i in Enumerable.Range(1, 30)) { column.Item() .Background(Colors.Grey.Lighten3) .Height(Random.Shared.Next(4, 8) * 25) .AlignCenter() .AlignMiddle() .Text($"Step {i}"); } }); page.Footer().Dynamic(new PageNumberSideComponent()); }); }) .GeneratePdf(); ``` ## Table with per-page subtotals This more complex example demonstrates how to create a table that spans multiple pages and displays subtotals for each page. It uses component state to track which items have been shown across pages. ::: warning Important: Always treat state as read-only. Never modify existing state directly. Instead, create a new instance of your state struct with the updated values and assign it to the State property. QuestPDF may call the Compose method multiple times per page and may internally change the state. ::: #### Model and state First, let's define our data model and the state structure. ```csharp public class OrderItem { public string ItemName { get; set; } = Placeholders.Label(); public int Price { get; set; } = Placeholders.Random.Next(1, 11) * 10; public int Count { get; set; } = Placeholders.Random.Next(1, 11); } public struct OrdersTableWithPageSubtotalsComponentState { public int ShownItemsCount { get; set; } } ``` #### Paging algorithm The following component implements a paging algorithm that: 1. Generates and measures the header to determine its height 2. Generates and measures a sample footer to determine its height 3. Calculates remaining space for table rows 4. Adds rows incrementally until available space is filled 5. Generates the actual footer with subtotals for the visible rows 6. Updates state to track progress ```csharp public class OrdersTableWithPageSubtotalsComponent : IDynamicComponent { private ICollection Items { get; } public OrdersTableWithPageSubtotalsComponentState State { get; set; } public OrdersTableWithPageSubtotalsComponent(ICollection items) { Items = items; State = new OrdersTableWithPageSubtotalsComponentState { ShownItemsCount = 0 }; } public DynamicComponentComposeResult Compose(DynamicContext context) { var header = ComposeHeader(context); var sampleFooter = ComposeFooter(context, []); var decorationHeight = header.Size.Height + sampleFooter.Size.Height; var rows = GetItemsForPage(context, decorationHeight).ToList(); var footer = ComposeFooter(context, rows.Select(x => x.Item)); var content = context.CreateElement(container => { container.Shrink().Decoration(decoration => { decoration.Before().Element(header); decoration.Content().Column(column => { foreach (var row in rows) column.Item().Element(row.Element); }); decoration.After().Element(footer); }); }); State = new OrdersTableWithPageSubtotalsComponentState { ShownItemsCount = State.ShownItemsCount + rows.Count }; return new DynamicComponentComposeResult { Content = content, HasMoreContent = State.ShownItemsCount < Items.Count }; } private static IDynamicElement ComposeHeader(DynamicContext context) { return context.CreateElement(element => { element .Width(context.AvailableSize.Width) .BorderBottom(1) .BorderColor(Colors.Grey.Darken2) .Padding(10) .DefaultTextStyle(TextStyle.Default.SemiBold()) .Row(row => { row.ConstantItem(50).Text("#").AlignCenter(); row.RelativeItem().Text("Item name"); row.ConstantItem(75).AlignRight().Text("Count"); row.ConstantItem(75).AlignRight().Text("Price"); row.ConstantItem(75).AlignRight().Text("Total"); }); }); } private static IDynamicElement ComposeFooter(DynamicContext context, IEnumerable items) { var total = items.Sum(x => x.Count * x.Price); return context.CreateElement(element => { element .Width(context.AvailableSize.Width) .Padding(10) .AlignRight() .Text($"Subtotal: {total}$") .Bold(); }); } private IEnumerable<(OrderItem Item, IDynamicElement Element)> GetItemsForPage(DynamicContext context, float decorationHeight) { var totalHeight = decorationHeight; foreach (var index in Enumerable.Range(State.ShownItemsCount, Items.Count - State.ShownItemsCount)) { var item = Items.ElementAt(index); var element = context.CreateElement(content => { content .Width(context.AvailableSize.Width) .BorderBottom(1) .BorderColor(Colors.Grey.Lighten2) .Padding(10 ) .Row(row => { row.ConstantItem(50).Text((index + 1).ToString(CultureInfo.InvariantCulture)); row.RelativeItem().Text(item.ItemName); row.ConstantItem(75).AlignRight().Text(item.Count.ToString(CultureInfo.InvariantCulture)); row.ConstantItem(75).AlignRight().Text($"{item.Price}$"); row.ConstantItem(75).AlignRight().Text($"{item.Count*item.Price}$"); }); }); var elementHeight = element.Size.Height; // it is important to use the Size.Epsilon constant to avoid floating point comparison issues if (totalHeight + elementHeight > context.AvailableSize.Height + Size.Epsilon) break; totalHeight += elementHeight; yield return (item, element); } } } ``` #### Usage Here is how you can integrate this component into a document that displays per-page subtotals. ```csharp var items = Enumerable.Range(0, 25).Select(x => new OrderItem()).ToList(); Document .Create(document => { document.Page(page => { page.Size(PageSizes.A4); page.DefaultTextStyle(x => x.FontSize(20)); page.Margin(50); page.Content() .Decoration(decoration => { decoration .Before() .PaddingBottom(10) .Text(text => { text.DefaultTextStyle(TextStyle.Default.Bold().FontColor(Colors.Blue.Darken2)); text.Span("Page "); text.CurrentPageNumber(); text.Span(" of "); text.TotalPages(); }); decoration .Content() .Dynamic(new OrdersTableWithPageSubtotalsComponent(items)); }); }); }) .GeneratePdf("orders.pdf"); ``` --- --- url: /concepts/code-patterns/capture-content-position.md --- # Code pattern: capture content position When generating PDF documents, you sometimes need to create elements that depend on the position of other content already placed in the document. QuestPDF provides the CaptureContentPosition API to address these scenarios elegantly. This feature observes the rendering process of your content and captures its precise position and size on each page. You can then use this captured positional data in a Dynamic component to build and position other elements exactly where you need them, creating sophisticated layout relationships between different parts of your document. ::: danger When using the `GetContentCapturedPositions` method, keep in mind that it may return an empty or incomplete array depending on the current document rendering phase. It is expected behavior, as the document generation process requires two rendering passes. Your implementation should handle these cases gracefully, as shown in the example above. ::: ## Example The following example demonstrates how to implement a demo of proofreading functionality. It highlights incorrect words in red with strikethrough formatting and adds corrected versions in green. Finally, it places an icon beside each correction for easy identification. ![example](/patterns-and-practices/code-pattern-element-position-locator.webp) ### Capturing position To implement this feature, we need to capture two types of positions: the position of the entire text container as a reference point, and the specific positions of each mistake that needs an icon. ```csharp{16,18,36,40} Document .Create(document => { document.Page(page => { page.ContinuousSize(575); page.DefaultTextStyle(x => x.FontSize(20)); page.Margin(25); page.Content() .Background(Colors.White) .Row(row => { row.Spacing(25); row.ConstantItem(0).Dynamic(new DynamicTextSpanPositionCapture()); row.RelativeItem().CaptureContentPosition("container").Text(text => { text.Justify(); var mistakeTextStyle = TextStyle.Default .FontColor(Colors.Red.Darken3) .BackgroundColor(Colors.Red.Lighten4) .Strikethrough() .DecorationThickness(2); var correctionTextStyle = TextStyle.Default .FontColor(Colors.Green.Darken3) .BackgroundColor(Colors.Green.Lighten4); text.Span("Proofreading").Bold().Underline().DecorationThickness(2); text.Span(" technical documentation is a critical quality assurance step that ensures clarity, accuracy, and consistency across all written content. It involves more than just checking for grammar and "); text.Span("spilling").Style(mistakeTextStyle); text.Span("spelling").Style(correctionTextStyle); text.Element(TextInjectedElementAlignment.Middle).CaptureContentPosition("mistake"); text.Span(" errors—it also includes verifying terminology, code syntax, formatting standards, and logical flow. A common best practice is to have the content reviewed by both a subject matter "); text.Span("export").Style(mistakeTextStyle); text.Span("expert").Style(correctionTextStyle); text.Element(TextInjectedElementAlignment.Middle).CaptureContentPosition("mistake"); text.Span(" and a language specialist, ensuring that the material is technically sound while also being accessible to the intended audience."); }); }); }); }) .GeneratePdf("file.pdf"); ``` ### Generating dependent content The dynamic component below uses the captured positions to generate and place correction icons. Notice how we retrieve both the container position and the positions of each mistake marker to calculate the proper placement of each icon. ```csharp{5,6} public class DynamicTextSpanPositionCapture : IDynamicComponent { public DynamicComponentComposeResult Compose(DynamicContext context) { var containerLocation = context.GetContentCapturedPositions("container").FirstOrDefault(x => x.PageNumber == context.PageNumber); var mistakeLocations = context.GetContentCapturedPositions("mistake").Where(x => x.PageNumber == context.PageNumber).ToList(); if (containerLocation == null || mistakeLocations.Count == 0) { return new DynamicComponentComposeResult { Content = context.CreateElement(_ => { }), HasMoreContent = false }; } var content = context.CreateElement(container => { container.Layers(layers => { layers.PrimaryLayer(); foreach (var mistakeLocation in mistakeLocations) { layers .Layer() .Unconstrained() .OffsetY(mistakeLocation.Y - containerLocation.Y) .OffsetX(-12) .OffsetY(-12) .Width(24) .Svg("Resources/proofreading.svg"); } }); }); return new DynamicComponentComposeResult { Content = content, HasMoreContent = false }; } } ``` --- --- url: /concepts/dynamic-composition/mixed-content-blocks.md --- # Dynamic composition: mixed content blocks Many documents are not designed as fixed layouts but rather as sequences of content blocks: headings, paragraphs, images, quotes, and so on. The exact order and number of blocks is usually known only at runtime, for example when the content is loaded from a database, a CMS, or provided by the end-user. Because QuestPDF describes documents with plain C# code, this scenario does not require any special API. You can model the content as a simple class hierarchy and use pattern matching to translate each block into its visual representation. #### Data model Each supported block type is modeled as a record deriving from a common base type: ```csharp public abstract record ContentBlock; public sealed record HeadingBlock(string Text) : ContentBlock; public sealed record ParagraphBlock(string Text) : ContentBlock; public sealed record ImageBlock(byte[] Data) : ContentBlock; public sealed record QuoteBlock(string Text, string Author) : ContentBlock; ``` In a real application, this collection would likely be built from external data. For this example, it is created in code with help of the `Placeholders` class: ```csharp var blocks = new List { new HeadingBlock("Quarterly Product Update"), new ParagraphBlock(Placeholders.Paragraph()), new ImageBlock(Placeholders.Image(600, 200)), new QuoteBlock("This release cut our document generation time in half.", "Anna Kowalska, Operations Lead"), new ParagraphBlock(Placeholders.Paragraph()) }; ``` #### Block composition A dedicated method uses pattern matching to decide how each block type is rendered. Simple blocks map to a single element, while more complex ones (such as the quote) can use any layout structure. ```csharp private void ComposeBlock(IContainer container, ContentBlock block) { if (block is HeadingBlock heading) { container.Text(heading.Text).FontSize(24).SemiBold().FontColor(Colors.Blue.Darken2); return; } if (block is ParagraphBlock paragraph) { container.Text(paragraph.Text); return; } if (block is ImageBlock image) { container.Image(image.Data); return; } if (block is QuoteBlock quote) { container .BorderLeft(3) .BorderColor(Colors.Blue.Medium) .PaddingLeft(15) .Column(column => { column.Item().Text(quote.Text).Italic(); column.Item().PaddingTop(5).Text($"— {quote.Author}").FontColor(Colors.Grey.Darken1); }); return; } throw new NotSupportedException($"Unsupported content block: {block.GetType().Name}"); } ``` ::: tip The final `throw` statement is intentional. When a new block type is added to the model but not yet supported by the composition logic, the document generation fails immediately instead of silently skipping content. ::: #### Usage The final document is a simple column that iterates over the collection and delegates each block to the composition method: ```csharp container.Column(column => { column.Spacing(15); foreach (var block in blocks) column.Item().Element(blockContainer => ComposeBlock(blockContainer, block)); }); ``` ![example](/patterns-and-practices/dynamic-composition-content-blocks.webp) --- --- url: /concepts/dynamic-composition/conditional-formatting.md --- # Dynamic composition: conditional formatting In many reports, the appearance of content depends on the data itself. A financial summary may highlight losses in red, a monitoring report may flag measurements that exceed a threshold, and a scorecard may color-code performance levels. Because QuestPDF describes documents with plain C# code, such formatting rules are just ordinary methods that inspect a value and apply the appropriate styles. The following example implements a stock listing where significant price changes are highlighted with color. #### Data model The table displays a collection of stock quotes: ```csharp public sealed record StockQuote( string Company, string Ticker, decimal Price, decimal DailyChange, decimal YearToDateChange); ``` ```csharp var quotes = new List { new("Kelbrick Robotics", "KLBR", 184.20m, 3.85m, 42.10m), new("Solmara Energy", "SLMR", 76.45m, 0.42m, 5.60m), new("Drennick Logistics", "DRNC", 51.08m, -0.35m, -2.15m), new("Marbrenna Biolabs", "MRBN", 229.90m, 0.68m, 18.75m), new("Corvidex Semiconductor", "CVDX", 33.67m, -4.20m, -27.30m), new("Halvern Bank", "HLVN", 118.55m, -0.15m, 0.75m) }; ``` #### Styling rules The following helper method receives a container along with the percentage change, and applies a background and text style only when the change is significant. For values between -1% and +1%, the container is returned unchanged. Please note that the text style is applied with the `DefaultTextStyle` method on the container level, so it automatically propagates to all text inside the cell. ```csharp private static IContainer PriceChangeHighlightStyle(IContainer container, decimal changeInPercent) { if (changeInPercent > 1m) { return container .Background(Colors.Green.Lighten5) .DefaultTextStyle(x => x.FontColor(Colors.Green.Darken2).Bold()); } if (changeInPercent < -1m) { return container .Background(Colors.Red.Lighten5) .DefaultTextStyle(x => x.FontColor(Colors.Red.Darken2).Bold()); } return container; } private static string FormatChange(decimal changeInPercent) { return changeInPercent.ToString("+0.00;-0.00;0.00", CultureInfo.InvariantCulture) + "%"; } ``` #### Composition Within the table, the conditional styling is applied with the `Element` method. Multiple `Element` calls can be chained: the first one applies the value-based style, while the second one applies the standard cell style shared by all cells. ```csharp{41-42,47-48} container.Table(table => { table.ColumnsDefinition(columns => { columns.RelativeColumn(); columns.ConstantColumn(100); columns.ConstantColumn(100); columns.ConstantColumn(100); }); table.Header(header => { header.Cell().Element(HeaderCellStyle).Text("Company"); header.Cell().Element(HeaderCellStyle).AlignRight().Text("Price"); header.Cell().Element(HeaderCellStyle).AlignRight().Text("Day"); header.Cell().Element(HeaderCellStyle).AlignRight().Text("YTD"); static IContainer HeaderCellStyle(IContainer container) { return container .ZIndex(1) .BorderBottom(2) .BorderColor(Colors.Grey.Darken3) .DefaultTextStyle(x => x.SemiBold()) .PaddingVertical(8) .PaddingHorizontal(10); } }); foreach (var quote in quotes) { table.Cell().Element(CellStyle).Text(text => { text.Span(quote.Company); text.Span($" ({quote.Ticker})").FontSize(12).FontColor(Colors.Grey.Darken1); }); table.Cell().Element(CellStyle).AlignRight() .Text(quote.Price.ToString("$#,##0.00", CultureInfo.InvariantCulture)); table.Cell() .Element(cell => PriceChangeHighlightStyle(cell, quote.DailyChange)) .Element(CellStyle) .AlignRight() .Text(FormatChange(quote.DailyChange)); table.Cell() .Element(cell => PriceChangeHighlightStyle(cell, quote.YearToDateChange)) .Element(CellStyle) .AlignRight() .Text(FormatChange(quote.YearToDateChange)); } static IContainer CellStyle(IContainer container) { return container .BorderBottom(1) .BorderColor(Colors.Grey.Lighten2) .PaddingVertical(8) .PaddingHorizontal(10); } }); ``` ![example](/patterns-and-practices/dynamic-composition-value-based-styling.webp) --- --- url: /concepts/dynamic-composition/configurable-tables.md --- # Dynamic composition: configurable tables Sometimes not only the data but also the structure of a document is determined at runtime. Typical examples include user-configurable reports where each customer selects which columns to display, report templates stored in a database, or generic export features. Because the QuestPDF Fluent API is executed as ordinary C# code, table columns, headers and cells can all be generated with loops based on an external configuration. In the following example, both the column definitions and the row data are provided as plain collections that could originate from any source. #### Report configuration Each column is described by its header text, sizing strategy and the name of the property that provides cell values: ```csharp public sealed record ReportColumn( string Header, bool IsConstantWidth, float Size, string PropertyName); ``` For this example, the configuration is defined directly in code. In a real application, it could just as well be deserialized from JSON, loaded from a database, or built from user preferences. ```csharp var reportColumns = new List { new(Header: "SKU", IsConstantWidth: true, Size: 100, PropertyName: "sku"), new(Header: "Product", IsConstantWidth: false, Size: 3, PropertyName: "name"), new(Header: "Warehouse", IsConstantWidth: false, Size: 2, PropertyName: "warehouse"), new(Header: "In stock", IsConstantWidth: true, Size: 90, PropertyName: "stock"), new(Header: "Unit price", IsConstantWidth: true, Size: 110, PropertyName: "price") }; ``` The row data is kept as dictionaries rather than a fixed class, so cell values can be accessed by property name: ```csharp var products = new List> { new() { ["sku"] = "MO-1042", ["name"] = "Wireless Optical Mouse", ["warehouse"] = "Gdansk", ["stock"] = "145", ["price"] = "$24.99" }, new() { ["sku"] = "KB-2205", ["name"] = "Mechanical Keyboard", ["warehouse"] = "Warsaw", ["stock"] = "38", ["price"] = "$89.50" }, new() { ["sku"] = "HU-3310", ["name"] = "USB-C Hub 7-in-1", ["warehouse"] = "Warsaw", ["stock"] = "76", ["price"] = "$45.00" }, new() { ["sku"] = "MS-4470", ["name"] = "27\" 4K Monitor Stand", ["warehouse"] = "Krakow", ["stock"] = "12", ["price"] = "$129.00" }, new() { ["sku"] = "WC-5521", ["name"] = "Full HD Webcam", ["warehouse"] = "Gdansk", ["stock"] = "210", ["price"] = "$59.00" } }; ``` #### Composition The table is generated entirely from the configuration. The `ColumnsDefinition` call chooses between constant and relative sizing for each column, the header row is created with a loop, and each cell reads its value by property name. ```csharp container.Table(table => { table.ColumnsDefinition(columns => { foreach (var column in reportColumns) { if (column.IsConstantWidth) columns.ConstantColumn(column.Size); else columns.RelativeColumn(column.Size); } }); table.Header(header => { foreach (var column in reportColumns) header.Cell().Element(HeaderCellStyle).Text(column.Header); static IContainer HeaderCellStyle(IContainer container) { return container .Background(Colors.Blue.Darken2) .DefaultTextStyle(x => x.FontColor(Colors.White).SemiBold()) .PaddingVertical(8) .PaddingHorizontal(10); } }); foreach (var product in products) { foreach (var column in reportColumns) table.Cell().Element(CellStyle).Text(product.GetValueOrDefault(column.PropertyName)); } static IContainer CellStyle(IContainer container) { return container .BorderBottom(1) .BorderColor(Colors.Grey.Lighten2) .PaddingVertical(8) .PaddingHorizontal(10); } }); ``` ![example](/patterns-and-practices/dynamic-composition-runtime-columns.webp) --- --- url: /concepts/dynamic-composition/nested-content.md --- # Dynamic composition: nested content Hierarchical data appears in many documents: product category trees, organization structures, tables of contents, nested bills of materials, and so on. Such structures usually have an arbitrary depth that is known only at runtime. Because QuestPDF layouts are composed with ordinary C# methods, rendering a tree is as simple as writing a method that calls itself for each child node. #### Data model Each node holds its own data and a list of child nodes: ```csharp public sealed record CategoryNode( string Name, int ProductCount, List Children); ``` ```csharp var catalog = new CategoryNode("All products", 231, [ new("Electronics", 154, [ new("Computers", 89, [ new("Laptops", 52, []), new("Desktops", 37, []) ]), new("Audio", 65, []) ]), new("Office supplies", 77, [ new("Paper", 41, []), new("Writing instruments", 36, []) ]) ]); ``` #### Composition The composition method renders the current node and then invokes itself for every child. The `depth` parameter is incremented on each level and controls the left indentation, making the hierarchy visible. ```csharp{6,13-17} private void ComposeCategory(IContainer container, CategoryNode node, int depth = 0) { container.Column(column => { column.Item() .PaddingLeft(depth * 25) .Text(text => { text.Span(node.Name).SemiBold(); text.Span($" ({node.ProductCount} products)").FontColor(Colors.Grey.Medium); }); foreach (var child in node.Children) { column.Item() .PaddingTop(8) .Element(x => ComposeCategory(x, child, depth + 1)); } }); } ``` ::: tip The `Column` element supports paging, so even large trees flow naturally across multiple pages. ::: #### Usage To start the recursion, invoke the composition method with the root node: ```csharp container.Element(content => ComposeCategory(content, catalog)); ``` ![example](/patterns-and-practices/dynamic-composition-recursive-content.webp) --- --- url: /api-reference/background.md --- # Background Background can be used to enhance the visual appearance of your document by providing a solid color or a gradient effect. ::: tip Learn more about supported color formats and predefined color palettes in the [Colors](/concepts/colors) section. ::: ## Solid color Sets a solid background color behind its content. ```csharp .Background("#00FF00") .Background(Colors.Green.Lighten2) ``` ```csharp{30} using QuestPDF.Helpers; var colors = new[] { Colors.LightBlue.Darken4, Colors.LightBlue.Darken3, Colors.LightBlue.Darken2, Colors.LightBlue.Darken1, Colors.LightBlue.Medium, Colors.LightBlue.Lighten1, Colors.LightBlue.Lighten2, Colors.LightBlue.Lighten3, Colors.LightBlue.Lighten4, Colors.LightBlue.Lighten5, Colors.LightBlue.Accent1, Colors.LightBlue.Accent2, Colors.LightBlue.Accent3, Colors.LightBlue.Accent4, }; container .Height(150) .Width(420) .Row(row => { foreach (var color in colors) row.RelativeItem().Background(color); }); ``` ![example](/api-reference/background-solid.webp) ## Gradient Applies a linear gradient background to the container with the specified angle and colors. The first argument is the angle in degrees, and the second argument is an array of colors that define the gradient. ```csharp{6,10,14} .Column(column => { column.Spacing(25); column.Item() .BackgroundLinearGradient(0, [Colors.Red.Lighten2, Colors.Blue.Lighten2]) .AspectRatio(2); column.Item() .BackgroundLinearGradient(45, [Colors.Green.Lighten2, Colors.LightGreen.Lighten2, Colors.Yellow.Lighten2]) .AspectRatio(2); column.Item() .BackgroundLinearGradient(90, [Colors.Yellow.Lighten2, Colors.Amber.Lighten2, Colors.Orange.Lighten2]) .AspectRatio(2); }); ``` ![example](/api-reference/background-gradient.webp) ## Rounded Corners Sets the corner radius for the background, creating rounded corners. ::: tip Read more about [rounded corners](/api-reference/rounded-corners.md). ::: ```csharp{2-3} container .Background(Colors.Grey.Lighten2) .CornerRadius(25) .Padding(25) .Text("Content with rounded corners"); ``` ![example](/api-reference/background-rounded-corners.webp) --- --- url: /api-reference/border.md --- # Border You can use borders to create visual separation between elements in your document. Borders can be applied to any element, including text, images, and containers. ```csharp{2} container .Border(3, Colors.Blue.Darken4) .Background(Colors.Blue.Lighten5) .Padding(25) .Text(text => { text.DefaultTextStyle(x => x.FontColor(Colors.Blue.Darken4).FontSize(16)); text.Span("TIP: ").Bold(); text.Span("You can use borders to create visual separation between elements in your document. Borders can be applied to any element, including text, images, and containers."); }); ``` ![example](/api-reference/border-simple.webp) ## Thickness | Method | Description | |----------------------|------------------------------------------------------------| | **Border** | Sets a uniform border (all edges) for its content. | | **BorderVertical** | Sets a vertical border (left and right) for its content. | | **BorderHorizontal** | Sets a horizontal border (top and bottom) for its content. | | **BorderLeft** | Sets a border on the left side of its content. | | **BorderRight** | Sets a border on the right side of its content. | | **BorderTop** | Sets a border on the top side of its content. | | **BorderBottom** | Sets a border on the bottom side of its content. | Each method requires a thickness value as a parameter. Optionally, you can specify the unit value (default is `Unit.Points`). ```csharp container.Border(1); container.Border(1, Unit.Millimeters); ``` ::: tip Learn more about supported units in the [Lenght unit types](/concepts/length-unit-types) section. ::: ### Consistent thickness ```csharp{6,12,18} .Row(row => { row.Spacing(25); row.RelativeItem() .Border(1, Colors.Black) .Padding(10) .AlignCenter() .Text("Thin"); row.RelativeItem() .Border(3, Colors.Black) .Padding(10) .AlignCenter() .Text("Medium"); row.RelativeItem() .Border(9, Colors.Black) .Padding(10) .AlignCenter() .Text("Bold"); }); ``` ![example](/api-reference/border-thickness-consistent.webp) ### Various thickness ```csharp{2-5} container .BorderLeft(4) .BorderTop(6) .BorderRight(8) .BorderBottom(10) .Padding(25) .Text("Sample text"); ``` ![example](/api-reference/border-thickness-various.webp) ## Solid Color In the vast majority of cases, borders are applied with a solid color. ::: tip Learn more about supported color formats and predefined color palettes in the [Colors](/concepts/colors) section. ::: ```csharp{16} .Row(row => { var colors = new[] { Colors.Red.Medium, Colors.Green.Medium, Colors.Blue.Medium }; row.Spacing(25); foreach (var color in colors) { row.RelativeItem() .Border(5) .BorderColor(color) .Padding(15) .Text(color) .FontColor(color); } }); ``` ![example](/api-reference/border-color-solid.webp) ## Gradient Applies a linear gradient background to the border with the specified angle and colors. The first argument is the angle in degrees, and the second argument is an array of colors that define the gradient. ```csharp{7,14,21} .Column(column => { column.Spacing(25); column.Item() .Border(5) .BorderLinearGradient(0, [Colors.Red.Darken1, Colors.Blue.Darken1]) .BorderAlignmentInside() .Padding(25) .Text("Horizontal gradient"); column.Item() .Border(10) .BorderLinearGradient(45, [Colors.Green.Darken1, Colors.LightGreen.Darken1, Colors.Yellow.Darken1]) .BorderAlignmentInside() .Padding(25) .Text("Diagonal gradient"); column.Item() .Border(10) .BorderLinearGradient(90, [Colors.Yellow.Darken1, Colors.Amber.Darken1, Colors.Orange.Darken1]) .CornerRadius(20) .Padding(25) .Text("Vertical gradient"); }); ``` ![example](/api-reference/border-color-gradient.webp) ## Alignment You can control the alignment of the border relative to the container's boundaries using the following methods: | Method | Description | |----------------------------|------------------------------------------------------------------------| | **BorderAlignmentOutside** | Aligns the container's border to the outer edge of the element. | | **BorderAlignmentMiddle** | Aligns the border in the middle of the specified container boundaries. | | **BorderAlignmentInside** | Aligns the border to the inside of the container. | By default, the border is aligned to the middle of the container boundaries. However, if the border has rounded corners, the alignment is set to inside by default. ```csharp{12,18,24} .Row(row => { row.Spacing(25); row.RelativeItem() .Background(Colors.Grey.Lighten1) .Padding(25) .Text("No Border"); row.RelativeItem() .Border(10, Colors.Grey.Darken2) .BorderAlignmentInside() .Padding(25) .Text("Border Inside"); row.RelativeItem() .Border(10, Colors.Grey.Darken2) .BorderAlignmentMiddle() .Padding(25) .Text("Border Middle"); row.RelativeItem() .Border(10, Colors.Grey.Darken2) .BorderAlignmentOutside() .Padding(25) .Text("Border Outside"); }); ``` ![example](/api-reference/border-alignment.webp) ## Examples ### Rounded corners Borders support rounded corners, which can be applied using the `CornerRadius` method. ::: tip Read more about [rounded corners](/api-reference/rounded-corners.md). ::: ```csharp{2-3} container .CornerRadius(10) .Border(1, Colors.Black) .Background(Colors.Grey.Lighten2) .Padding(25) .Text("Border with rounded corners"); ``` ![example](/api-reference/border-rounded-corners-1.webp) ### Multiple borders It is possible to apply multiple borders to the same content by separating each border instance with the `Container` method. ```csharp{6} container .BorderVertical(5) .BorderColor(Colors.Green.Darken2) .BorderAlignmentInside() .Container() .BorderHorizontal( 10) .BorderColor(Colors.Blue.Lighten1) .BorderAlignmentInside() .Background(Colors.Grey.Lighten2) .PaddingVertical(25) .PaddingHorizontal(50) .Text("Content"); ``` ![example](/api-reference/border-multiple.webp) ### Advanced style You can create advanced styles by combining borders with other properties, such as background color, padding, and text styles. ```csharp{2-5} container .CornerRadius(10) .BorderLeft(10) .BorderAlignmentInside() .BorderColor(Colors.Green.Darken2) .Background(Colors.Green.Lighten4) .Padding(25) .PaddingLeft(10) .DefaultTextStyle(x => x.FontColor(Colors.Green.Darken4)) .Column(column => { column.Item().Text("Completed").Bold(); column.Item().Height(5); column.Item().Text("The invoice has been paid in full.").FontSize(16); }); ``` ![example](/api-reference/border-rounded-corners-2.webp) --- --- url: /api-reference/rounded-corners.md --- # Rounded Corners Rounded corners can be applied to containers to create visually appealing designs. This feature allows you to specify the radius of the corners, giving a softer look to the edges of the container. | Method | Description | |-----------------------------|--------------------------------------------------------------------------------------------------------| | **CornerRadius** | Applies a uniform corner radius to all corners of the container with the specified value and unit. | | **CornerRadiusTopLeft** | Applies a corner radius to the top-left corner of the container with the specified value and unit. | | **CornerRadiusTopRight** | Applies a corner radius to the top-right corner of the container with the specified value and unit. | | **CornerRadiusBottomLeft** | Applies a border radius to the bottom-left corner of the container with the specified value and unit. | | **CornerRadiusBottomRight** | Applies a corner radius to the bottom-right corner of the container with the specified value and unit. | ## Consistent Corner Radius In the vast majority of cases, you will want to apply the same corner radius to all corners of a container. ```csharp{4} container .Border(1, Colors.Black) .Background(Colors.Grey.Lighten3) .CornerRadius(25) .Padding(25) .Text("Container with consistently rounded corners"); ``` ![example](/api-reference/rounded-corners-consistent.webp) ## Various Corner Radius It is also possible to apply different corner radii to each corner of a container, allowing for more complex designs. ```csharp{4-7} container .Border(1, Colors.Black) .Background(Colors.Grey.Lighten3) .CornerRadiusTopLeft(5) .CornerRadiusTopRight(10) .CornerRadiusBottomRight(20) .CornerRadiusBottomLeft(40) .Padding(25) .Text("Container with rounded corners"); ``` ![example](/api-reference/rounded-corners-various.webp) ## Image Example Rounded corners can also be applied to images, enhancing their appearance in documents. ```csharp{2} container .CornerRadius(25) .Image("Resources/landscape.jpg"); ``` ![example](/api-reference/rounded-corners-image.webp) ## Complex Example Rounded corners can be used in more complex layouts, such as tables, to create a polished look. ```csharp{3} container .Border(1, Colors.Black) .CornerRadius(15) .Table(table => { table.ColumnsDefinition(columns => { columns.ConstantColumn(100); columns.RelativeColumn(); columns.ConstantColumn(150); }); table.Header(header => { header.Cell().Element(Style).Text("Index"); header.Cell().Element(Style).Text("Label"); header.Cell().Element(Style).Text("Price"); IContainer Style(IContainer container) { return container .Border(1, Colors.Grey.Darken2) .Background(Colors.Grey.Lighten3) .PaddingVertical(10) .PaddingHorizontal(15) .DefaultTextStyle(x => x.Bold()); } }); foreach (var index in Enumerable.Range(1, 5)) { table.Cell().Element(Style).Text(index.ToString()); table.Cell().Element(Style).Text(Placeholders.Label()); table.Cell().Element(Style).Text(Placeholders.Price()); IContainer Style(IContainer container) { return container .Border(1, Colors.Grey.Darken2) .PaddingVertical(10) .PaddingHorizontal(15); } } }); ``` ![example](/api-reference/rounded-corners-complex.webp) --- --- url: /api-reference/shadow.md --- # Shadow Shadows can enhance the visual depth and separation of elements in a document. ```csharp{3-10} container .Border(1, Colors.Black) .Shadow(new BoxShadowStyle { Color = Colors.Grey.Medium, Blur = 5, Spread = 5, OffsetX = 5, OffsetY = 5 }) .Background(Colors.White) .Padding(15) .Text("Important content"); ``` ![example](/api-reference/shadow-simple.webp) ## Blur Gets or sets the blur radius of the shadow in pixels. Higher values produce a more diffused shadow with softer edges. A value of 0 results in a sharp, unblurred shadow. ::: tip Values different from 0 may significantly impact performance and enlarge the output file size. Use with caution, especially in large documents or when rendering complex shadows. ::: ```csharp{12} .Row(row => { row.Spacing(50); foreach (var blur in new[] { 5, 10, 20 }) { row.ConstantItem(100) .AspectRatio(1) .Shadow(new BoxShadowStyle { Color = Colors.Grey.Darken1, Blur = blur }) .Background(Colors.White); } }); ``` ![example](/api-reference/shadow-blur.webp) ## Spread Gets or sets the spread radius of the shadow in pixels. Positive values cause the shadow to expand, negative values cause it to contract. ```csharp{13} .Row(row => { row.Spacing(50); foreach (var spread in new[] { 0, 5, 10 }) { row.ConstantItem(100) .AspectRatio(1) .Shadow(new BoxShadowStyle { Color = Colors.Grey.Darken1, Blur = 5, Spread = spread }) .Background(Colors.White); } }); ``` ![example](/api-reference/shadow-spread.webp) ## Offset X Gets or sets the horizontal offset of the shadow in pixels. Positive values move the shadow to the right, negative values move it to the left. ```csharp{13} .Row(row => { row.Spacing(50); foreach (var offsetX in new[] { -10, 0, 10 }) { row.ConstantItem(100) .AspectRatio(1) .Shadow(new BoxShadowStyle { Color = Colors.Grey.Darken1, Blur = 10, OffsetX = offsetX }) .Background(Colors.White); } }); ``` ![example](/api-reference/shadow-offset-x.webp) ## Offset Y Gets or sets the vertical offset of the shadow in pixels. Positive values move the shadow downward, negative values move it upward. ```csharp{13} .Row(row => { row.Spacing(50); foreach (var offsetY in new[] { -10, 0, 10 }) { row.ConstantItem(100) .AspectRatio(1) .Shadow(new BoxShadowStyle { Color = Colors.Grey.Darken2, Blur = 10, OffsetY = offsetY }) .Background(Colors.White); } }); ``` ![example](/api-reference/shadow-offset-y.webp) ## Color Gets or sets the color of the shadow. ```csharp{18} .Row(row => { row.Spacing(50); var colors = new[] { Colors.Red.Darken2, Colors.Green.Darken2, Colors.Blue.Darken2 }; foreach (var color in colors) { row.ConstantItem(100) .AspectRatio(1) .Shadow(new BoxShadowStyle { Color = color, Blur = 10 }) .Background(Colors.White); } }); ``` ![example](/api-reference/shadow-color.webp) ## Without Blur (Fast) Shadows can be applied without any blur effect for a sharper appearance. This approach is faster and results in significantly smaller file sizes, making it suitable for performance-sensitive applications. ```csharp{10,22} .Row(row => { row.Spacing(50); row.ConstantItem(100) .AspectRatio(1) .Shadow(new BoxShadowStyle { Color = Colors.Grey.Lighten1, Blur = 0, OffsetX = 8, OffsetY = 8 }) .Border(1, Colors.Black) .Background(Colors.White); row.ConstantItem(100) .AspectRatio(1) .Shadow(new BoxShadowStyle { Color = Colors.Grey.Lighten1, Blur = 0, OffsetX = 8, OffsetY = 8 }) .Border(1, Colors.Black) .CornerRadius(16) .Background(Colors.White); }); ``` ![example](/api-reference/shadow-no-blur.webp) --- --- url: /api-reference/text/basics.md --- # Text ## Simple usage In most cases, text content can be added using the following shorthand. The text will inherit the default style. ```csharp container .Text("Hello, World!"); ``` ![example](/api-reference/text-basic.webp) ## Customization The `Text` method returns a descriptor that allows further customization of the text style. ```csharp{7-8,12-14,18-20} .Column(column => { column.Spacing(10); column.Item() .Element(CellStyle) .Text("Text with blue color") .FontColor(Colors.Blue.Darken1); column.Item() .Element(CellStyle) .Text("Bold and underlined text") .Bold() .Underline(); column.Item() .Element(CellStyle) .Text("Centered small text") .FontSize(12) .AlignCenter(); static IContainer CellStyle(IContainer container) => container.Background(Colors.Grey.Lighten3).Padding(10); }); ``` ![example](/api-reference/text-basic-descriptor.webp) ## Rich text formatting It is also possible to format specific parts of the text content using spans: ```csharp{5,7,9,11} container .Text(text => { text.Span("The "); text.Span("chemical formula").Underline(); text.Span(" of "); text.Span("sulfuric acid").BackgroundColor(Colors.Amber.Lighten3); text.Span(" is H"); text.Span("2").Subscript(); text.Span("SO"); text.Span("4").Subscript(); text.Span("."); }); ``` ![example](/api-reference/text-rich.webp) ## Typography pattern The typography pattern helps maintain consistent text styling across your documents. ```csharp public static class Typography { public static TextStyle Title => TextStyle .Default .FontType("Helvetica") .FontColor(Colors.Black) .FontSize(20) .Bold(); public static TextStyle Headline => TextStyle .Default .FontType("Helvetica") .FontColor(Colors.Blue.Medium) .FontSize(14); public static TextStyle Normal => TextStyle .Default .FontType("Helvetica") .FontColor("#000000") .FontSize(10) .LineHeight(1.25f) .AlignLeft(); } ``` Then, a predefined typography can be used in the following way: ```csharp{3} container .Text("Report #123") .Style(Typography.Title); // instead of container .Text("Report #123") .FontType("Helvetica") .FontColor(Colors.Black) .FontSize(20) .Bold(); ``` ## Hyperlinks Hyperlink is a clickable text that redirects the user to a specific webpage. ```csharp{8,10,12} .Text(text => { var hyperlinkStyle = TextStyle.Default .FontColor(Colors.Blue.Medium) .Underline(); text.Span("To learn more about QuestPDF, please visit its "); text.Hyperlink("homepage", "https://www.questpdf.com/").Style(hyperlinkStyle); text.Span(", "); text.Hyperlink("GitHub repository", "https://github.com/QuestPDF/QuestPDF").Style(hyperlinkStyle); text.Span(" and "); text.Hyperlink("NuGet package page", "https://www.nuget.org/packages/QuestPDF").Style(hyperlinkStyle); text.Span("."); }); ``` --- --- url: /api-reference/text/text-style.md --- # Text Style ## Font Size Font size measures the height of text characters, determining how large or small the text appears. It's worth noting that different fonts may render text with different visual sizes, even when assigned the same numerical font size. ```csharp{7,11,15} .Column(column => { column.Spacing(10); column.Item() .Text("This is small text (16pt)") .FontSize(16); column.Item() .Text("This is medium text (24pt)") .FontSize(24); column.Item() .Text("This is large text (36pt)") .FontSize(36); }); ``` ![example](/api-reference/text-font-size.webp) ## Font Family A font family is a collection of related fonts that share a consistent design style but may vary in weight, style, or width. Examples of font families include Arial, Times New Roman, and Calibri. ```csharp{8,11} .Column(column => { column.Spacing(10); column.Item().Text("This is text with default font (Lato)"); column.Item().Text("This is text with Times New Roman font") .FontFamily("Times New Roman"); column.Item().Text("This is text with Courier New font") .FontFamily("Courier New"); }); ``` ![example](/api-reference/text-font-family.webp) ## Font Fallback The Font Fallback option is a list of alternative fonts that are used when specific glyphs are unavailable in the primary font. This ensures that text is displayed correctly across different systems and environments. A common example is the display of non-Latin characters, such as Arabic or Chinese, which may not be supported by all fonts. ```csharp{3} container .Text("The Arabic word for programming is البرمجة.") .FontFamily("Lato", "Noto Sans Arabic"); ``` ![example](/api-reference/text-font-fallback.webp) It's also useful for displaying emojis, which are not universally supported by all fonts. ```csharp{3} container .Text("Popular emojis include 😊, 😂, ❤️, 👍, and 😎.") .FontFamily("Lato", "Noto Emoji"); ``` ![example](/api-reference/text-font-fallback-emoji.webp) ## Font Color The font color determines the color applied to text characters, affecting their visual appearance. It also influences the default color of text decorations, such as underlines. ```csharp{4,6,8} .Text(text => { text.Span("Each pixels consists of three sub-pixels: "); text.Span("red").FontColor(Colors.Red.Medium); text.Span(", "); text.Span("green").FontColor(Colors.Green.Medium); text.Span(" and "); text.Span("blue").FontColor(Colors.Blue.Medium); text.Span("."); }); ``` ![example](/api-reference/text-font-color.webp) ## Background Color Sets a solid background color for the text. This color fills the area behind the text or other elements, enhancing contrast and providing visual emphasis. ```csharp{4} .Text(text => { text.Span("The term "); text.Span("algorithm").BackgroundColor(Colors.Yellow.Lighten3).Bold(); text.Span(" "); }); ``` ![example](/api-reference/text-font-background.webp) ## Font Weight Determines the thickness of the text characters, ranging from light to bold, to create visual hierarchy or emphasis. Please note that not all fonts support every weight. If the specified weight isn't available, the library selects the closest available option. | Name | CSS Value | Example Text | |---------------|-----------|------------------------------------------------| | Thin | 100 | Example | | ExtraLight | 200 | Example | | Light | 300 | Example | | NormalWeight | 400 | Example | | Medium | 500 | Example | | SemiBold | 600 | Example | | Bold | 700 | Example | | ExtraBold | 800 | Example | | Black | 900 | Example | | ExtraBlack | 1000 | Example | ```csharp{4,6,8,10} .Text(text => { text.Span("This sentence demonstrates "); text.Span("bold").Bold(); text.Span(", "); text.Span("normal").NormalWeight(); text.Span(", "); text.Span("light").Light(); text.Span(" and "); text.Span("thin").Thin(); text.Span(" font weights."); }); ``` ![example](/api-reference/text-font-weight.webp) :::warning QuestPDF does not currently support fonts with variable weights. ::: ## Italic Renders text with an italic effect, where letters are slightly slanted to the right. Commonly used for emphasis or to distinguish specific words. ```csharp{4} .Text(text => { text.Span("In this sentence, the word "); text.Span("important").Italic(); text.Span(" is emphasized using italics."); }); ``` ![example](/api-reference/text-font-italic.webp) ## Decorations Applies decorative lines on text. Commonly used to emphasize specific words or phrases. ### Positions It is also possible to customize the decoration position: * Underline, * Strikethrough, * Overline. ```csharp{4,6,8} .Text(text => { text.Span("There are a couple of available text decorations: "); text.Span("underline").Underline().FontColor(Colors.Red.Medium); text.Span(", "); text.Span("strikethrough").Strikethrough().FontColor(Colors.Green.Medium); text.Span(" and "); text.Span("overline").Overline().FontColor(Colors.Blue.Medium); text.Span(". "); }); ``` ![example](/api-reference/text-decoration-types.webp) ### Styles It is also possible to customize the decoration line style: * DecorationSolid, * DecorationDouble, * DecorationWavy, * DecorationDotted, * DecorationDashed. ```csharp{4,6,8,10,12} .Text(text => { text.Span("Moreover, the decoration can be "); text.Span("solid").Underline().DecorationSolid().FontColor(Colors.Indigo.Medium); text.Span(", "); text.Span("double").Underline().DecorationDouble().FontColor(Colors.Blue.Medium); text.Span(", "); text.Span("wavy").Underline().DecorationWavy().FontColor(Colors.LightBlue.Medium); text.Span(", "); text.Span("dotted").Underline().DecorationDotted().FontColor(Colors.Cyan.Medium); text.Span(" or "); text.Span("dashed").Underline().DecorationDashed().FontColor(Colors.Green.Medium); text.Span("."); }); ``` ![example](/api-reference/text-decoration-styles.webp) ### Styling By default, the decoration line color is the same as the text color, and the decoration thickness is determined by the font. However, these properties can be customized. ```csharp{6-9} .Text(text => { text.Span("This text contains a "); text.Span("seriuos") .Underline() .DecorationWavy() .DecorationColor(Colors.Red.Medium) .DecorationThickness(2); text.Span(" typo."); }); ``` ![example](/api-reference/text-decoration-advanced.webp) ## Subscript Subscript displays text slightly below the baseline, often in a smaller size. Commonly used for chemical formulas or mathematical notations ```csharp{4} .Text(text => { text.Span("H"); text.Span("2").Subscript(); text.Span("O is the chemical formula for water."); }); ``` ![example](/api-reference/text-subscript.webp) ## Superscript Superscript displays text slightly above the baseline, often in a smaller size. Typically used for exponents, footnotes, or ordinal indicators ```csharp{4} .Text(text => { text.Span("E = mc"); text.Span("2").Superscript(); text.Span(" is the equation of mass-energy equivalence."); }); ``` ![example](/api-reference/text-superscript.webp) ## Line Height Adjusts the vertical spacing between lines of text, affecting readability and overall text layout. The added space is proportional to the text size. ```csharp{16} .Column(column => { column.Spacing(20); float[] lineHeights = [0.75f, 1f, 2f]; var paragraph = Placeholders.Paragraph(); foreach (var lineHeight in lineHeights) { column .Item() .Background(Colors.Grey.Lighten3) .Padding(5) .Text(paragraph) .FontSize(16) .LineHeight(lineHeight); } }); ``` ![example](/api-reference/text-line-height.webp) ## Letter Spacing Adjusts the horizontal spacing between characters in the text, affecting readability and overall visual style. The adjustment is proportional to the text size. ```csharp .Column(column => { column.Spacing(20); var letterSpacing = new[] { -0.08f, 0f, 0.2f }; var paragraph = Placeholders.Sentence(); foreach (var spacing in letterSpacing) { column .Item() .Background(Colors.Grey.Lighten3) .Padding(5) .Text(paragraph) .FontSize(18) .LetterSpacing(spacing); } }); ``` ![example](/api-reference/text-letter-spacing.webp) ## Word Spacing Adjusts the horizontal spacing between words in the text, affecting readability and overall visual style. The adjustment is proportional to the text size. ```csharp .Column(column => { column.Spacing(20); var wordSpacing = new[] { -0.2f, 0f, 0.4f }; var paragraph = Placeholders.Sentence(); foreach (var spacing in wordSpacing) { column.Item() .Background(Colors.Grey.Lighten3) .Padding(5) .Text(paragraph) .FontSize(16) .WordSpacing(spacing); } }); ``` ![example](/api-reference/text-word-spacing.webp) ## Font Features Font features are a set of typographic features that can be applied to text to enhance its appearance. They are used to control various aspects of text rendering. Font features are always encoded as 4-character long strings. For example, the ligatures feature is encode as `liga`, while the kernig feature as `kern`. For a list of available features, refer to the `QuestPDF.Helpers.FontFeatures` class. ::: tip Please note that fonts usually support only a subset of font features. If you try to enable a feature that is not supported by the font, it will be ignored. Moreover, some fonts have features enabled by default, and you may not see any difference when enabling them. ::: ::: info QuestPDF disables the `StandardLigatures` feature by default. Enable it explicitly whenever you want ligatures in your document. ::: ### Example Let's analyze the `StandardLigatures` font feature, which replaces specific pairs of letters (such as 'fi' or 'fl') with a single, combined glyph to enhance aesthetics. ```csharp{15,27} .Row(row => { row.Spacing(25); row.RelativeItem() .Background(Colors.Grey.Lighten3) .Padding(10) .Column(column => { column.Item().Text("Without ligatures").FontSize(16); column.Item() .Text("fly and fight") .FontSize(32) .DisableFontFeature(FontFeatures.StandardLigatures); }); row.RelativeItem() .Background(Colors.Grey.Lighten3) .Padding(10) .Column(column => { column.Item().Text("With ligatures").FontSize(16); column.Item().Text("fly and fight") .FontSize(32) .EnableFontFeature(FontFeatures.StandardLigatures); }); }); ``` ![example](/api-reference/text-font-features.webp) --- --- url: /api-reference/text/paragraph-style.md --- # Paragraph Style ## Text Alignment Determines how text is positioned horizontally within its container. ```csharp{3,10} container .Text("Sample text") .AlignCenter(); // or container .Text(text => { text.AlignCenter(); text.Span(Placeholders.Paragraph()); }); ``` Available alignment options: | Alignment Type | Description | |-----------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | **AlignLeft** | Aligns text horizontally to the left side. | | **AlignCenter** | Aligns text horizontally to the center, ensuring equal space on both left and right sides. | | **AlignRight** | Aligns content horizontally to the right side. | | **AlignStart** | Aligns the text horizontally to the start of the container. This method sets the horizontal alignment of the text to the start (left for left-to-right languages, right for right-to-left languages). | | **AlignEnd** | Aligns the text horizontally to the end of the container. This method sets the horizontal alignment of the text to the end (right for left-to-right languages, left for right-to-left languages). | | **Justify** | Justifies the text within its container. This method sets the horizontal alignment of the text to be justified, meaning it aligns along both the left and right margins, creating a clean, block-like appearance for the text. | Example: ```csharp{8,13,18,23} .Column(column => { column.Spacing(20); column.Item() .Element(CellStyle) .Text("This is an example of left-aligned text, showcasing how the text starts from the left margin and continues naturally across the container.") .AlignLeft(); column.Item() .Element(CellStyle) .Text("This text is centered within its container, creating a balanced look, especially for titles or headers.") .AlignCenter(); column.Item() .Element(CellStyle) .Text("This example demonstrates right-aligned text, often used for dates, numbers, or aligning text to the right margin.") .AlignRight(); column.Item() .Element(CellStyle) .Text("Justified text adjusts the spacing between words so that both the left and right edges of the text block are aligned, creating a clean, newspaper-like look.") .Justify(); static IContainer CellStyle(IContainer container) => container.Background(Colors.Grey.Lighten3).Padding(10); }); ``` ![example](/api-reference/text-paragraph-alignment.webp) ## Default Text Style Applies a consistent style for the whole content within the Text element. ```csharp{3} .Text(text => { text.DefaultTextStyle(x => x.Light().LetterSpacing(-0.1f).WordSpacing(0.1f)); text.Span("Changing typography settings helps creating "); text.Span("significant").LetterSpacing(0.2f).Black().BackgroundColor(Colors.Grey.Lighten2); text.Span(" visual contrast."); }); ``` ![example](/api-reference/text-paragraph-default-style.webp) ## Paragraph Spacing Adjusts the vertical gap between successive paragraphs (separated by line breaks), helping to visually separate blocks of text for improved readability. ```csharp{3,10} container .Text(Placeholders.Paragraphs()) .ParagraphFirstLineIndentation(40); // or container .Text(text => { text.ParagraphSpacing(20); text.Span(Placeholders.Paragraphs()); }); ``` ![example](/api-reference/text-paragraph-spacing.webp) ## First Line Indentation Specifies the horizontal offset of the first line in a paragraph. Commonly used to visually separate paragraphs in a block of text. ```csharp{3,10} container .Text(Placeholders.Paragraphs()) .ParagraphFirstLineIndentation(40); // or container .Text(text => { text.ParagraphFirstLineIndentation(20); text.Span(Placeholders.Paragraphs()); }); ``` ![example](/api-reference/text-paragraph-first-line-indentation.webp) ## Clamp Line With Ellipsis Limits the number of visible lines in a paragraph, truncating overflow text with an ellipsis or by hiding it to maintain layout consistency. ```csharp{17,25} container .Column(column => { column.Spacing(10); var paragraph = Placeholders.Paragraph(); column.Item() .Background(Colors.Grey.Lighten3) .Padding(5) .Text(paragraph); column.Item() .Background(Colors.Grey.Lighten3) .Padding(5) .Text(paragraph) .ClampLines(3); }); // or container .Text(text => { text.ClampLines(3); text.Span(Placeholders.Paragraphs()); }); ``` ![example](/api-reference/text-paragraph-clamp-lines.webp) It is also possible to customize the ellipsis: ```csharp{3} container .Text(Placeholders.Paragraph()) .ClampLines(3, " [...]"); ``` ![example](/api-reference/text-paragraph-clamp-lines-custom-ellipsis.webp) --- --- url: /api-reference/text/page-numbers.md --- # Page Numbers ## Document related The following methods allow you to inject page numbers into any text container. | Method | Description | |------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | **CurrentPageNumber** | Appends text showing the current page number. | | **TotalPages** | Appends text showing the total number of pages in the document. | ```csharp{13-18} Document.Create(document => { document.Page(page => { page.Size(PageSizes.A5); page.Margin(25); // content page.Footer() .PaddingTop(25) .AlignCenter() .Text(text => { text.CurrentPageNumber(); text.Span(" / "); text.TotalPages(); }); }); }); ``` ![example](/api-reference/text-page-number.webp) ## Section related The following methods allow you to display page numbers relative to a specific section of the document. ::: tip [Section](/api-reference/section) defines part of the document that can be further referenced by name, e.g. for page numbering or linking. ::: #### Available methods | Method | Description | |------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | **BeginPageNumberOfSection** | Appends text showing the number of the first page of the specified Section. | | **EndPageNumberOfSection** | Appends text showing the number of the last page of the specified Section. | | **PageNumberWithinSection** | Appends text showing the page number relative to the beginning of the given Section. For a section spanning pages 20 to 50, page 35 will show as 15. | | **TotalPagesWithinSection** | Appends text showing the total number of pages within the given Section. For a section spanning pages 20 to 50, the total is 30 pages. | #### Example First, define a section somewhere in the document: ```csharp{2} container .Section("customSection") // content of custom section ``` Then, refer to this location position in your list of contents: ```csharp container .Text(text => { // page number where section begins text.BeginPageNumberOfSection("customSection"); // page number where section ends text.EndPageNumberOfSection("customSection"); // page number relative to section beginning // at section beginning page, method returns 1 text.PageNumberWithinSection("customSection"); // how many pages section takes text.TotalPagesWithinSection("customSection"); }); ``` ## Custom formatting It is possible to format page numbers to a specified format, e.g. with leading zeros or as Roman numerals. ```csharp{4} container .Text(text => { text.CurrentPageNumber().Format(FormatWithLeadingZeros); }); // helper function static string FormatWithLeadingZeros(int? pageNumber) { const int expectedLength = 3; pageNumber ??= 1; return pageNumber.Value.ToString($"D{expectedLength}"); } ``` ::: warning Please note that the formatting function accepts a nullable integer (int?). QuestPDF employs a two-pass rendering algorithm, meaning that page numbers are only determined during the second pass. During the first pass, your formatting method will receive null, indicating that the page number has not yet been determined. Please ensure that any text you return matches the expected output length. ::: --- --- url: /api-reference/text/injecting-custom-content.md --- # Injecting custom content It is possible to inject custom content into the document, e.g. images. ::: warning The element must fit within one line and cannot span multiple pages. ::: ## Image The most common use-case is to inject images into the text. ```csharp{4,7} .Text(text => { text.Span("A unit test can either "); text.Element().PaddingBottom(-4).Height(24).Image("unit-test-completed-icon.png"); text.Span(" pass").FontColor(Colors.Green.Medium); text.Span(" or "); text.Element().PaddingBottom(-4).Height(24).Image("unit-test-failed-icon.png"); text.Span(" fail").FontColor(Colors.Red.Medium); text.Span("."); }); ``` ![example](/api-reference/text-inject-image.webp) ## SVG Another common use-case is to inject SVG icons into the text. ```csharp{4} .Text(text => { text.Span("To synchronize your email inbox, please click the "); text.Element().PaddingBottom(-4).Height(24).Svg("mail-synchronize-icon.svg"); text.Span(" icon."); }); ``` ![example](/api-reference/text-inject-svg.webp) ## Position The injected element can be positioned in relation to the text baseline or font edges. | **Enum Value** | **Description** | |-------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | **AboveBaseline** | Aligns the bottom edge of the injected element with the text baseline. The injected element sits on top of the baseline. | | **BelowBaseline** | Aligns the top edge of the injected element with the text baseline. The injected element hangs below the baseline. | | **Top** | Aligns the top edge of the injected element with the top edge of the font. If the injected element is very tall, the extra space will hang from the top and extend downward. | | **Bottom** | Aligns the bottom edge of the injected element with the top edge of the font. If the injected element is very tall, the extra space will rise from the bottom and extend upward. | | **Middle** | Aligns the middle of the injected element with the middle of the text. If the injected element is very tall, the extra space will grow equally from the top and bottom. | #### Example: ```csharp{5,11} .Text(text => { text.Span("This "); text.Element(TextInjectedElementAlignment.AboveBaseline) .Width(12).Height(12) .Background(Colors.Green.Medium); text.Span(" element is positioned above the baseline, while this "); text.Element(TextInjectedElementAlignment.BelowBaseline) .Width(12).Height(12) .Background(Colors.Blue.Medium); text.Span(" element is positioned below the baseline."); }); ``` ![example](/api-reference/text-inject-position.webp) --- --- url: /api-reference/text/style-inheritance.md --- # Style inheritance The `DefaultTextStyle` API allows you to define text styles that are automatically inherited by all child elements unless explicitly overridden. This hierarchical approach simplifies style management, ensuring consistency across document sections while enabling easy local customizations. It reduces repetitive code, enhances maintainability, and provides flexibility to adjust styles at different levels of the document structure. ```csharp{1,11,34} .DefaultTextStyle(style => style.FontSize(20)) .Column(column => { column.Spacing(10); column.Item().Text("Products").ExtraBold().Underline().DecorationThickness(2); column.Item().Text("Comments: " + Placeholders.Sentence()); column.Item() .DefaultTextStyle(style => style.FontSize(14)) .Table(table => { table.ColumnsDefinition(columns => { columns.ConstantColumn(30); columns.RelativeColumn(1); columns.RelativeColumn(2); }); table.Header(header => { header.Cell().Element(Style).Text("ID"); header.Cell().Element(Style).Text("Name"); header.Cell().Element(Style).Text("Description"); IContainer Style(IContainer container) { return container .Background(Colors.Grey.Lighten3) .BorderBottom(1) .PaddingHorizontal(5) .PaddingVertical(10) .DefaultTextStyle(x => x.Bold().FontColor(Colors.Blue.Medium)); } }); foreach (var i in Enumerable.Range(0, 5)) { table.Cell().Element(Style).Text(i.ToString()).Bold(); table.Cell().Element(Style).Text(Placeholders.Label()); table.Cell().Element(Style).Text(Placeholders.Sentence()); } IContainer Style(IContainer container) => container.container.Padding(5); }); }); ``` ![example](/api-reference/text-style-inheritance.webp) --- --- url: /api-reference/text/font-management.md --- # Font management ## Library default font To ensure successful document generation, QuestPDF uses and includes the `Lato` font version 2.015 by default. ::: tip [Lato](https://www.latofonts.com) is a sanserif typeface family designed in the Summer 2010 by Warsaw-based designer Łukasz Dziedzic (“Lato” means “Summer” in Polish). It is available under the [SIL Open Font License, Version 1.1](http://scripts.sil.org/OFL). You can download it from the [Adobe Fonts website](https://fonts.adobe.com/fonts/lato). ::: ## System font registration By default, QuestPDF loads all fonts available in the execution environment. This simplifies the development process, as your code can easily access all system fonts. However, in most cloud deployments, few or no fonts are available, which may lead to unexpected results. To avoid this, you can disable environment font loading using the following setting: ```csharp // true by default QuestPDF.Settings.UseEnvironmentFonts = false; ``` ## Automatic local font registration During application startup, QuestPDF automatically loads all font files present in the deployment directory (as specified by the `CopyToOutputDirectory` property in the `.csproj` file). This allows you to include font files in your project without the need for manual registration. If you prefer to manually specify directories for font discovery, use the following approach: ```csharp QuestPDF.Settings.FontDiscoveryPaths.Clear(); // adjust the path based on your project structure QuestPDF.Settings.FontDiscoveryPaths.Add("resources/fonts"); ``` ## Manual font registration You can manually register custom fonts using the `FontManager` class. Please perform this operation only once, during application startup or initialization. ```csharp using QuestPDF.Drawing; // register font from a file using var fontStream = File.OpenRead("NotoEmoji-Regular.ttf"); FontManager.RegisterFont(fontStream); // register font from an embedded resource // ensure the file is located in the YourApplication project under Resources/Fonts FontManager.RegisterFontFromEmbeddedResource("YourApplication.Resources.Fonts.NotoEmoji-Regular.ttf"); ``` You can also register fonts under custom names to simplify usage within your documents: ```csharp // load the font at startup using var fontStream = File.OpenRead("LibreBarcode39-Regular.ttf"); FontManager.RegisterFontWithCustomName("MyBarcodeFont", fontStream); // use it during document generation container .Text("*QuestPDF*") .FontFamily("MyBarcodeFont") // use your custom font name .FontSize(64); ``` ## Checking if all glyphs are available If your document contains non-Latin characters or special symbols such as emojis, you may want to verify that all required glyphs are available in the selected font. If glyphs are missing in both the primary font and all registered fallback fonts, they will be replaced with placeholder characters. To detect such issues, enable the following setting: ```csharp // enabled by default only when the debugger is attached QuestPDF.Settings.CheckIfAllTextGlyphsAreAvailable = true; ``` When enabled, the library will throw an exception if any glyphs are missing from the selected font. ## Removing the default Lato font QuestPDF includes the Lato font by default to ensure a seamless experience when generating PDFs. However, if you are using your own fonts and want to optimize your package size, you can safely remove Lato from the output. To follow this approach, please add the following snippet to your `.csproj` file: ```xml{3-5,7-9} ``` --- --- url: /api-reference/image/basics.md --- # Image Use this element to embed images into your document. By default, it preserves the image's original aspect ratio, ensuring that your visuals remain undistorted. Supported image format: JPEG, PNG, BMP, WEBP. ## Usage There are several ways to add an image to your document: ```csharp // 1) a binary array byte[] imageData = File.ReadAllBytes("path/to/logo.png") container.Image(imageData) // 2) a fileName container.Image("path/myFile.png") // 3) a stream using var stream = new FileStream("logo.png", FileMode.Open); container.Image(stream); ``` ::: tip Please note that there is a significant difference between image resolution (number of pixels vertically and horizontally) and its physical size described in points. Therefore, the resolution of an image is not used for determining its physical size on the document. ::: #### Example ```csharp{8-11} .Grid(grid => { grid.Columns(2); grid.Spacing(10); grid.Item(2).Text("My photo gallery:").Bold(); grid.Item().Image("photo-gallery-1.jpg"); grid.Item().Image("photo-gallery-2.jpg"); grid.Item().Image("photo-gallery-3.jpg"); grid.Item().Image("photo-gallery-4.jpg"); }); ``` ![example](/api-reference/image-example.webp) ## Image scaling When working with the Image element, controlling how it adjusts to available space is crucial for achieving the desired layout. By default, the image scales to fill the full width of its container while maintaining its aspect ratio. #### Fitting options | Method | Description | |-------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | **FitWidth** | Scales the image to fill the full width of its container. This is the default behavior. | | **FitHeight** | Stretches the image vertically to fit the full available height. Often used with height-constraining elements like `Height`, `MaxHeight`, etc. | | **FitArea** | Combines `FitWidth` and `FitHeight`. Resizes the image to utilize all available space, preserving its aspect ratio. Fills width or height based on container size. An optimal and safe choice. | | **FitUnproportionally** | Adjusts the image to fill all available space, disregarding original proportions. This can lead to distorted scaling and is generally not recommended for most cases. | ::: danger Please be careful. This component may try to enforce size constraints that are impossible to meet. For example, the container may require more space than is available, or may try to squeeze its child into less space than possible. Such scenarios result in a layout exception. ::: #### Example ```csharp{10,21,32,43,54} .Column(column => { column.Item().PaddingBottom(5).Text("FitWidth").Bold(); column.Item() .Width(200) .Height(150) .Border(4) .BorderColor(Colors.Red.Medium) .Image("photo.jpg") .FitWidth(); column.Item().Height(15); column.Item().PaddingBottom(5).Text("FitHeight").Bold(); column.Item() .Width(200) .Height(100) .Border(4) .BorderColor(Colors.Red.Medium) .Image("photo.jpg") .FitHeight(); column.Item().Height(15); column.Item().PaddingBottom(5).Text("FitArea 1").Bold(); column.Item() .Width(200) .Height(100) .Border(4) .BorderColor(Colors.Red.Medium) .Image("photo.jpg") .FitArea(); column.Item().Height(15); column.Item().PaddingBottom(5).Text("FitArea 2").Bold(); column.Item() .Width(200) .Height(150) .Border(4) .BorderColor(Colors.Red.Medium) .Image("photo.jpg") .FitArea(); column.Item().Height(15); column.Item().PaddingBottom(5).Text("FitUnproportionally").Bold(); column.Item() .Width(200) .Height(50) .Border(4) .BorderColor(Colors.Red.Medium) .Image("photo.jpg") .FitUnproportionally(); }); ``` ![example](/api-reference/image-scaling.webp) ## Limiting image size The PDF standard uses points to describe size, where there are 72 points in 1 inch. `Image` uses pixels to describe content. However, pixel does not have any meaningful size. Only when you specify DPI (dots per inch), is it possible to determine a pixel's size. The QuestPDF library always scales an image, because determining physical image size based on its resolution does not make sense. To force an image to take a specified area, you can use any of the constraining elements. The simplest ones are `Width` and `Height`, e.g.: ```csharp container .Width(1, Unit.Inch) .Image(ImageElement.Image) ``` Please note that because the `Image` element uses a proper scaling setting by default, you do not need to use both `Width` and `Height` (the image aspect ratio is preserved). --- --- url: /api-reference/image/optimization.md --- # Optimization Images can be a significant factor in the file size of your PDF documents. QuestPDF provides several options to help you optimize images for the best balance between quality and file size. ## Image compression Image compression is a setting that controls the balance between an image's file size and its visual fidelity during compression. Higher quality values preserve more detail with larger file sizes, while lower values reduce file size at the cost of potential image degradation, such as blurriness or artifacts. ::: tip Opaque images are JPEG-encoded based on this setting, while images with an alpha channel default to PNG format, disregarding this option. ::: #### Available compression quality settings: | Enum Value | JPEG Quality (out of 100) | |--------------------------------------|---------------------------| | ImageCompressionQuality.**Best** | 100 | | ImageCompressionQuality.**VeryHigh** | 90 | | ImageCompressionQuality.**High** | 75 | | ImageCompressionQuality.**Medium** | 50 | | ImageCompressionQuality.**Low** | 25 | | ImageCompressionQuality.**VeryLow** | 10 | #### Example: ```csharp{9,15} .Column(column => { column.Spacing(10); // low quality = smaller output file column .Item() .Image("photo.jpg") .WithCompressionQuality(ImageCompressionQuality.VeryLow); // high quality / fidelity = larger output file column .Item() .Image("photo.jpg") .WithCompressionQuality(ImageCompressionQuality.High); }); ``` ![example](/api-reference/image-compression.webp) ## Image DPI Image DPI (dots-per-inch) is a measure of an image's resolution that indicates how many individual dots (or pixels) fit into one inch of a printed image. Higher DPI values result in greater detail and sharpness, making images appear clearer when printed, while lower DPI values can cause images to look blurry or pixelated. #### Calculation The target resolution is computed by multiplying the DPI with the physical image size on the document. Consider an image of dimensions 3x4 inches. Using a DPI value of 300, the final resolution translates to 900x1200 pixels. If the input image has lower resolution that the one calculated from the DPI setting, it will NOT be rescaled. #### Example ```csharp{9,15} .Column(column => { column.Spacing(10); // lower raster dpi = lower resolution, pixelation column .Item() .Image("photo.jpg") .WithRasterDpi(16); // higher raster dpi = higher resolution column .Item() .Image("photo.jpg") .WithRasterDpi(72); }); ``` ![example](/api-reference/image-dpi.webp) ## Global settings It is possible to globally alter the default image compression quality and raster DPI for all images in the document. ```csharp{9-16} Document .Create(document => { document.Page(page => { page.Content().Image("photo.jpg"); }); }) .WithSettings(new DocumentSettings { // default: ImageCompressionQuality.High; ImageCompressionQuality = ImageCompressionQuality.Medium, // default: 288 ImageRasterDpi = 14 }) .GeneratePdf("image-global-settings.pdf"); ``` ## Using original image When enabled, the library does not resize the image to achieve the target DPI, nor compress it with target image quality. ```csharp{3} container .Image("photo.jpg") .UseOriginalImage(); ``` --- --- url: /api-reference/image/shared.md --- # Shared Images When generating a PDF with multiple items that use the same image, processing the image repeatedly can negatively affect both performance and the final file size. Consider the following scenario: you want to create a list of items, each displaying the same image. In the naive approach, for each item, the following steps occur: 1. Load the image file from the file system. 2. Parse the file into an image object. 3. Scale and compress the image using the specified settings. 4. Embed the processed image as a separate resource in the PDF document. Because these steps are repeated for every list item, the overall process becomes inefficient, and the PDF may end up including multiple copies of the same image. :::info Starting with the 2025.4.0 version, the library automatically detects when static assets (images loaded via local file paths) are used, and enhances performance by caching them to avoid redundant processing. ::: #### Example: inefficient image processing Below is an example where the image is loaded and processed for each item: ```csharp{9} .Column(column => { column.Spacing(15); foreach (var i in Enumerable.Range(0, 5)) { column.Item().Row(row => { row.AutoItem().Width(24).Image("checkbox.png"); row.RelativeItem().PaddingLeft(8).AlignMiddle().Text(Placeholders.Label()).FontSize(16); }); } }); ``` ![example](/api-reference/image-shared.png) #### Solution: shared image resources To avoid redundant processing, load the image once and reuse it across all items. This approach improves performance and reduces the final PDF file size: ```csharp{5,11} .Column(column => { column.Spacing(15); var image = Image.FromFile("checkbox.png"); foreach (var i in Enumerable.Range(0, 5)) { column.Item().Row(row => { row.AutoItem().Width(24).Image(image); row.RelativeItem().PaddingLeft(8).AlignMiddle().Text(Placeholders.Label()).FontSize(16); }); } }); ``` --- --- url: /api-reference/image/svg.md --- # SVG Support QuestPDF supports SVG images, allowing you to integrate scalable vector graphics just as you would with raster images. You can either load and parse an SVG image on demand or preload it to improve performance when the same image is used multiple times. ## Basic Usage There are two ways to add an SVG image to your document: ```csharp{11-12} // 1) with a text containing SVG content var svgContent = File.ReadAllText("pdf-icon.svg"); container.Svg(svgContent); // 2) with a file path container.Svg("pdf-icon.svg") ``` :::tip SVG content supports the same scaling options as raster images. [Learn more](/api-reference/image/basics.html#image-scaling) For example: ```csharp{4} container .Width(200) .Svg("pdf-icon.svg") .FitArea(); ``` ::: ## Example ```csharp{5} container.Column(column => { column.Item().Text("The classic PDF icon looks like this:").Bold(); column.Item().Height(15); column.Item().Svg(svgContent); }); ``` ![example](/api-reference/image-svg.webp) ## Preloading For better performance, especially when reusing the same image, you can preload the SVG image. This ensures that the image is loaded and parsed only once: ```csharp{1-2,14-15} // in global or static context var image = SvgImage.FromFile("pdf-icon.svg"); Document .Create(document => { document.Page(page => { page.Size(PageSizes.A7.Landscape()); page.Margin(25); page.Content() .Padding(25) .Svg(image)) .FitArea(); }); }) .GeneratePdfAndShow(); ``` ## Font Support If your SVG image contains text, please ensure that the font is available in your application: * when the `QuestPDF.Settings.UseEnvironmentFonts` is set to `true`, the font should be installed in the operating system, * when the `QuestPDF.Settings.UseEnvironmentFonts` is set to `false`, the font files should be deployed along with the application. ## Limitations The SVG module displays SVGs as images with high capabilities and compliance. Most SVG files are expected to render correctly, particularly those from popular design tools. However, there are some limitations to be aware of. If an SVG file does not render as expected after considering the following points, please file an issue. Learn more on [the Shopify page](https://shopify.github.io/react-native-skia/docs/images-svg/#svg-support). --- --- url: /api-reference/image/dynamic.md --- # Dynamic Images QuestPDF provides flexible layouts, which means the optimal image resolution cannot always be determined in advance. To ensure the best clarity, especially when generating maps or charts, it's important to produce images at a specific resolution (or a multiple of that resolution for retina displays). This dynamic image element behaves similarly to static images. However, instead of accepting a preloaded image, it expects a function that receives the available space and returns the image as a binary array. ```csharp{15,19} container .Column(column => { column.Spacing(10); column.Item().Text(text => { text.Span("The national flag of Poland").Bold(); text.Span(" consists of two horizontal stripes of equal width, the upper one white and the lower one red."); }); column.Item() .AspectRatio(80 / 50f) .Border(2) .Image(GenerateNationalFlagOfPoland); }); // using SkiaSharp for custom image generation byte[]? GenerateNationalFlagOfPoland(GenerateDynamicImageDelegatePayload context) { using var whitePaint = new SKPaint { Color = SKColors.White, }; using var redPaint = new SKPaint { Color = SKColor.Parse("#BB0A30"), }; using var bitmap = new SKBitmap(context.ImageSize.Width, context.ImageSize.Height); using var canvas = new SKCanvas(bitmap); canvas.DrawRect(0, 0, context.ImageSize.Width, context.ImageSize.Height / 2, whitePaint); canvas.DrawRect(0, context.ImageSize.Height / 2, context.ImageSize.Width, context.ImageSize.Height, redPaint); canvas.Flush(); using var content = bitmap.Encode(SKEncodedImageFormat.Png, 100); return content.ToArray(); } ``` ![example](/api-reference/image-dynamic.webp) --- --- url: /api-reference/line.md --- # Line The Line component allows you to render simple yet customizable vertical and horizontal lines within your layout. These lines can serve as visual dividers, helping to structure and improve the readability of your content. You can specify the thickness of the line and optionally customize its color. ## Vertical Renders a vertical line with a specified thickness. ```csharp{8-9} container .Row(row => { row.AutoItem().Text("Text on the left"); row.AutoItem() .PaddingHorizontal(15) .LineVertical(3) .LineColor(Colors.Blue.Medium); // optional row.AutoItem().Text("Text on the right"); }); ``` ![example](/api-reference/line-vertical.webp) ## Horizontal Renders a horizontal line with a specified thickness. ```csharp{8-9} container .Column(column => { column.Item().Text("Text above the line"); column.Item() .PaddingVertical(10) .LineHorizontal(2) .LineColor(Colors.Blue.Medium); // optional column.Item().Text("Text below the line"); }); ``` ![example](/api-reference/line-horizontal.webp) ## Thickness It is possible to modify how pronounced the line appears by adjusting its thickness. ```csharp{10} container .Column(column => { column.Spacing(20); foreach (var thickness in new[] { 1, 2, 4, 8 }) { column.Item() .Width(200) .LineHorizontal(thickness); } }); ``` ![example](/api-reference/line-thickness.webp) ## Solid Color Specifies the color for the line. ```csharp{18} container .Column(column => { var colors = new[] { Colors.Red.Medium, Colors.Green.Medium, Colors.Blue.Medium, }; column.Spacing(20); foreach (var color in colors) { column.Item() .Width(200) .LineHorizontal(5) .LineColor(color); } }); ``` ![example](/api-reference/line-color-solid.webp) ## Gradient Applies a linear gradient to a line using the specified colors. ```csharp{9,14,19} container .Column(column => { column.Spacing(20); column.Item() .Width(200) .LineHorizontal(5) .LineGradient([Colors.Red.Medium, Colors.Orange.Medium]); column.Item() .Width(200) .LineHorizontal(5) .LineGradient([Colors.Orange.Medium, Colors.Yellow.Medium, Colors.Lime.Medium]); column.Item() .Width(200) .LineHorizontal(5) .LineGradient([Colors.Blue.Lighten2, Colors.LightBlue.Lighten1, Colors.Cyan.Medium, Colors.Teal.Darken1, Colors.Green.Darken2]); }); ``` ![example](/api-reference/line-color-gradient.webp) ## Dash Pattern Configures a dashed pattern for the line. For example, a pattern of `[2, 3]` creates a dash of 2 units followed by a gap of 3 units. ::: warning The length of the pattern array must be even. ::: ```csharp{9,14,19} container .Column(column => { column.Spacing(20); column.Item() .Width(200) .LineHorizontal(5) .LineDashPattern([4f, 4f]); column.Item() .Width(200) .LineHorizontal(5) .LineDashPattern([12f, 12f]); column.Item() .Width(200) .LineHorizontal(5) .LineDashPattern([4f, 4f, 12f, 4f]); }); ``` ![example](/api-reference/line-dash-pattern.webp) ## Complex Example It is possible to combine multiple options to create a more complex line style. ```csharp container .Width(300) .LineHorizontal(8) .LineDashPattern([4, 4, 8, 8, 12, 12]) .LineGradient([Colors.Red.Medium, Colors.Orange.Medium, Colors.Yellow.Medium]); ``` ![example](/api-reference/line-example.webp) --- --- url: /api-reference/placeholder.md --- # Placeholder The Placeholder element is a simple utility for prototyping and layout visualization. It helps structure document layouts by displaying either a provided text label or a default icon when no text is specified. By default, Placeholder fills the designated space with an icon. If a text value is provided, it displays the specified text instead. ### Size You can adjust the size of the Placeholder by chaining layout-modifying elements before its invocation. ```csharp container .Width(200) .Height(100) .Placeholder("Sample text"); ``` ### Example ```csharp{12,16,20} Document .Create(document => { document.Page(page => { page.Size(PageSizes.A5); page.DefaultTextStyle(x => x.FontSize(20)); page.Margin(25); page.Header() .Height(100) .Placeholder("Header"); page.Content() .PaddingVertical(25) .Placeholder(); page.Footer() .Height(100) .Placeholder("Footer"); }); }) .GeneratePdf("placeholder.pdf"); ``` ![example](/api-reference/placeholder-element.webp) --- --- url: /api-reference/charts.md --- # Charts QuestPDF integrates seamlessly with the ScottPlot library to provide powerful charting capabilities in your PDF documents. This integration leverages vector graphics through dynamically generated SVG content, ensuring your charts remain sharp at any scale. ::: info This section provides examples of how to integrate the ScottPlot library with QuestPDF. This library is available under the "MIT" license. We extend our thanks to the authors and maintainers of that project for their contributions to the open-source community. * [ScottPlot official homepage](https://scottplot.net/) * [ScottPlot NuGet page](https://www.nuget.org/packages/ScottPlot) * [ScottPlot GitHub page](https://github.com/ScottPlot/ScottPlot) * [Examples](https://scottplot.net/cookbook/5.0/) ::: ::: warning Please note that the ScottPlot library is not included in the QuestPDF package. You need to install it separately via the NuGet package manager. ::: ## Pie Chart Below is a sample layout showing U.S. energy consumption by source in 2021. ScottPlot is responsible for creating the SVG string, which QuestPDF then embeds in the PDF. ```csharp using ScottPlot; using Colors = QuestPDF.Helpers.Colors; // somewhere in your document's implementation .Column(column => { column.Spacing(10); column.Item().Text("US energy consumption [%]\nby source in 2021").AlignCenter().Bold(); column.Item() .AspectRatio(1) .Svg(size => { ScottPlot.Plot plot = new(); var slices = new PieSlice[] { new() { Value = 8, FillColor = new ScottPlot.Color(Colors.Yellow.Medium.Hex), Label = "Nuclear" }, new() { Value = 12, FillColor = new ScottPlot.Color(Colors.Green.Medium.Hex), Label = "Renewable" }, new() { Value = 32, FillColor = new ScottPlot.Color(Colors.Blue.Medium.Hex), Label = "Natural gas" }, new() { Value = 11, FillColor = new ScottPlot.Color(Colors.Grey.Medium.Hex), Label = "Coal" }, new() { Value = 36, FillColor = new ScottPlot.Color(Colors.Brown.Medium.Hex), Label = "Petroleum" } }; var pie = plot.Add.Pie(slices); pie.DonutFraction = 0.5; pie.SliceLabelDistance = 1.5; pie.LineColor = ScottPlot.Colors.White; pie.LineWidth = 3; foreach (var pieSlice in pie.Slices) { pieSlice.LabelStyle.FontName = "Lato"; pieSlice.LabelStyle.FontSize = 16; } plot.Axes.Frameless(); plot.HideGrid(); return plot.GetSvgXml((int)size.Width, (int)size.Height); }); }); ``` ![example](/api-reference/chart-pie.webp) ## Bar Chart This example creates a bar chart showing the popularity of various C# versions in 2023. [Source](https://www.jetbrains.com/lp/devecosystem-2023/csharp/) ```csharp using ScottPlot; using Colors = QuestPDF.Helpers.Colors; // somewhere in your document's implementation .Column(column => { column.Spacing(10); column.Item().Text("Popularity of C# versions in 2023").AlignCenter().Bold(); column.Item() .AspectRatio(2) .Svg(size => { ScottPlot.Plot plot = new(); var bars = new Bar[] { new() { Position = 1, Value = 2 }, new() { Position = 2, Value = 3 }, new() { Position = 3, Value = 8 }, new() { Position = 4, Value = 13 }, new() { Position = 5, Value = 17 }, new() { Position = 6, Value = 17 }, new() { Position = 7, Value = 32 }, new() { Position = 8, Value = 42 } }; foreach (var bar in bars) { bar.FillColor = new ScottPlot.Color(Colors.Grey.Medium.Hex); bar.LineWidth = 0; bar.Size = 0.5; } plot.Add.Bars(bars); Tick[] ticks = [ new(1, "Other"), new(2, "C# 5"), new(3, "C# 6"), new(4, "C# 7"), new(5, "C# 8"), new(6, "C# 9"), new(7, "C# 10"), new(8, "C# 11") ]; plot.Axes.Bottom.TickGenerator = new ScottPlot.TickGenerators.NumericManual(ticks); plot.Axes.Bottom.MajorTickStyle.Length = 0; plot.Axes.Bottom.TickLabelStyle.FontName = "Lato"; plot.Axes.Bottom.TickLabelStyle.FontSize = 16; plot.Axes.Bottom.TickLabelStyle.OffsetY = 8; plot.Grid.XAxisStyle.IsVisible = false; plot.Axes.Margins(bottom: 0, top: 0.25f); return plot.GetSvgXml((int)size.Width, (int)size.Height); }); }); ``` ![example](/api-reference/chart-bars.webp) --- --- url: /api-reference/barcodes.md --- # Barcodes QuestPDF provides robust support for generating various types of barcodes in your PDF documents through integration with the ZXing.Net library. This implementation leverages vector graphics via SVG, ensuring your barcodes remain sharp and scannable at any resolution. ::: info This section provides examples of how to integrate the ZXing.Net library with QuestPDF. This library is available under the "Apache-2.0" license. We extend our thanks to the authors and maintainers of that project for their contributions to the open-source community. * [ZXing.Net NuGet page](https://www.nuget.org/packages/ZXing.Net) * [ZXing.Net GitHub page](https://github.com/micjahn/ZXing.Net/) * [Original project in Java](https://github.com/zxing/zxing) * [Tutorial about common standards in barcodes](https://github.com/zxing/zxing/wiki/Barcode-Contents) ::: ::: warning Please note that the ZXing.Net library is not included in the QuestPDF package. You need to install it separately via the NuGet package manager. ::: ## Supported formats The ZXing.Net supports the following list of formats: * **1D product**: UPC-A, UPC-E, EAN-8, EAN-13, UPC/EAN Extension 2/5, * **1D industrial**: Code 39, Code 93, Code 128, Codabar, ITF, * **2D**: QR Code, Data Matrix, Aztec, PDF 417, MaxiCode, RSS-14, RSS-Expanded. ## Barcode Linear barcodes (like EAN-8, EAN-13, Code 128, etc.) are perfect for encoding numeric or alphanumeric data in a compact format. Here's how to implement an EAN-8 barcode: ```csharp{34-42} using ZXing; using ZXing.OneD; using ZXing.Rendering; // somewhere in your document's implementation .Background(Colors.Grey.Lighten3) .Padding(25) .Row(row => { var productId = Random.Shared.NextInt64() % 10_000_000; row.Spacing(20); row.RelativeItem().Text(text => { text.ParagraphSpacing(10); text.Span("Product ID: ").Bold(); text.Line(productId.ToString("D7")); text.Span("Name: ").Bold(); text.Line(Placeholders.Label()); text.Span("Description: ").Bold(); text.Span(Placeholders.Sentence()); }); row.AutoItem() .Background(Colors.White) .AlignCenter() .AlignMiddle() .Width(200) .Height(75) .Svg(size => { var content = productId.ToString("D7"); var writer = new EAN8Writer(); var eanCode = writer.encode(content, BarcodeFormat.EAN_8, (int)size.Width, (int)size.Height); var renderer = new SvgRenderer { FontName = "Lato", FontSize = 16 }; return renderer.Render(eanCode, BarcodeFormat.EAN_8, content).Content; }); }); ``` ![example](/api-reference/barcode.webp) ## QR Code QR codes are versatile 2D barcodes that can encode larger amounts of data, including URLs, text, and more. Here's how to create a QR code: ```csharp{28-34} using ZXing; using ZXing.QrCode; using ZXing.Rendering; // somewhere in your document's implementation .Background(Colors.Grey.Lighten3) .Padding(25) .Row(row => { const string url = "https://en.wikipedia.org/wiki/Algorithm"; row.Spacing(20); row.RelativeItem() .AlignMiddle() .Text(text => { text.Justify(); text.Span("In mathematics and computer science, "); text.Span("an algorithm").Bold().BackgroundColor(Colors.White); text.Span(" is a finite sequence of mathematically rigorous instructions, typically used to solve a class of specific problems or to perform a computation. "); text.Hyperlink("Learn more", url).Underline().FontColor(Colors.Blue.Darken2); }); row.ConstantItem(5, Unit.Centimetre) .AspectRatio(1) .Background(Colors.White) .Svg(size => { var writer = new QRCodeWriter(); var qrCode = writer.encode(url, BarcodeFormat.QR_CODE, (int)size.Width, (int)size.Height); var renderer = new SvgRenderer { FontName = "Lato" }; return renderer.Render(qrCode, BarcodeFormat.EAN_13, null).Content; }); }); ``` ![example](/api-reference/qrcode.webp) --- --- url: /api-reference/maps.md --- # Maps QuestPDF provides seamless integration with Mapbox Static Maps API, allowing you to embed high-quality, customizable maps into your PDF documents. This integration offers a reliable and efficient way to include geographical visualizations in your reports, documents, or any PDF output. ::: info This section provides examples of how to integrate the Mapbox service with QuestPDF. This service is paid but provides a generous free tier, including commercial usage. * [Mapbox official homepage](https://www.mapbox.com) * [Mapbox pricing](https://www.mapbox.com/pricing) * [Static images API](https://docs.mapbox.com/api/maps/static-images/) * [Static images API Playground](https://docs.mapbox.com/playground/static/) ::: # Example The code below presents a simple helper class that fetches a map image based on the provided coordinates, zoom level, and dimensions. ::: warning Please generate your own access token via your Mapbox account before deploying. ::: ```csharp static class MapboxStaticMapRenderer { private static readonly HttpClient HttpClient = new(); private const string MapboxBaseUrl = "https://api.mapbox.com/styles/v1/mapbox/streets-v12/static"; private const string AccessToken = ""; public static async Task FetchStaticMapAsync(double longitude, double latitude, float zoom, int width, int height) { var longitudeString = longitude.ToString(System.Globalization.CultureInfo.InvariantCulture); var latitudeString = latitude.ToString(System.Globalization.CultureInfo.InvariantCulture); var url = $"{MapboxBaseUrl}/{longitudeString},{latitudeString},{zoom},0,0/{width}x{height}@2x?access_token={AccessToken}"; try { var response = await HttpClient.GetAsync(url); return await response.Content.ReadAsByteArrayAsync(); } catch (Exception ex) { return null; } } } ``` You can use the helper class implemented above to fetch a map image and embed it in your document. ```csharp{1,18-21} var map = await MapboxStaticMapRenderer.FetchStaticMapAsync(19.9376052f, 50.0616087f, 10, 500, 400); Document .Create(document => { document.Page(page => { page.ContinuousSize(550); page.Margin(25); page.Content() .Column(column => { column.Item().Text("Map of Kraków").FontSize(20).Bold(); column.Item().Text("Capital of Lesser Poland Voivodeship").FontSize(16).Light(); column.Item().Height(15); column.Item() .Background(Colors.Grey.Lighten3) .ShowIf(map != null) .Image(map); }); }); }) .GeneratePdf("map.pdf"); ``` ![example](/api-reference/map.webp) ::: warning Always fetch the map before starting PDF generation. The map retrieval is an asynchronous operation and should not be performed during document generation. ::: --- --- url: /api-reference/complex-graphics.md --- # Complex Graphics QuestPDF supports various built-in drawing capabilities, but there may be times when you need to include more sophisticated or custom graphics in your PDF. One powerful way to achieve this is by embedding SVG content dynamically. This allows you to draw custom shapes, gradients, and other visual elements that go beyond available functionalities. ## Rounded Rectangle This example shows how to draw a custom rectangle behind the text. It fills the available space, has rounded corners, and a gradient fill. ```csharp{3-17} .Layers(layers => { layers.Layer().Svg(size => { return $""" """; }); layers.PrimaryLayer() .PaddingVertical(10) .PaddingHorizontal(20) .Text("QuestPDF") .FontColor(Colors.White) .FontSize(32) .ExtraBlack(); }); ``` ![example](/api-reference/complex-graphics-rounded-rectangle-with-gradient.webp) ## Dotted Line This example creates structure similar to a table of contents with dotted lines connecting the page numbers to the titles. ```csharp{15-22} .Column(column => { column.Spacing(5); foreach (var i in Enumerable.Range(1, 5)) { var pageNumber = i * 7 + 4; column.Item().Row(row => { row.AutoItem().Text($"{i}."); row.ConstantItem(10); row.AutoItem().Text(Placeholders.Label()); row.RelativeItem().PaddingHorizontal(3).OffsetY(20).Height(2).Svg(size => { return $""" """; }); row.AutoItem().Text($"{pageNumber}"); }); } }); ``` ![example](/api-reference/complex-graphics-dotted-line.webp) --- --- url: /api-reference/skiasharp-integration.md --- # SkiaSharp Integration QuestPDF supports multiple ways to integrate dynamic complex graphics into PDF documents. The preferred method is to generate an SVG image, as it ensures scalability and lightweight nature. However, in cases where this approach is not sufficient, you can use the SkiaSharp library to create custom graphics and effects. ::: info This section provides examples of how to integrate the SkiaSharp library with QuestPDF. This library is available under the "MIT" license. We extend our thanks to the authors and maintainers of that project for their contributions to the open-source community. * [SkiaSharp NuGet page](https://www.nuget.org/packages/SkiaSharp) * [SkiaSharp GitHub page](https://github.com/mono/SkiaSharp) ::: ::: warning Please note that the SkiaSharp library is not included in the QuestPDF package. You need to install it separately via the NuGet package manager. ::: ::: tip If you are planning to host your application on a Linux server, please install the following nuget package: [SkiaSharp.NativeAssets.Linux.NoDependencies](https://www.nuget.org/packages/SkiaSharp.NativeAssets.Linux.NoDependencies) ::: ## Helper Script To simplify the integration of SkiaSharp with QuestPDF, you'll need to add a helper class to your project. This class provides two extension methods that bridge QuestPDF's container system with SkiaSharp's canvas-based drawing approach. Copy the following code into your project to enable SkiaSharp integration: ```csharp{10,24} using System.Text; using QuestPDF.Fluent; using QuestPDF.Infrastructure; using SkiaSharp; namespace QuestPDF.SkiaSharpIntegration; public static class SkiaSharpHelpers { public static void SkiaSharpSvgCanvas(this IContainer container, Action drawOnCanvas) { container.Svg(size => { using var stream = new MemoryStream(); using (var canvas = SKSvgCanvas.Create(new SKRect(0, 0, size.Width, size.Height), stream)) drawOnCanvas(canvas, size); var svgData = stream.ToArray(); return Encoding.UTF8.GetString(svgData); }); } public static void SkiaSharpRasterizedCanvas(this IContainer container, Action drawOnCanvas) { container.Image(payload => { using var bitmap = new SKBitmap(payload.ImageSize.Width, payload.ImageSize.Height); using (var canvas = new SKCanvas(bitmap)) { canvas.Scale(payload.ImageSize.Width / payload.AvailableSpace.Width, payload.ImageSize.Height / payload.AvailableSpace.Height); drawOnCanvas(canvas, new ImageSize((int)payload.AvailableSpace.Width, (int)payload.AvailableSpace.Height)); } return bitmap.Encode(SKEncodedImageFormat.Png, 100).ToArray(); }); } } ``` ## SVG **The SkiaSharpSvgCanvas method** allows you to use SkiaSharp's drawing capabilities while maintaining the benefits of vector graphics. This approach is ideal for most custom graphics as it preserves sharp edges and details at any scale and keeps document file sizes smaller compared to rasterized images. The following example demonstrates how to create a vector-based clock graphic using SkiaSharp. ```csharp{11} Document.Create(document => { document.Page(page => { page.Size(350, 350); page.Margin(25); page.Content() .Width(300) .Height(300) .SkiaSharpSvgCanvas((canvas, size) => { var centerX = size.Width / 2; var centerY = size.Height / 2; var radius = Math.Min(centerX, centerY); // draw clock face using var facePaint = new SKPaint { Color = new SKColor(Colors.Blue.Lighten4) }; canvas.DrawCircle(centerX, centerY, radius, facePaint); // draw clock ticks using var tickPaint = new SKPaint { Color = new SKColor(Colors.Blue.Darken4), StrokeWidth = 4, StrokeCap = SKStrokeCap.Round }; canvas.Save(); canvas.Translate(centerX, centerY); foreach (var i in Enumerable.Range(0, 12)) { canvas.DrawLine(new SKPoint(0, radius * 0.85f), new SKPoint(0, radius * 0.95f), tickPaint); canvas.RotateDegrees(30); } canvas.Restore(); // draw clock hands using var hourHandPaint = new SKPaint { Color = new SKColor(Colors.Blue.Darken4), StrokeWidth = 8, StrokeCap = SKStrokeCap.Round }; using var minuteHandPaint = new SKPaint { Color = new SKColor(Colors.Blue.Darken2), StrokeWidth = 4, StrokeCap = SKStrokeCap.Round }; canvas.Translate(centerX, centerY); canvas.Save(); canvas.RotateDegrees(6 * DateTime.Now.Minute); canvas.DrawLine(new SKPoint(0, 0), new SKPoint(0, -radius * 0.7f), minuteHandPaint); canvas.Restore(); canvas.Save(); canvas.RotateDegrees(30 * DateTime.Now.Hour + DateTime.Now.Minute / 2); canvas.DrawLine(new SKPoint(0, 0), new SKPoint(0, -radius * 0.5f), hourHandPaint); canvas.Restore(); }); }); }) .GeneratePdf("clock.pdf"); ``` ![example](/api-reference/skiasharp-integration-svg.webp) ## Rasterization While vector graphics are preferred for most cases, some visual effects and complex operations in SkiaSharp can only be properly rendered through rasterization. **The SkiaSharpRasterizedCanvas method** allows you to use the full range of SkiaSharp's capabilities when you need effects that aren't supported by SVG. The following example demonstrates how to draw an image with rounded corners and apply a drop shadow effect. ```csharp{10} Document.Create(document => { document.Page(page => { page.Size(new PageSize(500, 400)); page.DefaultTextStyle(x => x.FontSize(20)); page.Content() .Padding(25) .SkiaSharpRasterizedCanvas((canvas, size) => { // add padding to properly display the shadow effect const float padding = 25; canvas.Translate(padding, padding); // load image and scale canvas space using var bitmap = SKBitmap.Decode("Resources/landscape.jpg"); var targetBitmapSize = new SKSize(size.Width - 2 * padding, size.Height - 2 * padding); var scale = Math.Min(targetBitmapSize.Width / bitmap.Width, targetBitmapSize.Height / bitmap.Height); canvas.Scale(scale); var drawingArea = new SKRoundRect(new SKRect(0, 0, bitmap.Width, bitmap.Height), 32, 32); // draw drop shadow using var dropShadowFilter = SKImageFilter.CreateDropShadow(8, 8, 16, 16, SKColors.Black); using var paint = new SKPaint { ImageFilter = dropShadowFilter }; canvas.DrawRoundRect(drawingArea, paint); // draw image canvas.ClipRoundRect(drawingArea, antialias: true); canvas.DrawBitmap(bitmap, SKPoint.Empty); }); }); }) .GeneratePdf("rasterized-effect.pdf"); ``` ![example](/api-reference/skiasharp-integration-rasterized.webp) --- --- url: /api-reference/page/basics.md --- # Page This container allows you to define your page layout by configuring margins, watermarks, and different content sections such as header, footer, and the main content area. You can easily create pages of various sizes and orientations while controlling text styles and content direction. Below is a minimal example showing how to create a simple document with a header, main content, and footer. ```csharp Document.Create(document => { document.Page(page => { page.Size(PageSizes.A4); page.Margin(2, Unit.Centimetre); page.DefaultTextStyle(x => x.FontSize(24)); page.Header() .Text("Hello, World!") .FontSize(48).Bold(); page.Content() .PaddingVertical(25) .Text(Placeholders.LoremIpsum()) .Justify(); page.Footer() .AlignCenter() .Text(text => { text.CurrentPageNumber(); text.Span(" / "); text.TotalPages(); }); }); }) ``` ![example](/api-reference/page-simple.webp) --- --- url: /api-reference/page/slots.md --- # Page Slots The Page container is a multi-child container that allows you to define the layout of the page. It provides several slots that can be used to add content to the page. ## Main Slots The main slots are Header, Content, and Footer. | Slot | Description | |--------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | page.**Header()** | Represents the segment at the very top of the page, just above the main content. This container does not support paging capability. It is expected to be fully displayed on every page. | | page.**Content()** | Represents the primary content, located between the header and footer. This container supports paging capability and determines the final length of the document. | | page.**Footer()** | Represents the section at the very bottom of the page, just below the main content. This container does not support paging capability. It is expected to be fully displayed on every page. | ```csharp .Page(page => { document.Page(page => { page.Size(PageSizes.A4); page.Margin(2, Unit.Centimetre); page.DefaultTextStyle(x => x.FontSize(24)); page.Header() .Background(Colors.Grey.Lighten1) .Height(125) .AlignCenter() .AlignMiddle() .Text("Header"); page.Content() .Background(Colors.Grey.Lighten2) .AlignCenter() .AlignMiddle() .Text("Content"); page.Footer() .Background(Colors.Grey.Lighten1) .Height(75) .AlignCenter() .AlignMiddle() .Text("Footer"); }); }); ``` ![example](/api-reference/page-main-slots.webp) ## Foreground Slot Represents a layer drawn in front of the primary layer (header + content + footer), serving as a watermark. It is not affected by the Margin configuration and always occupy the entire page. ```csharp{18} document.Page(page => { page.Size(PageSizes.A4); page.Margin(2, Unit.Centimetre); page.DefaultTextStyle(x => x.FontSize(20)); page.Header() .PaddingBottom(1, Unit.Centimetre) .Text("Report") .FontSize(30) .Bold(); page.Content() .Text(Placeholders.Paragraphs()) .ParagraphSpacing(1, Unit.Centimetre) .Justify(); page.Foreground().Svg("Resources/draft-foreground.svg").FitArea(); }); ``` ![example](/api-reference/page-foreground.webp) ## Background Slot Represents a layer drawn behind the primary layer (header + content + footer). It is not affected by the Margin configuration and always occupy the entire page. ```csharp{5} document.Page(page => { page.Size(PageSizes.A4.Landscape()); page.Background().Svg("Resources/certificate-background.svg").FitArea(); page.Content() .PaddingLeft(10, Unit.Centimetre) .PaddingRight(5 , Unit.Centimetre) .AlignMiddle() .Column(column => { column.Item().Height(50).Svg("Resources/questpdf-logo.svg"); column.Item().Height(50); column.Item().Text("CERTIFICATE").FontSize(64).ExtraBlack(); column.Item().Height(25); column.Item() .Shrink().BorderBottom(1).Padding(10) .Text("Marcin Ziąbek").FontSize(32).Italic(); column.Item().Height(10); column.Item() .Text($"has successfully completed the course \"QuestPDF Basics\" on {DateTime.Now:dd MMM yyyy}.") .FontSize(20).Light(); }); }); ``` ![example](/api-reference/page-background.webp) --- --- url: /api-reference/page/settings.md --- # Page Settings This section describes how to configure the page settings in your document. ## Page Color You can set the background color of your document pages using the PageColor method. Colors can be specified using predefined constants, hexadecimal values, or named color variants: ```csharp document.Page(page => { page.PageColor(Colors.White); // or page.PageColor("#F0F0F0"); // or page.PageColor(Colors.Grey.Lighten3); }); ``` ::: tip Learn more about supported color formats and predefined color palettes in the [Colors](/concepts/colors) section. ::: ## Page Size QuestPDF offers multiple ways to define the dimensions of a page. You can set exact sizes in various units, choose from standard presets, or allow the library to adapt the page size dynamically based on your content. ::: tip Learn more about supported units in the [Lenght unit types](/concepts/length-unit-types) section. ::: ### Specific Page Size Configures the exact dimensions of every page within the set. ```csharp document.Page(page => { page.Size(595, 842); // in points // or page.Size(21, 29.7f, Unit.Centimeter); // or page.Size(PageSizes.A4); }); ``` ### Continuous Page Size Enables the continuous page size mode, allowing the page's height to adjust according to content while retaining a constant specified width. This configuration is useful for output types like receipts, scrolls, or other cases where the length of the page can continuously expand. ```csharp document.Page(page => { page.ContinuousSize(215); // or page.ContinuousSize(76, Unit.Millimeter); }); ``` ### Flexible Page Size Enables the flexible page size mode, where the output page's dimensions can vary based on its content. It is possible to specify the minimum and maximum dimensions for the page, or both. Please note that with this setting, individual pages within the document may have different sizes. ```csharp document.Page(page => { page.MinSize(400, 600); // and / or page.MaxSize(800, 1200); // also supports units and PageSizes }); ``` ### Predefined Page Size For convenience, QuestPDF provides commonly used page size presets, including optional orientation: ```csharp using QuestPDF.Helpers; document.Page(page => { page.Size(PageSizes.A4); page.Size(PageSizes.A3); page.Size(PageSizes.Letter); page.Size(PageSizes.Legal); // it is also possible to specify the orientation page.Size(PageSizes.A4.Portrait()); page.Size(PageSizes.A4.Landscape()); }); ``` ## Margin Margins add empty space around the main layout (header, content, and footer). You can configure each side individually or use combined methods for convenience: | Method | Summary | |----------------------|--------------------------------------------------------------------------| | **MarginLeft** | Adds empty space to the left of the primary layer. | | **MarginRight** | Adds empty space to the right of the primary layer. | | **MarginTop** | Adds empty space above the primary layer. | | **MarginBottom** | Adds empty space below the primary layer. | | **MarginVertical** | Adds empty space vertically (top and bottom) around the primary layer. | | **MarginHorizontal** | Adds empty space horizontally (left and right) around the primary layer. | | **Margin** | Adds empty space around the primary layer. | ```csharp document.Page(page => { page.MarginVertical(32); page.MarginHorizontal(2, Unit.Centimeter); }); ``` ## Default Text Style You can apply a default text style to every text element within a page. This is particularly helpful for setting consistent fonts, sizes, and colors across your document: [Learn more](/api-reference/text/style-inheritance) ```csharp document.Page(page => { page.DefaultTextStyle(TextStyle.Default.FontSize(20)); // or page.DefaultTextStyle(x => x.FontSize(20)); }); ``` ## Content Direction QuestPDF supports both left-to-right (LTR) and right-to-left (RTL) layouts to accommodate languages with different reading directions. This option applies a global content direction to the entire page set. [Learn more](/api-reference/content-direction) ```csharp document.Page(page => { page.ContentFromLeftToRight(); // or page.ContentFromRightToLeft(); }); ``` ## Documents with multiple page settings You can apply different settings to each page set in the document. This flexibility allows you to mix sizes, orientations, styles, or margins as needed: ```csharp Document .Create(document => { document.Page(page => { page.Size(PageSizes.A4); page.Margin(1, Unit.Inch); page.Content().AlignCenter().AlignMiddle().Text("A4 PORTAIT"); }); document.Page(page => { page.Size(PageSizes.A5.Landscape()); page.Margin(2f, Unit.Centimeter); page.Content().AlignCenter().AlignMiddle().Text("A5 LANDSCAPE"); }); }) .GeneratePdf(); ``` --- --- url: /api-reference/table/basics.md --- # Table QuestPDF’s table component provides a robust, flexible way to create complex, dynamic layouts in your PDF documents. Whether you need a basic grid or a detailed multi-page report with headers and footers, tables give you complete control over cell positioning, spanning, styling, and more. ## Introduction The Table component in QuestPDF organizes content into rows and columns. You start by defining a set of columns and then proceed to add content to the cells in each row. Below is a simple example that demonstrates how to create a table with a header row and a few data rows: ```csharp .Table(table => { table.ColumnsDefinition(columns => { columns.ConstantColumn(50); columns.RelativeColumn(); columns.ConstantColumn(125); }); table.Header(header => { header.Cell().BorderBottom(2).Padding(8).Text("#"); header.Cell().BorderBottom(2).Padding(8).Text("Product"); header.Cell().BorderBottom(2).Padding(8).AlignRight().Text("Price"); }); foreach (var i in Enumerable.Range(0, 6)) { var price = Math.Round(Random.Shared.NextDouble() * 100, 2); table.Cell().Padding(8).Text($"{i + 1}"); table.Cell().Padding(8).Text(Placeholders.Label()); table.Cell().Padding(8).AlignRight().Text($"${price}"); } }); ``` ![example](/api-reference/table-simple.webp) ## Columns Definition When building tables, one of the first steps is deciding how the columns should scale. QuestPDF provides two main column types: | Column Type | Description | |----------------------------|----------------------------------------------------------| | table.**RelativeColumn()** | Adjusts its width proportionally to the available space. | | table.**ConstantColumn()** | Has fixed width, defined in points (or other units. | ```csharp{3-8} .Table(table => { table.ColumnsDefinition(columns => { columns.ConstantColumn(150); columns.RelativeColumn(2); columns.RelativeColumn(3); }); table.Cell().ColumnSpan(3) .Background(Colors.Grey.Lighten2).Element(CellStyle) .Text("Total width: 450px"); table.Cell().Element(CellStyle).Text("Constant: 150px"); table.Cell().Element(CellStyle).Text("Relative: 2*"); table.Cell().Element(CellStyle).Text("Relative: 3*"); table.Cell().Element(CellStyle).Text("150px"); table.Cell().Element(CellStyle).Text("120px"); table.Cell().Element(CellStyle).Text("180px"); static IContainer CellStyle(IContainer container) => container.Border(1).Padding(10); }); ``` ![example](/api-reference/table-columns-definition.webp) ## Manual cell placement QuestPDF provides an automatic cell placement mechanism that simplifies the process of creating tables. For more advanced layout scenarios, it is possible to specify the exact position of each cell. This allows you to place cells at specific locations, merge multiple rows or columns, and therefore craft sophisticated table layouts. ```csharp{3-6} table .Cell() .Row(1) // optional .Column(2) // optional .RowSpan(3) // optional .ColumnSpan(4) // optional .Text("Cell content"); ``` Here’s an example showcasing a confusion matrix layout where cells are placed strategically: ```csharp .Table(table => { table.ColumnsDefinition(columns => { columns.ConstantColumn(75); columns.ConstantColumn(150); columns.ConstantColumn(200); columns.ConstantColumn(200); }); table.Cell().Row(1).Column(3).ColumnSpan(2) .Element(HeaderCellStyle) .Text("Predicted condition").Bold(); table.Cell().Row(3).Column(1).RowSpan(2) .Element(HeaderCellStyle).RotateLeft() .Text("Actual\ncondition").Bold().AlignCenter(); table.Cell().Row(2).Column(3) .Element(HeaderCellStyle) .Text("Positive (PP)"); table.Cell().Row(2).Column(4) .Element(HeaderCellStyle) .Text("Negative (PN)"); table.Cell().Row(3).Column(2) .Element(HeaderCellStyle).Text("Positive (P)"); table.Cell().Row(4).Column(2) .Element(HeaderCellStyle) .Text("Negative (N)"); table.Cell() .Row(3).Column(3).Element(GoodCellStyle) .Text("True positive (TP)"); table.Cell() .Row(3).Column(4).Element(BadCellStyle) .Text("False negative (FN)"); table.Cell().Row(4).Column(3) .Element(BadCellStyle).Text("False positive (FP)"); table.Cell().Row(4).Column(4) .Element(GoodCellStyle).Text("True negative (TN)"); static IContainer CellStyle(IContainer container, Color color) => container.Border(1).Background(color).PaddingHorizontal(10).PaddingVertical(15).AlignCenter().AlignMiddle(); static IContainer HeaderCellStyle(IContainer container) => CellStyle(container, Colors.Grey.Lighten4 ); static IContainer GoodCellStyle(IContainer container) => CellStyle(container, Colors.Green.Lighten4).DefaultTextStyle(x => x.FontColor(Colors.Green.Darken2)); static IContainer BadCellStyle(IContainer container) => CellStyle(container, Colors.Red.Lighten4).DefaultTextStyle(x => x.FontColor(Colors.Red.Darken2)); }); ``` ![example](/api-reference/table-manual-cell-placement.webp) --- --- url: /api-reference/table/cell-style-pattern.md --- # Cell Style Pattern This code pattern provides a structured approach to styling individual table cells in a consistent and reusable manner. It allows for defining cell appearance, such as background color, padding, and text styling, ensuring a cohesive visual experience across the table. ```csharp{16-23,39-49} .Table(table => { table.ColumnsDefinition(columns => { columns.RelativeColumn(); columns.ConstantColumn(125); columns.ConstantColumn(125); }); table.Header(header => { header.Cell().Element(CellStyle).Text("Day"); header.Cell().Element(CellStyle).AlignCenter().Text("Weather"); header.Cell().Element(CellStyle).AlignRight().Text("Temp"); static IContainer CellStyle(IContainer container) { return container .Background(Colors.Blue.Darken2) .DefaultTextStyle(x => x.FontColor(Colors.White).Bold()) .PaddingVertical(8) .PaddingHorizontal(16); } }); foreach (var i in Enumerable.Range(0, 7)) { var weatherIndex = Random.Shared.Next(0, weatherIcons.Length); table.Cell().Element(CellStyle) .Text(new DateTime(2025, 2, 26).AddDays(i).ToString("dd MMMM")); table.Cell().Element(CellStyle).AlignCenter().Height(24) .Svg($"Resources/WeatherIcons/{weatherIcons[weatherIndex]}"); table.Cell().Element(CellStyle).AlignRight() .Text($"{Random.Shared.Next(-10, 35)}°"); IContainer CellStyle(IContainer container) { var backgroundColor = i % 2 == 0 ? Colors.Blue.Lighten5 : Colors.Blue.Lighten4; return container .Background(backgroundColor) .PaddingVertical(8) .PaddingHorizontal(16); } } }); ``` ![example](/api-reference/table-cell-style.webp) --- --- url: /api-reference/table/header-and-footer.md --- # Table: Header And Foot When working with larger tables that span multiple pages, it is often helpful to define a dedicated table header and footer. QuestPDF makes this straightforward: headers and footers are repeated on each page so readers can instantly understand the table context, even if content extends beyond one page. Keep in mind that header and footer rows are separate from the table’s main content rows, so they do not count toward your column structure or row indices in the body of the table. ```csharp{43-60} var pageSizes = new List<(string name, double width, double height)>() { ("Letter (ANSI A)", 8.5f, 11), ("Legal", 8.5f, 14), ("Ledger (ANSI B)", 11, 17), ("Tabloid (ANSI B)", 17, 11), ("ANSI C", 22, 17), ("ANSI D", 34, 22), ("ANSI E", 44, 34) }; const int inchesToPoints = 72; container .Padding(10) .MinimalBox() .Border(1) .Table(table => { IContainer DefaultCellStyle(IContainer container, string backgroundColor) { return container .Border(1) .BorderColor(Colors.Grey.Lighten1) .Background(backgroundColor) .PaddingVertical(5) .PaddingHorizontal(10) .AlignCenter() .AlignMiddle(); } table.ColumnsDefinition(columns => { columns.RelativeColumn(); columns.ConstantColumn(80); columns.ConstantColumn(80); columns.ConstantColumn(80); columns.ConstantColumn(80); }); table.Header(header => { // please be sure to call the 'header' handler! header.Cell().RowSpan(2).Element(CellStyle).ExtendHorizontal().AlignLeft().Text("Document type"); header.Cell().ColumnSpan(2).Element(CellStyle).Text("Inches"); header.Cell().ColumnSpan(2).Element(CellStyle).Text("Points"); header.Cell().Element(CellStyle).Text("Width"); header.Cell().Element(CellStyle).Text("Height"); header.Cell().Element(CellStyle).Text("Width"); header.Cell().Element(CellStyle).Text("Height"); // you can extend existing styles by creating additional methods IContainer CellStyle(IContainer container) => DefaultCellStyle(container, Colors.Grey.Lighten3); }); foreach (var page in pageSizes) { table.Cell().Element(CellStyle).ExtendHorizontal().AlignLeft().Text(page.name); // inches table.Cell().Element(CellStyle).Text(page.width); table.Cell().Element(CellStyle).Text(page.height); // points table.Cell().Element(CellStyle).Text(page.width * inchesToPoints); table.Cell().Element(CellStyle).Text(page.height * inchesToPoints); IContainer CellStyle(IContainer container) => DefaultCellStyle(container, Colors.White).ShowOnce(); } }); ``` #### Page 1: ![example](/api-reference/table-header-and-footer-0.webp) #### Page 2: ![example](/api-reference/table-header-and-footer-1.webp) --- --- url: /api-reference/table/overlapping-cells.md --- # Overlapping Cells This example demonstrates how to create complex structures by overlapping multiple cells, spanning them across rows and columns, and applying dynamic styling. ```csharp .Border(1) .BorderColor(Colors.Grey.Lighten1) .Table(table => { table.ColumnsDefinition(columns => { // hour column columns.ConstantColumn(60); // day columns foreach (var i in Enumerable.Range(0, 5)) columns.RelativeColumn(); }); // even/odd columns background foreach (var column in Enumerable.Range(0, 7)) { var backgroundColor = column % 2 == 0 ? Colors.Grey.Lighten3 : Colors.White; table.Cell().Column((uint)column).RowSpan(24).Background(backgroundColor); } // hours and hour lines foreach (var hour in Enumerable.Range(6, 10)) { table.Cell().Column(1).Row((uint)hour) .PaddingVertical(5).PaddingHorizontal(10).AlignRight() .Text($"{hour}"); table.Cell().Row((uint)hour).ColumnSpan(6) .Border(1).BorderColor(Colors.Grey.Lighten1).Height(20); } // dates and day names foreach (var i in Enumerable.Range(0, 5)) { table.Cell() .Column((uint) i + 2).Row(1).Padding(5) .Column(column => { column.Item().AlignCenter().Text($"{17 + i}").FontSize(24).Bold(); column.Item().AlignCenter().Text(dayNames[i]).Light(); }); } // standup events foreach (var i in Enumerable.Range(1, 4)) AddEvent((uint)i, 8, 1, "Standup", Colors.Blue.Lighten4, Colors.Blue.Darken3); // other events AddEvent(2, 11, 2, "Interview", Colors.Red.Lighten4, Colors.Red.Darken3); AddEvent(3, 12, 3, "Demo", Colors.Red.Lighten4, Colors.Red.Darken3); AddEvent(5, 5, 17, "PTO", Colors.Green.Lighten4, Colors.Green.Darken3); void AddEvent(uint day, uint hour, uint length, string name, Color backgroundColor, Color textColor) { table.Cell() .Column(day + 1).Row(hour).RowSpan(length) .Padding(5).Background(backgroundColor).Padding(5) .AlignCenter().AlignMiddle() .Text(name).FontColor(textColor); } }); ``` ![example](/api-reference/table-overlapping-cells.webp) --- --- url: /api-reference/column.md --- # Column The Column element arranges content vertically, stacking items one below another. It supports paging functionality, allowing content to flow naturally across multiple pages when needed. When required, child items are split across pages, ensuring that the content is not cut off. ## Basic usage The Column element uses a lambda function to define its content. Inside the lambda, you can add multiple items using the `Item` method. ```csharp{4-9} container .Width(250) .Padding(25) .Column(column => { column.Item().Background(Colors.Grey.Medium).Height(50); column.Item().Background(Colors.Grey.Lighten1).Height(75); column.Item().Background(Colors.Grey.Lighten2).Height(100); }); ``` ![example](/api-reference/column-simple.webp) ## Spacing You can adjust the vertical spacing between items using the `Spacing` method. ```csharp{6} container .Width(250) .Padding(25) .Column(column => { column.Spacing(25); column.Item().Background(Colors.Grey.Medium).Height(50); column.Item().Background(Colors.Grey.Lighten1).Height(75); column.Item().Background(Colors.Grey.Lighten2).Height(100); }); ``` ![example](/api-reference/column-spacing.webp) Optionally, you can specify the unit value (default is `Unit.Points`). ```csharp column.Spacing(5, Unit.Millimeters); ``` ::: tip Learn more about supported units in the [Lenght unit types](/concepts/length-unit-types) section. ::: ## Custom spacing You can adjust the spacing between items individually by adding an empty item with a specific height. ```csharp .Column(column => { column.Item().Background(Colors.Grey.Darken1).Height(50); column.Item().Height(10); column.Item().Background(Colors.Grey.Medium).Height(50); column.Item().Height(20); column.Item().Background(Colors.Grey.Lighten1).Height(50); column.Item().Height(30); column.Item().Background(Colors.Grey.Lighten2).Height(50); }); ``` ![example](/api-reference/column-spacing-custom.webp) ## Uniform item width By default, all items in a Column match the width of the widest item. This ensures consistent visual alignment, but sometimes it can result in unwanted visual stretching. To disable this behavior, use the `ShrinkHorizontal` API: ```csharp{19} .Column(column => { column.Spacing(15); column.Item() .Element(LabelStyle) .Text("REST API"); column.Item() .Element(LabelStyle) .Text("Garbage Collection"); column.Item() .Element(LabelStyle) .Text("Object-Oriented Programming"); // use helper method to apply the same style to all labels static IContainer LabelStyle(IContainer container) => container .ShrinkHorizontal() .Background(Colors.Grey.Lighten3) .CornerRadius(15) .Padding(15); }); ``` #### Default behavior (consistent item width) ![example](/api-reference/column-uniform-width-enabled.webp) #### Effect with ShrinkVertical applied ![example](/api-reference/column-uniform-width-disabled.webp) --- --- url: /api-reference/row.md --- # Row Draws a collection of elements horizontally. It supports paging functionality, allowing content to flow naturally across multiple pages when needed. When required, child items are split across pages, ensuring that the content is not cut off. ## Item Types For a row element with a width of 100 points that has three items (a relative item of size 1, a relative item of size 5, and a constant item of size 10 points), the items will occupy sizes of 15 points, 75 points, and 10 points respectively. | Method | Description | |------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | **ConstantItem** | Adds a new item to the row element with a specified constant size. | | **RelativeItem** | Adds a new item to the row element. This item occupies space proportionally to other relative items. | | **AutoItem** | Adds a new item to the row element. It requests as much horizontal space as its content requires. **Warning**: It doesn't adjust its size based on other items and may frequently result in layout exceptions. It is recommended to use this API in conjunction with the [MaxWidth](/api-reference/width) element. | For ConstantItem, you can optionally specify the unit value (default is `Unit.Points`). ```csharp row.ConstantItem(5, Unit.Centimetre); ``` ::: tip Learn more about supported units in the [Lenght unit types](/concepts/length-unit-types) section. ::: ## Basic usage The Row element uses a lambda function to define its content. Inside the lambda, you can add multiple items using the `Item` method. ```csharp{4,6,11,16} container .Padding(25) .Width(325) .Row(row => { row.ConstantItem(100) .Background(Colors.Grey.Medium) .Padding(10) .Text("100pt"); row.RelativeItem() .Background(Colors.Grey.Lighten1) .Padding(10) .Text("75pt"); row.RelativeItem(2) .Background(Colors.Grey.Lighten2) .Padding(10) .Text("150pt"); }); ``` ![example](/api-reference/row-simple.webp) ## Spacing You can adjust the horizontal spacing between items using the `Spacing` method. ```csharp{7} container .Padding(25) .Width(220) .Height(50) .Row(row => { row.Spacing(10); row.RelativeItem(2).Background(Colors.Grey.Darken1); row.RelativeItem(3).Background(Colors.Grey.Medium); row.RelativeItem(5).Background(Colors.Grey.Lighten1); }); ``` ![example](/api-reference/row-spacing.webp) Optionally, you can specify the unit value (default is `Unit.Points`). ```csharp row.Spacing(5, Unit.Millimeters); ``` ::: tip Learn more about supported units in the [Lenght unit types](/concepts/length-unit-types) section. ::: ## Custom spacing You can adjust the spacing between items individually by adding an empty ConstantItem with a specific width. ```csharp .Row(row => { row.RelativeItem().Background(Colors.Grey.Darken1); row.ConstantItem(10); row.RelativeItem().Background(Colors.Grey.Medium); row.ConstantItem(20); row.RelativeItem().Background(Colors.Grey.Lighten1); row.ConstantItem(30); row.RelativeItem().Background(Colors.Grey.Lighten2); }); ``` ![example](/api-reference/row-spacing-custom.webp) ## Uniform item height By default, all items in a Row match the height of the tallest item. This ensures consistent visual alignment, but sometimes it can result in unwanted visual stretching. To disable this behavior, use the `ShrinkVertical` API: ```csharp{15} .Row(row => { row.Spacing(15); row.RelativeItem() .Element(LabelStyle) .Text("Programming blends logic and creativity, transforming abstract ideas into working systems. It teaches precision, patience, and the joy of solving problems step by step."); row.RelativeItem() .Element(LabelStyle) .Text("Programming is the craft of turning clear logic into living systems that think and create."); // use helper method to apply the same style to both labels static IContainer LabelStyle(IContainer container) => container .ShrinkVertical() .Background(Colors.Grey.Lighten3) .CornerRadius(15) .Padding(15); }); ``` #### Default behavior (consistent item height) ![example](/api-reference/row-uniform-height-disabled.webp) #### Effect with ShrinkVertical applied ![example](/api-reference/row-uniform-height-enabled.webp) --- --- url: /api-reference/decoration.md --- # Decoration Divides the container's space into three distinct sections: before, content, and after. The before section is rendered above the main `content`, while the `after` section is rendered below it. If the main `content` spans across multiple pages, both the `before` and `after` sections are consistently rendered on every page. ## API | Method | Description | |-------------|-----------------------------------------------------------------------------------------| | **Before** | Returns a container for the section positioned before (above) the primary main content. | | **Content** | Returns a container for the main section. | | **After** | Returns a container for the section positioned after (below) the main content. | ## Example A typical use-case for this method is to render a table that spans multiple pages, with a consistent caption or header on each page. ```csharp{4,7,16} container .Background(Colors.Grey.Lighten3) .Padding(15) .Decoration(decoration => { decoration .Before() .DefaultTextStyle(x => x.Bold()) .Column(column => { column.Item().ShowOnce().Text("Customer Instructions:"); column.Item().SkipOnce().Text("Customer Instructions [continued]:"); }); decoration .Content() .PaddingTop(10) .Text("Please wrap the item in elegant gift paper and include a small blank card for a personal message. If possible, remove any price tags or invoices from the package. Make sure the wrapping is secure but easy to open without damaging the contents."); }); ``` Page 1: ![example](/api-reference/decoration-0.webp) Page 2: ![example](/api-reference/decoration-1.webp) --- --- url: /api-reference/lists.md --- # Ordered and bullet lists Lists are an essential part of structured documents, helping to present information in an organized and easy-to-read format. In QuestPDF, lists can be created using the `Column` and `Row` elements, allowing for both unordered and ordered lists with custom styling and nesting capabilities. ## Unordered list An unordered list is a list of items where the order does not matter. You can create an unordered list in QuestPDF by prepending each item with an image, such as a bullet point icon. ```csharp container.Column(column => { column.Spacing(10); foreach (var i in Enumerable.Range(1, 7)) { column.Item().Row(row => { row.ConstantItem(26).Image("Resources/bulletpoint.png"); row.ConstantItem(5); row.RelativeItem().Text(Placeholders.Label()); }); } }); ``` ![example](/api-reference/list-unordered.webp) ## Ordered list An ordered list is a list of items that follow a sequential order, typically numbered. In QuestPDF, you can create an ordered list by prepending each item with a number. ```csharp container.Column(column => { column.Spacing(10); foreach (var i in Enumerable.Range(1, 11)) { column.Item().Row(row => { row.ConstantItem(35).Text($"{i}."); row.RelativeItem().Text(Placeholders.Sentence()); }); } }); ``` ![example](/api-reference/list-ordered.webp) ## Nested lists Nested lists allow structuring items hierarchically, creating sub-items under main list entries. In QuestPDF, nested lists can be implemented by adjusting item indentation levels or using recursive functions for dynamic list generation. ```csharp container.Column(column => { const float nestingSize = 25; column.Spacing(10); column.Item() .Text("Algorithm: Checking if a Number is Prime") .FontSize(24).FontColor(Colors.Blue.Darken2); AddListItem(0, "1.", "Handle special cases"); AddListItem(1, "a)", "If n is less than 2, return false (not prime)."); AddListItem(1, "b)", "If n is 2, return true (prime)."); AddListItem(0, "2.", "Check divisibility"); AddListItem(1, "-", "Iterate through numbers from 2 to n - 1:"); AddListItem(2, "-", "If n is divisible by any of these numbers, return false."); AddListItem(0, "3.", "Return true (if no divisors were found, n is prime)."); void AddListItem(int nestingLevel, string bulletText, string text) { column.Item().Row(row => { row.ConstantItem(nestingSize * nestingLevel); row.ConstantItem(nestingSize).Text(bulletText); row.RelativeItem().Text(text); }); } }); ``` ![example](/api-reference/list-nested.webp) --- --- url: /api-reference/layers.md --- # Layers The Layers element adds content either underneath (as a background) or on top of (as a watermark) the main content. The main layer supports paging, can span multiple pages, and determines the container's target length. Additional layers can also span multiple pages and are repeated on each one. | Method | Description | |------------------|--------------------------------------------------| | **PrimaryLayer** | Sets the primary content for the container. | | **Layer** | Specifies an additional layer for the container. | ::: warning Exactly one `PrimaryLayer` must be defined. ::: ::: tip The order of code execution determines the drawing order: * If the layer is defined before the primary layer, it's drawn underneath the primary content (as a background). * If defined after the primary layer, it's drawn in front of the primary content (as a watermark). ::: ### Example A common use-case for this element is to add background content behind the main content. ```csharp{7-22} .Column(column => { column.Item().PaddingBottom(15).Text("Proposed Business Card Design:").Bold(); column.Item() .AspectRatio(4 / 3f) .Layers(layers => { layers.Layer().Image("Resources/card-background.jpg").FitUnproportionally(); layers.PrimaryLayer() .OffsetY(75) .Column(innerColumn => { innerColumn.Item() .AlignCenter() .Text("Horizon Ventures") .Bold().FontSize(32).FontColor(Colors.Blue.Darken2); innerColumn.Item().AlignCenter().Text("Your journey begins here"); }); }); }); ``` ![example](/api-reference/layers.webp) --- --- url: /api-reference/inlined.md --- # Inlined The Inlined component arranges elements sequentially in a line, automatically wrapping to the next line when needed. This layout is particularly useful when you need to display a collection of elements horizontally with consistent spacing and alignment options. #### Helper method The following helper method generates sample blocks with random sizes and colors to demonstrate the Inlined component's capabilities: ```csharp void RandomBlock(IContainer container) { container .Width(Random.Shared.Next(1, 4) * 25) .Height(Random.Shared.Next(1, 4) * 25) .Border(1) .BorderColor(Colors.Grey.Darken2) .Background(Placeholders.BackgroundColor()); } ``` #### Usage ```csharp{5-13} .Background(Colors.Grey.Lighten3) .Padding(25) .Border(1) .Background(Colors.White) .Inlined(inlined => { inlined.Spacing(25); inlined.BaselineMiddle(); inlined.AlignCenter(); foreach (var _ in Enumerable.Range(0, 15)) inlined.Item().Element(RandomBlock); }); ``` ![example](/api-reference/inlined.webp) ## Spacing | Option | Description | |-----------------------|------------------------------------------------------| | **Spacing** | Sets the vertical and horizontal gaps between items. | | **VerticalSpacing** | Sets the vertical gaps between items. | | **HorizontalSpacing** | Sets the horizontal gaps between items. | ## Horizontal alignment | Option | Description | |----------------------|-------------------------------------------------------------------------------------------| | **AlignLeft** | Aligns items horizontally to the left side. | | **AlignCenter** | Aligns items horizontally to the center. | | **AlignRight** | Aligns items horizontally to the left right. | | **AlignJustify** | Distributes items horizontally, ensuring even spacing from edge to edge of the container. | | **AlignSpaceAround** | Spaces items equally in a horizontal arrangement, both between items and at the ends. | ## Baseline alignment | Option | Description | |--------------------|---------------------------------------------------------------------------------| | **BaselineTop** | Positions items vertically such that their top edges align on a single line. | | **BaselineMiddle** | Positions items to have their centers in a straight horizontal liąne. | | **BaselineBottom** | Positions items vertically such that their bottom edges align on a single line. | --- --- url: /api-reference/multi-column.md --- # Multi Column Layout A multi-column layout arranges content into vertical columns, similar to newspaper or magazine formatting. This approach optimizes horizontal space and enhances readability, especially for wide containers or screens. ::: warning Multi-column layouts require significant computational resources, which may impact performance. ::: ## Example **The Content() method** provides access to the container where your primary content will be distributed across multiple columns. This container serves as the main content area for your multi-column layout and supports all available layout elements. **The Columns() method** defines the number of vertical columns in your layout. This setting establishes the basic structure of the grid layout. **The Spacing() method** configures the horizontal space between adjacent columns. This setting affects the visual presentation of your column arrangement. Positive values increase separation between columns, while negative values may cause overlap (though this is rarely desirable). ```csharp container.MultiColumn(multiColumn => { multiColumn.Columns(3); multiColumn.Spacing(25); multiColumn .Content() .Column(column => { column.Spacing(15); foreach (var sectionId in Enumerable.Range(0, 3)) { foreach (var textId in Enumerable.Range(0, 3)) column.Item().Text(Placeholders.Paragraph()).Justify(); column.Item().AspectRatio(21 / 9f).Image(Placeholders.Image); } }); }); ``` ![example](/api-reference/multicolumn-example.webp) ## Spacer Use the Spacer approach to create a visual break between content sections, improving readability and aesthetics. The container's dimensions are determined by the height of the columns and the configured spacing. It supports all available layout elements. ```csharp{6-10} container.MultiColumn(multiColumn => { multiColumn.Columns(2); multiColumn.Spacing(50); multiColumn .Spacer() .AlignCenter() .LineVertical(2) .LineColor(Colors.Grey.Medium); multiColumn .Content() .Column(column => { column.Spacing(15); foreach (var textId in Enumerable.Range(0, 5)) column.Item().Text(Placeholders.Paragraph()).Justify(); }); }); ``` ![example](/api-reference/multicolumn-spacer.webp) ## Balance height The BalanceHeight() method controls how content is distributed across columns. This feature helps create a more aesthetically pleasing and professional layout by ensuring columns have similar heights. ```csharp{4} container.MultiColumn(multiColumn => { multiColumn.Spacing(30); multiColumn.BalanceHeight(); multiColumn .Content() .Column(column => { column.Spacing(15); foreach (var textId in Enumerable.Range(0, 8)) column.Item().Text(Placeholders.Paragraph()).Justify(); }); }); ``` #### BalanceHeight disabled The layout occupies the entire vertical space, often leaving the last column shorter or empty if there is less content to fill it. ![example](/api-reference/multicolumn-balance-height-without.webp) #### BalanceHeight enabled The layout engine distributes elements so that each column ends up with approximately the same height. ![example](/api-reference/multicolumn-balance-height-with.webp) --- --- url: /api-reference/width.md --- # Width Use this element to control the horizontal size of its content. | Method | Description | |--------------|----------------------------------------| | **Width** | Sets the exact width of its content. | | **MinWidth** | Sets the minimum width of its content. | | **MaxWidth** | Sets the maximum width of its content. | ## Example The following example shows how text content adjusts to the specified width constraints. ```csharp{9,14} container .Width(300) .Padding(25) .Column(column => { column.Spacing(25); column.Item() .MinWidth(200) .Background(Colors.Grey.Lighten3) .Text("Lorem ipsum"); column.Item() .MaxWidth(100) .Background(Colors.Grey.Lighten3) .Text("dolor sit amet"); }); ``` ![example](/api-reference/width.webp) ::: danger Please be careful. This component may try to enforce size constraints that are impossible to meet. For example, the container may require more space than is available, or may try to squeeze its child into less space than possible. Such scenarios result in a layout exception. ::: --- --- url: /api-reference/height.md --- # Height Use this element to control the veertical size of its content. | Method | Description | |---------------|-----------------------------------------| | **Height** | Sets the exact height of its content. | | **MinHeight** | Sets the minimum height of its content. | | **MaxHeight** | Sets the maximum height of its content. | ## Example The following example demonstrates a container with a fixed height of 100 pt and a width of 200 pt. ```csharp{4} container .Width(300) .Padding(25) .Height(100) .AspectRatio(2f, AspectRatioOption.FitHeight) .Background(Colors.Grey.Lighten1); ``` ![example](/api-reference/height.webp) ::: danger Please be careful. This component may try to enforce size constraints that are impossible to meet. For example, the container may require more space than is available, or may try to squeeze its child into less space than possible. Such scenarios result in a layout exception. ::: --- --- url: /api-reference/alignment.md --- # Alignment The Alignment element controls the positioning of its child content within the available space. It offers both horizontal and vertical options that can be used independently or combined. ## API ### Horizontal | Method | Description | |-----------------|-----------------------------------------------------------------------------------------------| | **AlignLeft** | Aligns content horizontally to the left side. | | **AlignCenter** | Aligns content horizontally to the center, ensuring equal space on both left and right sides. | | **AlignRight** | Aligns its content horizontally to the right side. | ### Vertical | Method | Description | |-----------------|--------------------------------------------------------------------------------| | **AlignTop** | Aligns content vertically to the upper side. | | **AlignMiddle** | Aligns content vertically to the center, ensuring equal space above and below. | | **AlignBottom** | Aligns content vertically to the bottom side. | ## Example ```csharp{4-5} container .Width(300) .Height(300) .AlignBottom() .AlignCenter() .Background(Colors.Grey.Lighten2) .Padding(10) .Text("Test"); ``` ![example](/api-reference/alignment.webp) --- --- url: /api-reference/padding.md --- # Padding For positive values, the Padding element adds empty space around its content. ```csharp{3-5} container .Width(250) .PaddingVertical(10) .PaddingLeft(20) .PaddingRight(40) .Background(Colors.Grey.Lighten2) .Text("Sample text"); ``` ![example](/api-reference/padding-simple.webp) ## Negative padding For negative values, it pushes content beyond the edges, increasing available space (similar to negative HTML margins). ```csharp{3,5} container .Width(250) .Padding(50) .Background(Colors.Grey.Lighten2) .PaddingHorizontal(-25) .Text("Sample text with negative padding"); ``` ![example](/api-reference/padding-negative.webp) ## API | Method | Description | |-----------------------|--------------------------------------------------------------------| | **Padding** | Adds empty space around its content. | | **PaddingHorizontal** | Adds empty space horizontally (left and right) around its content. | | **PaddingVertical** | Adds empty space vertically (top and bottom) around its content. | | **PaddingTop** | Adds empty space above its content. | | **PaddingBottom** | Adds empty space below its content. | | **PaddingLeft** | Adds empty space to the left of its content. | | **PaddingRight** | Adds empty space to the right of its content. | --- --- url: /api-reference/aspect-ratio.md --- # Aspect Ratio Constrains its content to maintain a given width-to-height ratio. ## API Specify the aspect-ratio value either as a number or a division of two numbers: ```csharp .AspectRatio(0.5) // use a ratio .AspectRatio(1f / 2f) // or division ``` Additionally, you can specify how the content should be adjusted to meet the aspect ratio: ```csharp .AspectRatio(0.5, AspectRatioOption.FitArea) ``` ### Fitting Options | Method | Description | |---------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | AspectRatioOption.**FitWidth** | Adjusts content to occupy the full width available. Used as the **default** setting in the library. | | AspectRatioOption.**FitHeight** | Adjusts content to fill the available height. Often used with height-constraining elements. | | AspectRatioOption.**FitArea** | Adjusts content to fill the available area while maintaining its aspect ratio. This may result in the content fully occupying either the width or height, depending on its dimensions. Often used with constraining elements | ::: danger Please be careful. This component may try to enforce size constraints that are impossible to meet. For example, the container may require more space than is available, or may try to squeeze its child into less space than possible. Such scenarios result in a layout exception. ::: ## Example ```csharp{4} container .Width(300) .Height(300) .AspectRatio(3f/4f, AspectRatioOption.FitArea) .Background(Colors.Grey.Lighten2) .AlignCenter() .AlignMiddle() .Text("3:4 Content Area"); ``` ![example](/api-reference/aspect-ratio.webp) --- --- url: /api-reference/rotate.md --- # Rotate ## Constrained Constrained rotation enables you to rotate an element by exactly 90 degrees, either clockwise or counterclockwise, while maintaining the content within the same space and size constraints. | Method | Description | |-----------------|--------------------------------------------------| | **RotateLeft** | Rotates its content 90 degrees counterclockwise. | | **RotateRight** | Rotates its content 90 degrees clockwise. | ::: warning When applying rotation, be aware that it changes the dimensional behavior of your elements. What was previously considered width may become height and vice versa. This affects how other properties like alignment and padding work on the rotated element. ::: ```csharp{4} container.Row(row => { row.AutoItem() .RotateLeft() .AlignCenter() .Text("Definition") .Bold().FontColor(Colors.Blue.Darken2); row.AutoItem() .PaddingHorizontal(15) .LineVertical(2).LineColor(Colors.Blue.Medium); row.RelativeItem() .Background(Colors.Blue.Lighten5) .Padding(15) .Text(text => { text.Span("A variable").Bold(); text.Span(" is a named storage location in memory that holds a value which can be modified during program execution."); }); }); ``` ![example](/api-reference/rotate.webp) ## Free Rotates its content clockwise by a given angle. ```csharp{24} container .Background(Colors.Grey.Lighten2) .Padding(25) .Row(row => { row.Spacing(25); AddIcon(0); AddIcon(30); AddIcon(45); AddIcon(80); void AddIcon(float angle) { const float itemSize = 100; row.AutoItem() .Width(itemSize) .AspectRatio(1) .OffsetX(itemSize / 2) .OffsetY(itemSize / 2) .Rotate(angle) .OffsetX(-itemSize / 2) .OffsetY(-itemSize / 2) .Svg("Resources/compass.svg"); } }); ``` ![example](/api-reference/rotate-free.webp) --- --- url: /api-reference/scale.md --- # Scale | Method | Description | |---------------------|-------------------------------------------------------------------------------------------------------------------------------------------------| | **Scale** | Scales its inner content proportionally. | | **ScaleHorizontal** | Scales the available horizontal space (along the X axis), causing content to appear expanded or squished, rather than simply larger or smaller. | | **ScaleVertical** | Scales the available vertical space (along the Y axis), causing content to appear expanded or squished, rather than simply larger or smaller. | ### Scaling Factor Values greater than one enlarge the content, while values less than one reduce it. ```csharp{12} container .Scale(1.5f) // enlarged content by 50% ``` ### Interaction with Other Elements Although this adjustment modifies the space available to its inner content, some elements might use their own strategies to fill that space. For example, an Image with the setting may retain its size, but its quality could vary based on the DPI setting. In contrast, text will not only appear smaller or bigger; but also a different number of words may fit each line. ## Example Please note that all content inside the container is scaled proportionally: including text, images, padding, etc. ```csharp{12} container .Width(300) .Column(column => { var scales = new[] { 0.75f, 1f, 1.25f, 1.5f }; foreach (var scale in scales) { column .Item() .Border(1) .Scale(scale) .Padding(10) .Text($"Content scale: {scale}") .FontSize(20); } }); ``` ![example](/api-reference/scale.webp) --- --- url: /api-reference/scale-to-fit.md --- # Scale to fit This container dynamically adjusts its content to fit within the available space by proportionally scaling it down if necessary. By attempting to shrink its child elements, it prevents common layout issues such as infinite layout exceptions. It is particularly useful when your content generally fits within the available space but occasionally needs slight adjustments to maintain a consistent appearance. :::warning This container determines the optimal scale value through multiple iterations. For complex content, this may introduce a significant performance overhead. ::: ```csharp{12-15} container.Column(column => { const string text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat."; foreach (var i in Enumerable.Range(4, 5)) { column .Item() .Shrink() .Border(1) .Padding(15) .Width(i * 50) // sizes from 200x100 to 450x175 .Height(i * 25) .ScaleToFit() .Text(text); } }); ``` ![example](/api-reference/scale-to-fit.webp) ::: danger This component scales the available space, not the content directly. As a result, you may still encounter situations where content doesn't fit properly, especially when a child element enforces a specific aspect ratio or has other fixed dimensional constraints. ::: --- --- url: /api-reference/offset.md --- # Offset This container allows you to precisely position content by moving it horizontally and vertically relative to its original position, independent of layout constraints. When you apply offset, the element maintains its original size constraints while shifting its visual position. | Method | Description | |-------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------| | **OffsetX** | Moves content along the horizontal axis. A positive value moves content to the right; a negative value moves it to the left. Does not alter the available space. | | **OffsetY** | Moves content along the vertical axis. A positive value moves content downwards; a negative value moves it upwards. Does not alter the available space. | ```csharp{4-5} container .Padding(50) .Background(Colors.Blue.Lighten3) .OffsetX(25) .OffsetY(25) .Border(4) .BorderColor(Colors.Blue.Darken2) .Padding(50) .Text("Moved content") .FontSize(25); ``` ![example](/api-reference/offset.webp) --- --- url: /api-reference/flip.md --- # Flip | Method | Description | |--------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------| | **FlipHorizontal** | Flips its content to create a mirror image along the Y axis, swapping elements from left to right. Elements on the left will appear on the right. | | **FlipVertical** | Flips its content to create a mirror image along the X axis, moving elements from the top to the bottom. Elements at the top will be positioned at the bottom. | | **FlipOver** | Creates a mirror image of its content across both axes. Elements originally in the top-left corner will be positioned in the bottom-right corner. | ## Example ```csharp{12} container.Column(column => { column.Spacing(15); column.Item() .Text("Read the message below by putting a mirror on the right side of the screen."); column.Item() .AlignLeft() .Background(Colors.Red.Lighten5) .Padding(10) .FlipHorizontal() .Text("This is a secret message.") .FontColor(Colors.Red.Darken2); }); ``` ![example](/api-reference/flip.webp) --- --- url: /api-reference/unconstrained.md --- # Unconstrained The Unconstrained container creates a space where content can go beyond the limits set by parent elements. This helps when you need elements that overlap or extend past their container. When you use Unconstrained, the container doesn't take up space in the layout. It removes any size limits from parent elements, making content appear to "float" compared to other elements. You can pair this with translation to place elements exactly where you want them. ```csharp{12} container .Width(400) .Height(350) .Padding(25) .PaddingLeft(50) .Column(column => { column.Item().Width(300).Height(150).Background(Colors.Blue.Lighten3); column .Item() .Unconstrained() .OffsetX(-50) .OffsetY(-50) .Width(100) .Height(100) .Background(Colors.Blue.Darken2); column.Item().Width(300).Height(150).Background(Colors.Blue.Lighten2); }); ``` ![example](/api-reference/unconstrained.webp) --- --- url: /api-reference/extend.md --- # Extend | Method | Description | |----------------------|------------------------------------------------------------------------------------------| | **Extend** | Forces its content to occupy entire available space, maximizing both width and height. | | **ExtendVertical** | Forces its content to occupy entire available vertical space, maximizing height usage. | | **ExtendHorizontal** | Expands its content to occupy entire available horizontal space, maximizing width usage. | --- --- url: /api-reference/shrink.md --- # Shrink Renders its content in the most compact size achievable. Ideal for situations where the parent element provides more space than necessary. | Method | Description | |----------------------|----------------------------------------------------------------------| | **Shrink** | Shrinks both vertically and horizontally. | | **ShrinkVertical** | Minimizes content height to the minimum, optimizing vertical space. | | **ShrinkHorizontal** | Minimizes content width to the minimum, optimizing horizontal space. | --- --- url: /api-reference/zindex.md --- # Z-Index By default, the library draws content in the order it is defined, which may not always be the desired behavior. This element allows you to alter the rendering order, ensuring that the content is displayed in the correct sequence. The default z-index is 0, unless a different value is inherited from a parent container. Higher values are rendered above lower values. ## Example The following example shows how to use the `ZIndex` element to create visually appealing pricing tables. ```csharp{11} container .PaddingVertical(15) .Border(2) .Row(row => { row.RelativeItem() .Background(Colors.Grey.Lighten3) .Element(c => AddPricingItem(c, "Community", "Free")); row.RelativeItem() .ZIndex(1) // -1 or 0 or 1 .Padding(-15) .Border(1) .Background(Colors.Grey.Lighten1) .PaddingTop(15) .Element(c => AddPricingItem(c, "Professional", "$699")); row.RelativeItem() .Background(Colors.Grey.Lighten3) .Element(c => AddPricingItem(c, "Enterprise", "$1999")); void AddPricingItem(IContainer container, string name, string formattedPrice) { container .Padding(25) .Column(column => { column.Item().AlignCenter().Text(name).FontSize(24).Black(); column.Item().AlignCenter().Text(formattedPrice).FontSize(20).SemiBold(); column.Item().PaddingHorizontal(-25).PaddingVertical(10).LineHorizontal(1); foreach (var i in Enumerable.Range(1, 4)) { column.Item() .PaddingTop(10) .AlignCenter() .Text(Placeholders.Label()) .FontSize(16) .Light(); } }); } }); ``` #### `Without` Z-Index Element (Default Behavior) ![example](/api-reference/zindex-zero.webp) #### With Z-Index `1` Element (Correct Implementation) ![example](/api-reference/zindex-positive.webp) #### With Z-Index `-1` Element (Incorrect Implementation) ![example](/api-reference/zindex-negative.webp) --- --- url: /api-reference/page-break.md --- # Page break The Page Break feature allows you to control the layout of your document by forcing content to start on a new page. This is useful for separating sections, improving readability, and ensuring that specific elements appear on dedicated pages. In the example below, we generate a programming dictionary where each term appears on its own page. ```csharp{28} Document .Create(document => { document.Page(page => { page.Size(300, 450); page.DefaultTextStyle(x => x.FontSize(20)); page.Margin(25); page.Content() .PaddingTop(15) .Column(column => { var terms = new[] { ("Garbage Collection", "An automatic memory management feature in many programming languages that identifies and removes unused objects to free up memory, preventing memory leaks."), ("Constructor", "A special method in object-oriented programming that is automatically called when an object is created. It initializes the object's properties and sets up any necessary resources."), ("Dependency", "A software component or external library that a program relies on to function correctly. Dependencies can include third-party modules, frameworks, or system-level packages that provide additional functionality without requiring developers to write everything from scratch.") }; column.Item() .Extend() .AlignCenter().AlignMiddle() .Text("Programming dictionary").FontSize(24).Bold(); foreach (var term in terms) { column.Item().PageBreak(); column.Item().Element(c => GeneratePage(c, term.Item1, term.Item2)); } static void GeneratePage(IContainer container, string term, string definition) { container.Text(text => { text.Span(term).Bold().FontColor(Colors.Blue.Darken2); text.Span($" - {definition}"); }); } }); }); }) .GeneratePdf("page-break.pdf"); ``` --- --- url: /api-reference/prevent-page-break.md --- # Prevent page break Attempts to keep the container's content together on its first page of occurrence. If the content does not fit entirely on that page, it is moved to the next page. If it spans multiple pages, all subsequent pages are rendered as usual without restriction. ## Example This method is useful for ensuring that content remains visually coherent and is not arbitrarily split. ```csharp{7} container.Column(column => { column.Item().Height(400).Background(Colors.Grey.Lighten3); column.Item().Height(30); column.Item() .PreventPageBreak() .Text(text => { text.ParagraphSpacing(15); text.Span("Optimizing Content Placement").Bold().FontColor(Colors.Blue.Darken2).FontSize(24); text.Span("\n"); text.Span("By carefully determining where to place a page break, you can avoid awkward text separations and maintain readability. Thoughtful formatting improves the overall user experience, making complex topics easier to digest."); }); }); ``` #### Without PreventPageBreak #### With PreventPageBreak --- --- url: /api-reference/ensure-space.md --- # Ensure space Ensures that the container's content occupies at least a specified minimum height on its first page of occurrence. * If there is enough space, the content is rendered as usual. * However, if a page break is required, this method ensures that a minimum amount of space is available before rendering the content. If the required space is not available, the content is moved to the next page. * This rule applies only to the first page where the content appears. If the content spans multiple pages, all subsequent pages are rendered without this restriction. ## Example This method is particularly useful for structured elements like tables, where rendering only a small fragment at the bottom of a page could negatively impact readability. By ensuring a minimum height, you can prevent undesired content fragmentation. ```csharp{7} container.Column(column => { column.Item().Height(400).Background(Colors.Grey.Lighten3); column.Item().Height(30); column.Item() .EnsureSpace(100) .Table(table => { table.ColumnsDefinition(columns => { columns.ConstantColumn(40); columns.RelativeColumn(); }); foreach (var i in Enumerable.Range(1, 12)) { table.Cell().Text($"{i}."); table.Cell().ShowEntire().Text(Placeholders.Sentence()); } }); }); ``` #### Without EnsureSpace #### With EnsureSpace --- --- url: /api-reference/show-entire.md --- # Show entire The ShowEntire element is designed to ensure that specific content remains on a single page, preventing it from being split across multiple pages. While many elements within the library naturally support paging, allowing content to flow seamlessly across pages, ShowEntire enforces strict page constraints to maintain visual cohesiveness. This can be particularly useful when presenting structured data, tables, or definitions that should remain uninterrupted for clarity and readability. ::: warning The ShowEntire element imposes strict space constraints, which may lead to a DocumentLayoutException if the content exceeds the page's capacity. Ensure that the enclosed content fits within a single page to avoid errors. ::: ::: tip Please consider using a less-strict alternative, [EnsureSpace](/api-reference/ensure-space), if you want to maintain visual consistency without enforcing a hard page constraint. ::: ### Example The following example demonstrates how to use the ShowEntire element to create a glossary where each term and its definition remain together on the same page: ```csharp{21} container .Decoration(decoration => { var terms = new[] { ("Function", "A reusable block of code designed to perform a specific task. Functions take input parameters, process them, and return results, making code modular, readable, and maintainable. They are an essential component of all programming languages."), ("Recursion", "A programming technique where a function calls itself in order to solve a problem by breaking it down into smaller, similar subproblems. Recursion is often used for complex algorithms, such as searching, sorting, and tree traversal."), ("Framework", "A pre-built collection of code, tools, and best practices that provides a structured foundation for developing software. Frameworks simplify development by handling common functionalities, such as database access, user authentication, and UI rendering."), ("Package", "A self-contained collection of code, typically consisting of functions, classes, and modules, that provides specific functionality. Packages help organize large projects and allow developers to reuse and distribute their code easily."), }; decoration.Before().Text("Terms and their definitions:").FontSize(24).Bold().Underline(); decoration.Content().PaddingTop(15).Column(column => { column.Spacing(15); foreach (var term in terms) { column.Item() .ShowEntire() .Text(text => { text.Span(term.Item1).Bold().FontColor(Colors.Blue.Darken2); text.Span($" - {term.Item2}"); }); } }); }); ``` ### Without the ShowEntire element ![example](/api-reference/show-entire-without-0.webp) ![example](/api-reference/show-entire-without-1.webp) ### With the ShowEntire element ![example](/api-reference/show-entire-with-0.webp) ![example](/api-reference/show-entire-with-1.webp) ``` ``` --- --- url: /api-reference/repeat.md --- # Repeat When designing a document, you may need certain elements—such as headers, footers, labels, or key terms—to be visible on every page where applicable. The Repeat element is designed to fulfill this requirement by rendering the specified content multiple times across different pages, rather than just once. ### Example Please note that the term "Variable" is repeated across multiple pages. ```csharp{24} container .Decoration(decoration => { var terms = new[] { ("Algorithm", "A precise set of instructions that defines a process for solving a specific problem or performing a computation. Algorithms are the foundation of programming and are used to optimize tasks efficiently."), ("Bug", "An error, flaw, or unintended behavior in a program that causes it to produce incorrect or unexpected results. Debugging is the process of identifying, analyzing, and fixing these issues to improve software reliability."), ("Variable", "A named storage location in memory that holds a value, which can be modified during program execution. Variables make code dynamic and flexible by allowing data manipulation and retrieval."), ("Compilation", "The process of transforming human-readable source code into machine code (binary instructions) that a computer can execute. This process is performed by a compiler and often includes syntax checks, optimizations, and linking dependencies.") }; decoration.Before().Text("Terms and their definitions:").Bold(); decoration.Content().PaddingTop(15).Column(column => { foreach (var term in terms) { column.Item().Row(row => { row.RelativeItem(2) .Border(1) .Background(Colors.Grey.Lighten3) .Padding(15) .Repeat() .Text(term.Item1); row.RelativeItem(3) .Border(1) .Padding(15) .Text(term.Item2); }); } }); }); ``` ### Without the Repeat element ![example](/api-reference/repeat-without-0.webp) ![example](/api-reference/repeat-without-1.webp) ### With the Repeat element ![example](/api-reference/repeat-with-0.webp) ![example](/api-reference/repeat-with-1.webp) --- --- url: /api-reference/show-if.md --- # Show if The ShowIf element provides a simple way to conditionally display or hide content without breaking the fluent API chain. This is particularly useful when you need to show or hide sections of your document based on runtime conditions. ```csharp{8-11,19} var condition = numberOfElements > 5; // c# if-statement approach .Row(row => { row.RelativeItem().Text("One"); var secondColumn = row.RelativeItem(); if (condition) secondColumn.Text("Two"); }); // equivalent fluent approach .Row(row => { row.RelativeItem().Text("One"); row.RelativeItem().ShowIf(condition).Text("Two"); }); ``` --- --- url: /api-reference/show-once.md --- # Show once The ShowOnce element provides fine-grained control over content rendering across multiple pages. By default, all elements are fully rendered once and do not repeat. However, in specific contexts such as headers, footers, or decorative elements positioned before and after content slots, elements may be repeated on every page. To prevent this automatic repetition, use the ShowOnce element. ::: tip Combine this element with SkipOnce to achieve more complex behaviors, e.g.: * `container.SkipOnce().ShowOnce()` ensures the child element is displayed only on the second page. * `container.SkipOnce().SkipOnce()` starts displaying the child element from the third page onwards. * `container.ShowOnce().SkipOnce()` draws nothing, as the order of invocation is important. ::: ### Example The following example demonstrates how to use ShowOnce to create a professional invoice header that shows different content on the first page compared to subsequent pages: ```csharp{7,22} container .Decoration(decoration => { decoration.Before().Column(column => { column.Item() .ShowOnce() .Row(row => { row.ConstantItem(80).AspectRatio(4 / 3f).Placeholder(); row.ConstantItem(10); row.RelativeItem() .AlignMiddle() .Column(innerColumn => { innerColumn.Item().Text("Invoice #1234").FontSize(24).Bold(); innerColumn.Item().Text($"Generated on {DateTime.Now:d}").FontSize(16).Light(); }); }); column.Item() .SkipOnce() .Text("Invoice #1234").FontSize(24).Bold(); }); // generate dummy content decoration.Content() .PaddingTop(15) .ExtendHorizontal() .Column(column => { column.Spacing(10); foreach (var i in Enumerable.Range(1, 15)) { column.Item() .Height(30) .Background(Colors.Grey.Lighten3) .AlignCenter() .AlignMiddle() .Text($"{i}"); } }); }); ``` ![example](/api-reference/show-once-0.webp) ![example](/api-reference/show-once-1.webp) --- --- url: /api-reference/skip-once.md --- # Skip once If the container spans multiple pages, its content is omitted on the first page and then displayed on the second and subsequent pages. A common use-case for this element is when displaying a consistent header across pages but needing to conditionally show/hide specific fragments on the first page. ::: tip Combine this element with SkipOnce to achieve more complex behaviors, e.g.: * `container.SkipOnce().ShowOnce()` ensures the child element is displayed only on the second page. * `container.SkipOnce().SkipOnce()` starts displaying the child element from the third page onwards. * `container.ShowOnce().SkipOnce()` draws nothing, as the order of invocation is important. ::: ### Example In this example, the SkipOnce and ShowOnce elements are combined to ensure that if a glossary term spans multiple pages, the header displays "Continued" on the second and subsequent pages. ```csharp{20-29} container .Column(column => { var terms = new[] { ("Repository", "A centralized storage location for source code and related files, typically managed using version control systems like Git. Repositories allow multiple developers to collaborate on projects, track changes, and maintain version history."), ("Version Control", "A system that tracks changes to code over time, enabling developers to collaborate efficiently, revert to previous versions, and maintain a structured development workflow. Popular version control tools include Git, Mercurial, and Subversion."), ("Abstraction", "A programming concept that hides complex implementation details and exposes only the necessary parts. Abstraction helps simplify code and allows developers to focus on high-level design rather than low-level implementation details."), ("Namespace", "A container that groups related identifiers, such as variables, functions, and classes, to prevent naming conflicts in a program. Namespaces are commonly used in large projects to organize code efficiently."), }; column.Spacing(15); foreach (var term in terms) { column.Item().Decoration(decoration => { decoration.Before() .DefaultTextStyle(x => x.FontSize(24).Bold().FontColor(Colors.Blue.Darken2)) .Column(innerColumn => { innerColumn.Item().ShowOnce().Text(term.Item1); innerColumn.Item().SkipOnce().Text(text => { text.Span(term.Item1); text.Span(" (continued)").Light().Italic(); }); }); decoration.Content().Text(term.Item2); }); } }); ``` ![example](/api-reference/skip-once-0.webp) ![example](/api-reference/skip-once-1.webp) --- --- url: /api-reference/stop-paging.md --- # Stop paging Renders the element exclusively on the first page. Any portion of the element that doesn't fit is omitted. ::: tip If your goal is to limit the content to a specific area, please consider using other approaches: [Text Clamp Lines](/api-reference/text/paragraph-style#clamp-line-with-ellipsis) and [Scale to Fit](/api-reference/scale-to-fit). ::: ## Example ```csharp{6} const string bookDescription = "\"Master Modern C# Development\" is a comprehensive guide that takes you from the basics to advanced concepts in C# programming. Perfect for beginners and intermediate developers looking to enhance their skills with practical examples and real-world applications. Covering object-oriented programming, LINQ, asynchronous programming, and the latest .NET features, this book provides step-by-step explanations to help you write clean, efficient, and scalable code. Whether you're building desktop, web, or cloud applications, this resource equips you with the knowledge and best practices to become a confident C# developer."; container .Width(400) .Height(300) .StopPaging() .Decoration(decoration => { decoration.Before().Text("Book description:").Bold(); decoration.Content().Text(bookDescription); }); ``` ### Without StopPaging ### With StopPaging --- --- url: /api-reference/section.md --- # Section A **Section** defines a named fragment of a document that can span multiple pages. It is useful for creating table of contents and document navigation. A **SectionLink** creates a clickable area that allows users to navigate to a designated section. This enhances document usability by enabling quick access to relevant content. It is also possible to display page numbers for each section, which is particularly useful when generating tables of contents or cross-referencing sections. ### Example ```csharp{32,38,48} Document .Create(document => { document.Page(page => { page.Size(PageSizes.A5.Landscape()); page.DefaultTextStyle(x => x.FontSize(20)); page.Margin(25); page.Content() .Column(column => { var terms = new[] { ("Bit", "The smallest unit of data in computing, representing either a 0 or a 1. Multiple bits are combined to form bytes, which are used to store larger data values."), ("Byte", "A unit of digital information that consists of 8 bits. A byte is commonly used to store a single character of text, such as a letter or a number, in computer memory."), ("Binary", "A number system that uses only two digits, 0 and 1, which are the fundamental building blocks of computer operations. Computers process and store all data in binary format, including text, images, and instructions."), ("Array", "A data structure that stores a fixed-size sequence of elements, all of the same type, in a contiguous block of memory. Arrays allow quick access to elements using an index and are commonly used to manage collections of data.") }; // title column.Item().Extend().AlignMiddle().AlignCenter().Text("Programming Glossary").FontSize(32).Bold(); column.Item().PageBreak(); // table of contents column.Item().PaddingBottom(25).Text("Table of Contents").FontSize(24).Bold().Underline(); foreach (var term in terms) { column.Item() .PaddingBottom(10) .SectionLink($"term-{term}") .Text(text => { text.Span("Term "); text.Span(term.Item1).Bold(); text.Span(" on page "); text.BeginPageNumberOfSection($"term-{term}"); }); } // content foreach (var term in terms) { column.Item().PageBreak(); column.Item() .Section($"term-{term}") .Text(text => { text.Span(term.Item1).Bold().FontColor(Colors.Blue.Darken2); text.Span(" - "); text.Span(term.Item2); }); } }); }); }) .GeneratePdf("sections.pdf"); ``` --- --- url: /api-reference/hyperlink.md --- # Hyperlink The Hyperlink element creates a clickable area that redirects the user to a designated webpage. ### Content Hyperlink can span any content, including text, images, or other elements. ```csharp{10} .Column(column => { column.Spacing(25); column.Item() .Text("Clicking the NuGet logo will redirect you to the NuGet website."); column.Item() .Width(150) .Hyperlink("https://www.nuget.org/") .Svg("Resources/nuget-logo.svg"); }); ``` ### Inside text Hyperlinks can also be placed inside text elements. ```csharp{5} container .Text(text => { text.Span("Click "); text.Hyperlink("here", "https://www.nuget.org/").Underline().FontColor(Colors.Blue.Darken2); text.Span(" to visit the official NuGet website."); }); ``` --- --- url: /api-reference/lazy.md --- # Lazy When generating large PDF documents with thousands of pages, memory consumption becomes a critical concern. QuestPDF provides specialized elements to optimize memory usage by deferring content creation until it is actually needed. This reduces the lifetime of objects, allowing for more efficient garbage collection and lowering the risk of out-of-memory errors. ## Available Approaches There are two primary approaches to optimize memory usage when generating large documents: * **The Lazy element** defers the construction of document elements until they are required for rendering. This means that instead of preloading and storing all content in memory at once, only the necessary elements are created dynamically when needed. The Lazy element achieves this by providing a delegate function which is executed later during the document generation process. * **The LazyWithCache element** introduces an additional performance benefit: previously rendered sections are cached, reducing the recomputation overhead when revisiting pages. However, this may lead to higher native memory usage due to caching mechanisms. ## Example This example uses a simple component generating a list of numbers from a specified range. It simulates a typical text-heavy content generation scenario. ```csharp{8-19} class SimpleComponent : IComponent { public required int Start { get; init; } public required int End { get; init; } public void Compose(IContainer container) { container.Decoration(decoration => { decoration.Before() .Text($"Numbers from {Start} to {End}") .FontSize(20).Bold().FontColor(Colors.Blue.Darken2); decoration.Content().Column(column => { foreach (var i in Enumerable.Range(Start, End - Start + 1)) column.Item().Text($"Number {i}").FontSize(10); }); }); } } ``` ### Normal Approach This approach does not use any optimization techniques and generates the entire document at once. It is typically used for small documents or when memory usage is not a concern. ```csharp{10-19} Document .Create(document => { document.Page(page => { page.Margin(10); page.Content().Column(column => { const int sectionSize = 1000; foreach (var i in Enumerable.Range(0, 1000)) { column.Item().Component(new SimpleComponent { Start = i * sectionSize, End = i * sectionSize + sectionSize - 1 }); } }); }); }) .GeneratePdf("lazy-disabled.pdf"); ``` ### Lazy Approach This approach uses the Lazy element to defer the creation of content until it is needed. ```csharp{17-24} Document .Create(document => { document.Page(page => { page.Margin(10); page.Content().Column(column => { const int sectionSize = 1000; foreach (var i in Enumerable.Range(0, 1000)) { var start = i * sectionSize; var end = start + sectionSize - 1; column.Item().Lazy(c => { c.Component(new SimpleComponent { Start = start, End = end }); }); } }); }); }) .GeneratePdf("lazy-enabled.pdf"); ``` ### LazyWithCache Approach This approach uses the LazyWithCache element to defer the creation of content and cache previously rendered sections. ```csharp{17-24} Document .Create(document => { document.Page(page => { page.Margin(10); page.Content().Column(column => { const int sectionSize = 1000; foreach (var i in Enumerable.Range(0, 1000)) { var start = i * sectionSize; var end = start + sectionSize - 1; column.Item().LazyWithCache(c => { c.Component(new SimpleComponent { Start = start, End = end }); }); } }); }); }) .GeneratePdf("lazy-enabled-with-cache.pdf"); ``` ### Observed results Please analyze the following results to understand the performance benefits of each approach: | Approach | Time | Memory | |-------------------|------|--------| | **Normal** | 24s | 950 MB | | **Lazy** | 32s | 120 MB | | **LazyWithCache** | 18s | 480 MB | Understanding when and how to use these elements is key to improving both document generation speed and resource management. --- --- url: /api-reference/default-text-style.md --- # Default text style Applies a default text style to all nested Text elements. Please note that this element extends and overrides existing styles with additional configuration. ## API Depending on your use-case, you can provide a TextStyle object or use a lambda expression: ```csharp .DefaultTextStyle(x => x.Bold().Underline()) .DefaultTextStyle(TextStyle.Default.Bold().Underline()) ``` ## Example ```csharp{4,16} container .Width(400) .Padding(25) .DefaultTextStyle(x => x.Bold().Underline()) .Column(column => { column.Spacing(10); column.Item().Text("Inherited bold and underline"); column.Item() .Text("Disabled underline, inherited bold and adjusted font color") .Underline(false).FontColor(Colors.Green.Darken2); column.Item() .DefaultTextStyle(x => x.DecorationWavy().FontColor(Colors.LightBlue.Darken3)) .Text("Changed underline type and adjusted font color"); }); ``` Please note that this element extends existing styles with additional configuration. Those styles can be extended/overridden in later stages of the code. ![example](/api-reference/default-text-style.webp) --- --- url: /api-reference/content-direction.md --- # Content Direction The ContentDirection element controls the flow direction of content in your document, supporting both left-to-right (LTR) and right-to-left (RTL) layouts. This is essential for proper text alignment and content organization when working with different languages. ```csharp container .ContentFromRightToLeft() // content in right-to-left direction ``` ## API | Method | Description | |----------------------------|---------------------------------------------------------------------------------------------------| | **ContentFromLeftToRight** | Sets the left-to-right (LTR) direction for its entire content. This is a **default** setting. | | **ContentFromRightToLeft** | Sets the right-to-left (RTL) direction for its entire content. | ## Overriding content direction It is also possible to override the content direction for specific elements: ```csharp{1,10} .ContentFromRightToLeft() .Column(column => { column .Item() // content with inherited RTL content direction column .Item() .ContentFromLeftToRight() // content with overridden LTR content direction }); ``` ## Impact On Content This element impacts several key aspects: * Text alignment and positioning * Text direction and word wrapping * Element ordering in collections (Row, Table etc.) * Default content alignment * Content flow direction ```csharp .ContentFromRightToLeft() // LTR or RTL mode .Row(row => { row.Spacing(5); row.AutoItem().Height(50).Width(50).Background(Colors.Red.Lighten1); row.AutoItem().Height(50).Width(50).Background(Colors.Green.Lighten1); row.AutoItem().Height(50).Width(75).Background(Colors.Blue.Lighten1); }); ``` | LTR | RTL | |----------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------| | Items are typically aligned to the left. For most containers, the first item is positioned on the left, while the last item is on the right. | Items are typically aligned to the right. For most containers, the first item is positioned on the right, while the last item is on the left. | | ![example](/api-reference/content-direction-ltr.webp) | ![example](/api-reference/content-direction-rtl.webp) | --- --- url: /api-reference/debug-area.md --- # Debug area The DebugArea element helps you visually debug document layouts by drawing a labeled box around its content. This aids in understanding spacing, alignment and pinpointing specific sections of the document during development. ::: tip For enhanced development and debugging experience, please consider using [the QuestPDF Companion App](/companion/usage). ::: ## API You can specify text and color to better distinguish between various debug elements: ```csharp{2} container .Debug("Grid example", Colors.Blue.Medium) // content ``` It is also possible to skip the color (it is red by default), and even the label: ```csharp .Debug("Grid example") .Debug() ``` ::: tip Learn more about supported color formats and predefined color palettes in the [Colors](/concepts/colors) section. ::: ## Example ```csharp{5} container .Width(250) .Height(250) .Padding(25) .DebugArea("Grid example", Colors.Blue.Medium) .Grid(grid => { grid.Columns(3); grid.Spacing(5); foreach (var _ in Enumerable.Range(0, 8)) grid.Item().Height(50).Placeholder(); }); ``` ![example](/api-reference/debug-area.webp) --- --- url: /api-reference/debug-pointer.md --- # Debug pointer Inserts a virtual debug element visible in the document hierarchy tree in the QuestPDF Companion App, as well as in the enhanced debugging message provided by the DocumentLayoutException. It does not appear in the final PDF output. ::: tip For enhanced development and debugging experience, please consider using [the QuestPDF Companion App](/companion/usage). ::: ::: tip Learn more about the [DocumentLayoutException](/concepts/common-exceptions#documentlayoutexception). ::: ## Example ```csharp{2-4} container .Width(100) .DebugPointer("Product details section") .Width(150) .Column(column => { column.Item().Text("Coffee Beans"); column.Item().Text("$19.99"); }); ``` The code above throws an exception with the following element trace: ```csharp{20} The provided document content contains conflicting size constraints. For example, some elements may require more space than is available. The layout issue is likely present in the following part of the document: -> Document -> Page -> Page -> Content -> Content -> In method: content Called from: Render Source path: /Users/marcinziabek/RiderProjects/QuestPDF/Source/QuestPDF.Examples/Engine/RenderingTest.cs Line number: 100 -> Product details section To learn more, please analyse the document measurement of the problematic location: 🚨 Constrained 🚨 ================== Available Space: (Width: 100,000, Height: 340,000) Space Plan: Wrap Wrap Reason: The available horizontal space is less than the minimum width. ------------------ Content Direction: LeftToRight Min Width: 150 Max Width: 150 Min Height: - Max Height: - Enforce Size When Empty: False 🟡 Column ========== Available Space: (Width: 0,000, Height: 0,000) Space Plan: PartialRender (Width: 0,000, Height: 0,000) ---------- 🟡 TextBlock ============= Available Space: (Width: 0,000, Height: 0,000) Space Plan: PartialRender (Width: 0,000, Height: 0,000) ------------- Alignment: Start Content Direction: LeftToRight Line Clamp: - Line Clamp Ellipsis: - Paragraph Spacing: 0 Paragraph First Line Indentation: 0 Text: Coffee Beans ⚪️ TextBlock ============= Alignment: Start Content Direction: LeftToRight Line Clamp: - Line Clamp Ellipsis: - Paragraph Spacing: 0 Paragraph First Line Indentation: 0 Text: $19.99 Legend: 🚨 - Element that is likely the root cause of the layout issue based on library heuristics and prediction. 🔴 - Element that cannot be drawn due to the provided layout constraints. This element likely causes the layout issue, or one of its descendant children is responsible for the problem. 🟡 - Element that can be partially drawn on the page and will also be rendered on the consecutive page. In more complex layouts, this element may also cause issues or contain a child that is the actual root cause. 🟢 - Element that is successfully and completely drawn on the page. ⚪️ - Element that has not been drawn on the faulty page. Its children are omitted. ``` --- --- url: /examples/custom-header-on-first-page.md --- # Customizing Header/Footer on the first page A common requirement in document design is to create a distinct header for the first page, with all subsequent pages sharing a standard header format. This can be easily accomplished using the [ShowOnce](/api-reference/show-once) and [SkipOnce](/api-reference/skip-once) elements in QuestPDF. ```csharp{10-15} Document .Create(document => { document.Page(page => { page.Size(PageSizes.A5); page.Margin(30); page.DefaultTextStyle(x => x.FontSize(20)); page.Header().Column(column => { column.Item().ShowOnce().Background(Colors.Blue.Lighten2).Height(80); column.Item().SkipOnce().Background(Colors.Green.Lighten2).Height(60); }); page.Content().PaddingVertical(20).Column(column => { column.Spacing(20); foreach (var _ in Enumerable.Range(0, 20)) column.Item().Background(Colors.Grey.Lighten3).Height(40); }); page.Footer().AlignCenter().Text(text => { text.CurrentPageNumber(); text.Span(" / "); text.TotalPages(); }); }); }) .GeneratePdf("custom-header-on-first-page.pdf"); ``` The code above produces the following results: --- --- url: /examples/aspnet-integration.md --- # Integration with ASP.NET ## License configuration Configure your license in either the Startup.cs or Program.cs file depending on your project configuration. This code should be executed only once, when the application starts or during its initialization step. ```csharp // please kindly ensure what license is appropriate for your project QuestPDF.Settings.License = LicenseType.Community; ``` ::: tip Learn more about the licensing and related configuration [here](https://www.questpdf.com/license/configuration.html). ::: ## Generating PDF files in controller endpoints This section demonstrates how to generate and return a PDF file in an ASP.NET controller endpoint using QuestPDF. The example below creates a simple PDF document and sends it as a response when the endpoint is accessed. ```csharp{5-14} [ApiController] [Route("[controller]")] public class WeatherForecastController : ControllerBase { [HttpGet(Name = "GeneratePdf")] public IResult GeneratePdf() { // use any method to create a document, e.g.: injected service var document = CreateDocument(); // generate PDF file and return it as a response var pdf = document.GeneratePdf(); return Results.File(pdf, "application/pdf", "hello-world.pdf"); } QuestPDF.Infrastructure.IDocument CreateDocument() { return Document.Create(container => { container.Page(page => { page.Size(PageSizes.A4); page.Margin(2, Unit.Centimetre); page.PageColor(Colors.White); page.DefaultTextStyle(x => x.FontSize(20)); page.Header() .Text("Hello PDF!") .SemiBold().FontSize(36).FontColor(Colors.Blue.Medium); page.Content() .PaddingVertical(1, Unit.Centimetre) .Column(x => { x.Spacing(20); x.Item().Text(Placeholders.LoremIpsum()); x.Item().Image(Placeholders.Image(200, 100)); }); page.Footer() .AlignCenter() .Text(x => { x.Span("Page "); x.CurrentPageNumber(); }); }); }); } } ``` --- --- url: /examples/zugferd.md --- # Creating ZUGFeRD-Compliant PDF Documents ## Introduction ZUGFeRD (Zentraler User Guide des Forums elektronische Rechnung Deutschland) is a German standard for electronic invoicing that combines PDF documents with embedded XML data. It allows for both human-readable PDF invoices and machine-readable structured data in a single file, enabling automated processing while maintaining traditional PDF workflow compatibility. A ZUGFeRD-compliant PDF document must meet the following requirements: * Be PDF/A-3b compliant * Include the invoice data as an XML attachment * Contain specific XMP metadata ::: warning ZUGFeRD comes in different versions with varying requirements. This documentation covers ZUGFeRD 2.1, which is based on the UN/CEFACT Cross Industry Invoice (CII) standard. When implementing ZUGFeRD support, ensure you're using the correct version for your needs and that all components (XML schema, metadata, and PDF/A version) align with that version. ::: ::: tip QUALITY ASSURANCE The validation process can vary across different tools, often producing different results. At QuestPDF, as part of our CI/CD pipeline, we use automated document validation with [veraPDF](https://verapdf.org/) (to verify PDF/A-3b compliance) and the [Mustang Project](https://www.mustangproject.org/) (to verify ZUGFeRD compliance). Both tools are open-source and free to use. In addition, we periodically perform manual validation using the Adobe Acrobat Pro Preflight tool to further ensure compliance. ::: ## Document Creation Here's a complete example showing how to create a ZUGFeRD-compliant PDF document: ```csharp Document .Create(document => { document.Page(page => { page.Content().Text("Your invoice content"); }); }) .WithMetadata(new DocumentMetadata { Title = "Conformance Test: ZUGFeRD", Author = "SampleCompany", Subject = "ZUGFeRD Test Document", Language = "en-US" }) .WithSettings(new DocumentSettings { PdfA = true }) // PDF/A-3b .GeneratePdf("invoice-bbb.pdf"); DocumentOperation .LoadFile("invoice.pdf") .AddAttachment(new DocumentOperation.DocumentAttachment { Key = "factur-zugferd", FilePath = "resource-factur-x.xml", AttachmentName = "factur-x.xml", MimeType = "text/xml", Description = "Factur-X Invoice", Relationship = DocumentOperation.DocumentAttachmentRelationship.Source, CreationDate = DateTime.UtcNow, ModificationDate = DateTime.UtcNow }) .ExtendMetadata(File.ReadAllText("resource-zugferd-metadata.xml")) .Save("zugferd-invoice.pdf"); ``` ::: tip Find the full example here: [ZUGFeRD Example](https://github.com/QuestPDF/QuestPDF/tree/main/Source/QuestPDF.ZUGFeRD) ::: --- --- url: /license/community.md --- --- --- url: /license.md --- --- --- url: /license/guide.md --- --- --- url: /license/purchase-success.md --- --- --- url: /api-reference/page.md --- # Page This container consists of multiple page-related slots. ## Main slots Main slots (`Header`, `Content` and `Footer`) can be used to specify page content: * The `Header` element is always visible at the top of each page. * The `Content` element is drawn on the space between the `Header` and the `Footer`. * The `Footer` element is always visible at the bottom of each page. ```csharp .Page(page => { page.MarginHorizontal(40); page.MarginVertical(60); page.Header() .Height(60) .Background(Colors.Grey.Lighten1) .AlignCenter() .AlignMiddle() .Text("Header"); page.Content() .Background(Colors.Grey.Lighten2) .AlignCenter() .AlignMiddle() .Text("Content"); page.Footer() .Height(30) .Background(Colors.Grey.Lighten1) .AlignCenter() .AlignMiddle() .Text("Footer"); }); ``` ::: danger Please be careful! When the combined heights of the header and footer elements is greater than the total page height, there is insufficient space for the content, in which case a layout exception is thrown. ::: ![example](/api-reference/page-example.png) ## Watermark slots The watermark slots (background and foreground) can be used to add content behind or in front of the main content, respectively. ```csharp{10-14,16-20} .Page(page => { page.Size(PageSizes.A4); page.Margin(1, Unit.Inch); page.DefaultTextStyle(TextStyle.Default.FontSize(16)); page.PageColor(Colors.White); const string transparentBlue = "#662196f3"; page.Background() .AlignTop() .ExtendHorizontal() .Height(200) .Background(transparentBlue); page.Foreground() .AlignBottom() .ExtendHorizontal() .Height(250) .Background(transparentBlue); page.Header() .Text("Background and foreground") .Bold().FontColor(Colors.Blue.Darken2).FontSize(36); page.Content().PaddingVertical(25).Column(column => { column.Spacing(25); foreach (var i in Enumerable.Range(0, 100)) column.Item().Background(Colors.Grey.Lighten2).Height(75); }); }); ``` ![example](/api-reference/page-background-foreground.png) Let's consider a more advanced example that adds additional visual elements on the side of actual content. This can be easily achieved with watermark slots: ```csharp{10-30} document.Page(page => { const float horizontalMargin = 1.5f; const float verticalMargin = 1f; page.Size(PageSizes.A4); page.MarginVertical(verticalMargin, Unit.Inch); page.MarginHorizontal(horizontalMargin, Unit.Inch); page.Background() .PaddingVertical(verticalMargin, Unit.Inch) .RotateRight() .Decoration(decoration => { decoration.Before().RotateRight().RotateRight().Element(DrawSide); decoration.Content().Extend(); decoration.After().Element(DrawSide); void DrawSide(IContainer container) { container .Height(horizontalMargin, Unit.Inch) .AlignMiddle() .Row(row => { row.AutoItem().PaddingRight(16).Text("COMPANY NAME").FontSize(16).FontColor(Colors.Red.Medium); row.RelativeItem().PaddingTop(12).ExtendHorizontal().LineHorizontal(2).LineColor(Colors.Red.Medium); }); } }); page.Content().Column(column => { column.Spacing(25); foreach (var i in Enumerable.Range(1, 100)) column.Item().Background(Colors.Grey.Lighten2).Height(75).AlignCenter().AlignMiddle().Text(i.ToString()).FontSize(16); }); }); ``` That produces the following result: ![example](/api-reference/page-slots-advanced.png) ## Page settings It is possible to create a document containing pages having different settings. For example, the following code inserts an A4 page followed by an A3 page, both with different margins: ```csharp{10-13,21-24} public class StandardReport : IDocument { // metadata public void Compose(IDocumentContainer container) { container .Page(page => { page.MarginVertical(80); page.MarginHorizontal(100); page.PageColor(Colors.Grey.Medium); // transparent is default page.Size(PageSizes.A3); page.Header().Element(ComposeHeader); page.Content().Element(ComposeBigContent); page.Footer().AlignCenter().PageNumber(); }) .Page(page => { // you can specify multiple page types in the document // with independent configurations page.Margin(50) page.Size(PageSizes.A4); page.Header().Element(ComposeHeader); page.Content().Element(ComposeSmallContent); page.Footer().AlignCenter().PageNumber(); }); } // content implementation } ``` You easily change page orientation as illustrated below: ```csharp // default is portrait page.Size(PageSizes.A3); // explicit portrait orientation page.Size(PageSizes.A3.Portrait()); // change to landscape orientation page.Size(PageSizes.A3.Landscape()); ``` ## Continuous page size It is possible to define a page size with known width but dynamic height. In the following example, the resulting page has a constant width (equal to the width of an A4 page, but its height depends on the content: ```csharp{13} public class StandardReport : IDocument { // metadata public void Compose(IDocumentContainer container) { container .Page(page => { page.MarginVertical(40); page.MarginHorizontal(60); page.ContinuousSize(PageSizes.A4.Width); page.Header().Element(ComposeHeader); page.Content().Element(ComposeContent); page.Footer().AlignCenter().PageNumber(); }); } // content implementation } ``` ::: danger Because of practical layout limitations, the maximum page height is limited to 14400 points (around 5 meters). ::: ## Global text style The QuestPDF library provides a default set of styles that are applied to text. ```csharp .Text("Text with library default styles") ``` You can adjust the text style by providing additional arguments: ```csharp .Text("Red semibold text of size 20").FontSize(20).SemiBold() ``` The above option above overrides the default style. To get more control you can set a default text style in your document. Please notice that all changes are additive as shown in the following example ```csharp{9-10,22-23,27-28} public class SampleReport : IDocument { public DocumentMetadata GetMetadata() => new DocumentMetadata(); public void Compose(IDocumentContainer container) { container.Page(page => { // all text in this set of pages has size 20 page.DefaultTextStyle(TextStyle.Default.FontSize(20)); page.Margin(20); page.Size(PageSizes.A4); page.PageColor(Colors.White); page.Content().Column(column => { column.Item().Text(Placeholders.Sentence()); column.Item().Text(text => { // text in this block is additionally semibold text.DefaultTextStyle(x => x.SemiBold()); text.Line(Placeholders.Sentence()); // this text has size 20 but also semibold and red text.Span(Placeholders.Sentence()).FontColor(Colors.Red.Medium); }); }); }); } } ``` ![example](/patterns-and-practices/global-text-style.png) ## Global content direction (RTL) It is possible to globally specify content direction for entire documents. ::: tip To learn more about how the Content direction works, please read the documentation for the [ContentDirection](/api-reference/content-direction) element. ::: ```csharp document.Page(page => { // default setting page.ContentFromLeftToRight(); // optional RTL mode page.ContentFromRightToLeft(); }); ``` A further example follows: ```csharp{8} document.Page(page => { page.Size(PageSizes.A5); page.Margin(20); page.PageColor(Colors.White); page.DefaultTextStyle(x => x.FontFamily("Calibri").FontSize(20)); page.ContentFromRightToLeft(); page.Content().Column(column => { column.Spacing(20); column.Item() .Text("مثال على الفاتورة") // example invoice .FontSize(32).FontColor(Colors.Blue.Darken2).SemiBold(); column.Item().Table(table => { table.ColumnsDefinition(columns => { columns.RelativeColumn(); columns.ConstantColumn(75); columns.ConstantColumn(100); }); table.Cell().Element(HeaderStyle).Text("وصف السلعة"); // item description table.Cell().Element(HeaderStyle).Text("كمية"); // quantity table.Cell().Element(HeaderStyle).Text("سعر"); // price var items = new[] { "دورة البرمجة", // programming course "دورة تصميم الرسومات", // graphics design course "تحليل وتصميم الخوارزميات", // analysis and design of algorithms }; foreach (var item in items) { var price = Placeholders.Random.NextDouble() * 100; table.Cell().Text(item); table.Cell().Text(Placeholders.Random.Next(1, 10)); table.Cell().Text($"USD${price:F2}"); } static IContainer HeaderStyle(IContainer x) => x.BorderBottom(1).PaddingVertical(5); }); }); }); ``` ![example](/api-reference/page-content-direction-rtl.png) --- --- url: /pricing.md --- --- --- url: /privacy-policy.md --- --- --- url: /license/professional-enterprise.md --- --- --- url: /security-policy.md --- --- --- url: /terms-of-service.md --- --- --- url: /api-reference/tip-layout-constraints.md --- ::: danger Please be careful. This component may try to enforce size constraints that are impossible to meet. For example, the container may require more space than is available, or may try to squeeze its child into less space than possible. Such scenarios result in a layout exception. ::: --- --- url: /api-reference/tip-debugging.md --- ::: tip For enhanced development and debugging experience, please consider using [the QuestPDF Companion App](/companion/usage). ::: --- --- url: /api-reference/tip-color.md --- ::: tip Learn more about supported color formats and predefined color palettes in the [Colors](/concepts/colors) section. ::: --- --- url: /api-reference/tip-show-once-skip-once.md --- ::: tip Combine this element with SkipOnce to achieve more complex behaviors, e.g.: * `container.SkipOnce().ShowOnce()` ensures the child element is displayed only on the second page. * `container.SkipOnce().SkipOnce()` starts displaying the child element from the third page onwards. * `container.ShowOnce().SkipOnce()` draws nothing, as the order of invocation is important. ::: --- --- url: /api-reference/tip-unit.md --- ::: tip Learn more about supported units in the [Lenght unit types](/concepts/length-unit-types) section. ::: --- --- url: /features-overview.md --- --- --- url: /contact.md ---