Layout Elements

This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production.

The reference material consulted while preparing these pages is listed in the section bibliography.

The layout module of iText turns a tree of high level elements into positioned content on PDF pages. You describe what the invoice contains (a title, an address box, a list of payment steps, a link to the terms) and iText decides where each line goes, when a page is full and where the next page starts. This page covers the building blocks of that model with the Bookshelf Billing invoice as running scenario: Paragraph, Text, Div, List and ListItem, Link, AreaBreak, Image, UnitValue, margins, padding and borders, the Property.TRANSFORM property, footnotes, hyphenation and the layout.Canvas used to fill a fixed rectangle. Versions are stated as of 2026-10-09: iText Core 9.8.0 on Java 21. Every example below was compiled and run against exactly that version, and the text of the resulting PDFs was extracted to check that the content is where the page says it is.

Tables, fonts, page headers and footers and custom renderers are large topics of their own and have their own pages in this section: Tables, Fonts & Unicode, Page Events and Renderers & Custom Layout. The life cycle of the PdfWriter, PdfDocument and Document objects used here is described in Document Lifecycle.

Maven coordinates

The layout classes live in the layout module, which itext-core brings in together with the kernel, forms, signature, barcode, SVG and hyphenation modules. The coordinates are the ones from Getting Started with Spring Boot; the Bouncy Castle adapter is not needed for the layout examples on this page, but it is harmless and needed as soon as the same project signs or encrypts.

<dependency>
    <groupId>com.itextpdf</groupId>
    <artifactId>itext-core</artifactId>
    <version>9.8.0</version>
    <type>pom</type>
</dependency>
<dependency>
    <groupId>com.itextpdf</groupId>
    <artifactId>bouncy-castle-adapter</artifactId>
    <version>9.8.0</version>
</dependency>

iText is available under the AGPLv3 or under a commercial licence. With the AGPL, a service that lets users interact with it over a network has to offer its complete source code to those users, so a closed-source product needs a commercial licence. The layout module is part of the open source core: nothing on this page needs an add-on. See the Introduction & Approaches page of this section for the licensing consequences.

The element model

Everything you hand to a Document implements IElement, and two sub-interfaces decide how it behaves in the flow.

  • Block elements (IBlockElement) take a rectangle: Paragraph, Div, List, Table and LineSeparator. They start on a new line and stack vertically.

  • Leaf elements (ILeafElement) are the inline content inside a block: Text, Image, Tab and Link, which is a Text with an action. They are placed one after the other on a line and wrapped at the right edge. Image implements only ILeafElement, but Document has a separate add(Image) overload, so an image added directly to the document is laid out on a line of its own, like a block.

A Div is the generic container: it holds other block elements and gives them a common margin, padding, border and background. A ListItem is a Div with a list symbol. A Paragraph holds leaf elements. The API is fluent, so setters return the element itself, with a few exceptions that matter in practice (List.setPostSymbolText(...) and ListItem.add(...) return void and Div respectively, so those calls cannot be chained as one expression; the examples keep them on their own lines). The element tree is only a description: positioning happens later, when the Document asks a renderer to lay each element out, which is the subject of Renderers & Custom Layout.

Properties set on an element apply to it and are inherited by its children where the property is inheritable. Font, font size, font colour, text alignment and leading are inheritable, so document.setFont(...) and document.setFontSize(...) set the defaults for the whole document, while margin, padding, border and background are not inherited. A reusable bundle of properties is a Style, applied with addStyle(style) to as many elements as needed.

One API difference matters when you follow older tutorials. Elements in iText 9.8.0 no longer have setBold() and setItalic(); the compiled API has simulateBold() and simulateItalic(), which fake the weight and slant of whatever font is set. For genuine bold text, create a bold PdfFont and apply it with setFont(...), as the examples do. Fonts and font selection are covered in Fonts & Unicode.

Paragraph, Text and styles

A Paragraph is the workhorse. Create it from a string for a single run of text, or empty and add(...) several Text objects when parts of the line need their own font, size or colour. Paragraph level properties control the block: text alignment (TextAlignment.LEFT, CENTER, RIGHT, JUSTIFIED), first line indent, leading (the line spacing, as a fixed value with setFixedLeading or a multiple of the font size with setMultipliedLeading) and widow and orphan behaviour when the paragraph splits across pages. A Text carries the character level properties: font, size, colour, underline, character spacing and text rise.

Two small helpers complete the inline toolbox. Tab together with TabStop creates aligned columns inside a paragraph, with an optional leader line such as DottedLine, which is how the invoice below places the issue date flush right. LineSeparator is a block element that draws a horizontal rule using any ILineDrawer.

The first example builds the top of the Bookshelf invoice: a title, a line with the invoice number and a right-aligned date, a separator, a framed address box built with a Div, and a justified closing paragraph. It also shows a Style shared by two labels.

package com.example.billing;

import com.itextpdf.kernel.colors.ColorConstants;
import com.itextpdf.kernel.colors.DeviceRgb;
import com.itextpdf.kernel.font.PdfFont;
import com.itextpdf.kernel.font.PdfFontFactory;
import com.itextpdf.io.font.constants.StandardFonts;
import com.itextpdf.kernel.geom.PageSize;
import com.itextpdf.kernel.pdf.PdfDocument;
import com.itextpdf.kernel.pdf.PdfWriter;
import com.itextpdf.layout.Document;
import com.itextpdf.layout.Style;
import com.itextpdf.layout.borders.Border;
import com.itextpdf.layout.borders.SolidBorder;
import com.itextpdf.layout.element.Div;
import com.itextpdf.layout.element.LineSeparator;
import com.itextpdf.layout.element.Paragraph;
import com.itextpdf.layout.element.Tab;
import com.itextpdf.layout.element.TabStop;
import com.itextpdf.layout.element.Text;
import com.itextpdf.layout.properties.TabAlignment;
import com.itextpdf.layout.properties.TextAlignment;
import com.itextpdf.layout.properties.UnitValue;
import com.itextpdf.kernel.pdf.canvas.draw.SolidLine;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;

public class InvoiceHeader {

    public static byte[] render() throws IOException {
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        PdfFont regular = PdfFontFactory.createFont(StandardFonts.HELVETICA);
        PdfFont bold = PdfFontFactory.createFont(StandardFonts.HELVETICA_BOLD);

        try (PdfDocument pdf = new PdfDocument(new PdfWriter(out));
             Document document = new Document(pdf, PageSize.A4)) {
            document.setFont(regular).setFontSize(10);
            document.setMargins(54, 54, 54, 54); // top, right, bottom, left in points

            Style label = new Style().setFont(bold).setFontColor(ColorConstants.GRAY);

            Paragraph title = new Paragraph("Bookshelf Billing")
                    .setFont(bold).setFontSize(20)
                    .setMarginBottom(2);
            Paragraph subtitle = new Paragraph()
                    .add(new Text("Invoice ").addStyle(label))
                    .add(new Text("2026-000042").setFont(bold))
                    .add(new Tab())
                    .addTabStops(new TabStop(480, TabAlignment.RIGHT))
                    .add(new Text("Issued 2026-10-09").addStyle(label));
            document.add(title).add(subtitle);
            document.add(new LineSeparator(new SolidLine(0.5f)).setMarginTop(4).setMarginBottom(8));

            Div customer = new Div()
                    .setWidth(UnitValue.createPercentValue(50))
                    .setPadding(8)
                    .setMarginBottom(12)
                    .setBackgroundColor(new DeviceRgb(0xF3, 0xF4, 0xF6))
                    .setBorder(new SolidBorder(new DeviceRgb(0x9C, 0xA3, 0xAF), 0.75f))
                    .setBorderLeft(new SolidBorder(new DeviceRgb(0x1D, 0x4E, 0xD8), 3));
            customer.add(new Paragraph("Bill to").addStyle(label).setMargin(0));
            for (String line : List.of("Example Reader", "1 Placeholder Street", "Sample City")) {
                customer.add(new Paragraph(line).setMargin(0));
            }
            document.add(customer);

            document.add(new Paragraph("Thank you for shopping at the Bookshelf. Payment is due within 30 days.")
                    .setTextAlignment(TextAlignment.JUSTIFIED)
                    .setFirstLineIndent(12)
                    .setMultipliedLeading(1.3f)
                    .setBorder(Border.NO_BORDER));
        }
        return out.toByteArray();
    }

    public static void main(String[] args) throws IOException {
        Files.write(Path.of("target/invoice-header.pdf"), render());
    }
}

PdfDocument and Document are AutoCloseable, so the try-with-resources block closes both in the right order and flushes the last page; there is no explicit document.close(). The sizes are in points (1/72 inch), the PDF default unit, and the page is A4, 595 by 842 points.

Units, margins, padding and borders

Sizes in iText are float values in points, or a UnitValue when the unit matters. A UnitValue is either UnitValue.createPointValue(...) or UnitValue.createPercentValue(...); the percentage is relative to the width of the containing area, which is how the address box in the example takes half the page width with setWidth(UnitValue.createPercentValue(50)). Setters such as setWidth(float) and setMarginTop(float) are the point shorthand.

The box model follows the same layers as CSS, from the outside in.

  • Margin is the empty space outside the border: setMargin(...), setMargins(top, right, bottom, left) and the setMarginTop, setMarginRight, setMarginBottom and setMarginLeft variants. A Document has its own page margins, set with document.setMargins(...), in the same top, right, bottom, left order.

  • Border is the line around the element: setBorder(...) for all sides and setBorderTop, setBorderLeft and so on for a single side. The layout.borders package has SolidBorder, DashedBorder, DottedBorder, DoubleBorder, RoundDotsBorder, Border3D variants and Border.NO_BORDER. A border takes a colour and a width.

  • Padding is the space between the border and the content: setPadding(...) and the per-side variants.

  • Background is setBackgroundColor(...), painted inside the border box.

Colours are Color objects from the kernel: ColorConstants.GRAY, new DeviceRgb(...), new DeviceCmyk(...). Their model (RGB, CMYK, spot colours and patterns) is covered in Colours, Images & SVG.

By default the width of a block element is the width of the available area, and its height grows with the content. Setting setWidth or setHeight fixes it, and setMinHeight and setMaxHeight bound it. A fixed height smaller than the content is the situation the overflow property in the Canvas section below deals with.

A List holds ListItem objects (or plain strings, which become items). The constructor with a ListNumberingType (DECIMAL, ROMAN_LOWER, ENGLISH_UPPER, GREEK_LOWER and others) makes an ordered list; the default constructor makes a bulleted one. setListSymbol(...) accepts a string, a Text, an Image or a numbering type, setSymbolIndent(...) sets the gap between symbol and content, and setPreSymbolText and setPostSymbolText add text around a generated number, such as the dot in 1.. A ListItem is a Div, so it can contain further paragraphs and nested lists.

A Link is a Text that carries a PdfAction. PdfAction.createURI(...) opens a web address, and PdfAction.createGoTo("name") jumps to a named destination, created on the target element with setDestination("name"). Links get no visual treatment of their own, so the example gives them a colour and an underline. Bookmarks, annotations and the other kinds of action are in Navigation, Annotations & Actions.

An AreaBreak ends the current area: AreaBreakType.NEXT_PAGE starts a new page, NEXT_AREA moves to the next area (the next column in a multi-column renderer, otherwise the next page) and LAST_PAGE continues on the last existing page, which is useful when a document is built in several passes.

An Image wraps an ImageData created by ImageDataFactory.create(...) from a file path, a URL or a byte array. As a block element it can be scaled with scaleToFit(maxWidth, maxHeight) (keeps the aspect ratio), scaleAbsolute(...) or setWidth(...), which is the percentage-capable variant used in the example together with setAutoScale(false) to keep the image from being rescaled to the available width. The example draws a placeholder logo in memory with java.awt, so it needs no resource file; in a Spring Boot service the bytes would come from the classpath or from object storage. Image formats, masks and SVG are covered in Colours, Images & SVG.

package com.example.billing;

import com.itextpdf.io.image.ImageDataFactory;
import com.itextpdf.kernel.colors.ColorConstants;
import com.itextpdf.io.font.constants.StandardFonts;
import com.itextpdf.kernel.font.PdfFont;
import com.itextpdf.kernel.font.PdfFontFactory;
import com.itextpdf.kernel.pdf.PdfDocument;
import com.itextpdf.kernel.pdf.PdfWriter;
import com.itextpdf.kernel.pdf.action.PdfAction;
import com.itextpdf.layout.Document;
import com.itextpdf.layout.element.AreaBreak;
import com.itextpdf.layout.element.Image;
import com.itextpdf.layout.element.Link;
import com.itextpdf.layout.element.List;
import com.itextpdf.layout.element.ListItem;
import com.itextpdf.layout.element.Paragraph;
import com.itextpdf.layout.element.Text;
import com.itextpdf.layout.properties.AreaBreakType;
import com.itextpdf.layout.properties.ListNumberingType;
import com.itextpdf.layout.properties.UnitValue;

import javax.imageio.ImageIO;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;

public class LinksListsImages {

    /** A 120x40 placeholder logo drawn in memory, so the example needs no external file. */
    static byte[] logoPng() throws IOException {
        BufferedImage image = new BufferedImage(120, 40, BufferedImage.TYPE_INT_RGB);
        Graphics2D g = image.createGraphics();
        g.setColor(new Color(0x1D4ED8));
        g.fillRect(0, 0, 120, 40);
        g.dispose();
        ByteArrayOutputStream png = new ByteArrayOutputStream();
        ImageIO.write(image, "png", png);
        return png.toByteArray();
    }

    public static byte[] render() throws IOException {
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        PdfFont bold = PdfFontFactory.createFont(StandardFonts.HELVETICA_BOLD);
        try (PdfDocument pdf = new PdfDocument(new PdfWriter(out));
             Document document = new Document(pdf)) {

            Image logo = new Image(ImageDataFactory.create(logoPng()))
                    .scaleToFit(120, 40)
                    .setMarginBottom(12);
            document.add(logo);

            // An ordered list with a nested bulleted list
            List steps = new List(ListNumberingType.DECIMAL);
            steps.setSymbolIndent(12);
            steps.setPostSymbolText(". ");
            steps.add("Review the invoice lines");
            List details = new List();
            details.setListSymbol("• ");
            details.add("Reference: the invoice number");
            details.add("Due within 30 days");
            ListItem transfer = new ListItem("Pay by bank transfer");
            transfer.add(details);
            steps.add(transfer);
            steps.add("Keep the PDF for your records");
            document.add(new Paragraph(new Text("How to pay").setFont(bold)));
            document.add(steps);

            // External link and internal link to a named destination on the second page
            Link terms = new Link("terms of sale", PdfAction.createURI("https://example.com/terms"));
            terms.setFontColor(ColorConstants.BLUE).setUnderline();
            Link appendix = new Link("see the appendix", PdfAction.createGoTo("appendix"));
            appendix.setFontColor(ColorConstants.BLUE).setUnderline();
            document.add(new Paragraph()
                    .add("By paying you accept the ").add(terms)
                    .add("; for details ").add(appendix).add("."));

            document.add(new AreaBreak(AreaBreakType.NEXT_PAGE));
            document.add(new Paragraph(new Text("Appendix").setFont(bold)).setDestination("appendix"));
            document.add(new Image(ImageDataFactory.create(logoPng()))
                    .setWidth(UnitValue.createPercentValue(30))
                    .setAutoScale(false));
            document.add(new Paragraph(new Text("End of document")));
        }
        return out.toByteArray();
    }

    public static void main(String[] args) throws IOException {
        Files.write(Path.of("target/links-lists-images.pdf"), render());
    }
}

Java 21 idioms with the layout API (O28)

The material that still circulates about iText was written for Java 1.3 to 5: no generics, anonymous inner classes for every callback, manual close() calls in finally blocks and string concatenation for multi-line text. The iText 9.8 API targets modern Java and the documentation pages in this section use Java 21 throughout. Four idioms are worth adopting when you generate documents.

  • try-with-resources for PdfDocument and Document (and for PdfWriter and PdfReader when they are created on their own), so a failure while building the document still releases the output stream.

  • Records for the data passed to the layout code: an invoice line, a customer, a report row. A record is immutable and has equals, hashCode and toString for free, which suits a value that is read once and rendered.

  • Text blocks for fixed multi-line text such as payment terms, with String.strip() to drop the final newline and formatted(...) for the few values that change.

  • Immutable collections and var: List.of(...) for fixed data and local var where the type is obvious from the right-hand side. A note on names: the iText class com.itextpdf.layout.element.List clashes with java.util.List, so a file that uses both imports one and qualifies the other.

The example below renders the body of the invoice with a record and a text block. Each line is a paragraph with a dotted tab leader, the quantity and title on the left and the amount flush right.

package com.example.billing;

import com.itextpdf.kernel.pdf.PdfDocument;
import com.itextpdf.kernel.pdf.PdfWriter;
import com.itextpdf.kernel.pdf.canvas.draw.DottedLine;
import com.itextpdf.layout.Document;
import com.itextpdf.layout.element.Paragraph;
import com.itextpdf.layout.element.Tab;
import com.itextpdf.layout.element.TabStop;
import com.itextpdf.layout.element.Text;
import com.itextpdf.layout.properties.TabAlignment;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.math.BigDecimal;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;

public class InvoiceBody {

    /** One invoice line, as a record: immutable, with equals and toString for free. */
    record InvoiceLine(String title, int quantity, BigDecimal unitPrice) {
        BigDecimal total() {
            return unitPrice.multiply(BigDecimal.valueOf(quantity));
        }
    }

    private static final String TERMS = """
            Payment is due within 30 days.
            Late payments accrue interest as stated in the terms of sale.
            """;

    public static byte[] render(List<InvoiceLine> lines) throws IOException {
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        try (PdfDocument pdf = new PdfDocument(new PdfWriter(out));
             Document document = new Document(pdf)) {

            for (InvoiceLine line : lines) {
                // Dotted leader between the title and a right-aligned amount
                Paragraph row = new Paragraph()
                        .addTabStops(new TabStop(480, TabAlignment.RIGHT, new DottedLine()))
                        .add(new Text("%d x %s".formatted(line.quantity(), line.title())))
                        .add(new Tab())
                        .add(new Text("%s EUR".formatted(line.total().setScale(2))));
                document.add(row);
            }
            document.add(new Paragraph(TERMS.strip()).setMarginTop(12));
        }
        return out.toByteArray();
    }

    public static void main(String[] args) throws IOException {
        var lines = List.of(
                new InvoiceLine("Example Book One", 2, new BigDecimal("12.50")),
                new InvoiceLine("Example Book Two", 1, new BigDecimal("17.00")));
        Files.write(Path.of("target/invoice-body.pdf"), render(lines));
    }
}

Money is a BigDecimal and the formatting is explicit; never use float or double for amounts, even though iText itself uses float for coordinates. Pattern matching for switch and sealed types are equally natural for choosing between element types in a larger builder, but they add nothing specific to iText, so the examples keep to the four idioms above.

Source: the same layout tutorials as above, chapter 2, combined with the Java SE 21 language updates.

Transforms, footnotes and hyphenation

Three properties go beyond the CSS-like basics.

Transform. In iText 9.8 the Property.TRANSFORM property, with the Transform class, applies an affine transformation to an element: rotate(radians), translate, scaleX, scaleY, skewX and skewY, which can be chained. The element is laid out as usual and then drawn transformed, so the space it occupies in the flow is the untransformed rectangle. There is no setTransform convenience method on the elements: the property is set with setProperty(Property.TRANSFORM, transform), whose return type is void, so it is written as its own statement. The example rotates a "PAID" stamp by 20 degrees.

Footnotes. A Footnote holds the note text, and a FootnoteAnchor is the inline marker added to a paragraph; the note is collected at the bottom of the page the anchor lands on. A FootnoteAnchor built from the note alone shows an asterisk; the markers are numbered only when a numbering type is set with FootnotesProperties.setFootnoteNumberingType(...). In 9.8.0 the classes are in the package com.itextpdf.layout.properties.margins, next to FootnotesProperties, which configures the numbering type and the styles of the container and the markers, and is set on the Document. Because the package lives under properties, the classes are easy to miss when searching the API by name.

Hyphenation. A HyphenationConfig tells a paragraph to break long words at syllable boundaries, which matters for narrow columns and justified text. The constructor takes the language and country, and the minimum number of characters to leave before and after the hyphen: new HyphenationConfig("en", "GB", 3, 3). The patterns ship in the hyph module, which itext-core already includes; other languages are selected by passing their language code. In the output of the example the word "documentation" is split as "doc-" and "umentation" at the end of the line, because the width of 110 points is too narrow for it.

package com.example.billing;

import com.itextpdf.io.font.constants.StandardFonts;
import com.itextpdf.kernel.font.PdfFont;
import com.itextpdf.kernel.font.PdfFontFactory;
import com.itextpdf.kernel.pdf.PdfDocument;
import com.itextpdf.kernel.pdf.PdfWriter;
import com.itextpdf.layout.Document;
import com.itextpdf.layout.element.Div;
import com.itextpdf.layout.element.Paragraph;
import com.itextpdf.layout.element.Text;
import com.itextpdf.layout.hyphenation.HyphenationConfig;
import com.itextpdf.layout.properties.Property;
import com.itextpdf.layout.properties.TextAlignment;
import com.itextpdf.layout.properties.Transform;
import com.itextpdf.layout.properties.margins.Footnote;
import com.itextpdf.layout.properties.margins.FootnoteAnchor;
import com.itextpdf.layout.properties.margins.FootnotesProperties;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;

public class TransformFootnotesHyphenation {

    public static byte[] render() throws IOException {
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        PdfFont bold = PdfFontFactory.createFont(StandardFonts.HELVETICA_BOLD);
        try (PdfDocument pdf = new PdfDocument(new PdfWriter(out));
             Document document = new Document(pdf)) {

            document.setFootnotesProperties(new FootnotesProperties());

            // 1. Transform: a PAID stamp rotated 20 degrees
            Div stamp = new Div().add(new Paragraph(new Text("PAID").setFont(bold)).setFontSize(24));
            stamp.setProperty(Property.TRANSFORM, new Transform().rotate((float) Math.toRadians(20)));
            document.add(stamp);

            // 2. Footnotes: anchor in the text, note collected at the bottom of the page
            Footnote vat = new Footnote("Prices include VAT at the rate in force on the issue date.");
            document.add(new Paragraph("Total due: 42.00 EUR")
                    .add(new FootnoteAnchor(vat)));

            // 3. Hyphenation: break long words at syllable boundaries
            Paragraph narrow = new Paragraph("Internationalization of the extraordinarily "
                    + "straightforward documentation")
                    .setWidth(110)
                    .setTextAlignment(TextAlignment.JUSTIFIED)
                    .setHyphenation(new HyphenationConfig("en", "GB", 3, 3));
            document.add(narrow);
        }
        return out.toByteArray();
    }

    public static void main(String[] args) throws IOException {
        Files.write(Path.of("target/transform-footnotes-hyphenation.pdf"), render());
    }
}

The footnote text appears at the bottom of the page, and the anchor is shown as an asterisk after the amount, because the example sets no numbering type; both can be restyled through FootnotesProperties.

Source: Transform API, Footnote API and HyphenationConfig API. These three API pages are the reference for the example; the Knowledge Base chapters on the layout module predate them and do not cover transforms or footnotes.

Fixed areas with layout.Canvas

Document flows content from page to page. Sometimes the position is dictated by a pre-printed form or a design: the bank details box in the top right corner, a label next to a barcode, a stamp area. For that, the layout module offers com.itextpdf.layout.Canvas, a layout container for a single rectangle that you create over a PdfCanvas (a page) or a PdfFormXObject. You add the same elements to it as to a Document, and they are laid out inside the rectangle.

There is no page flow: a Canvas never creates a new page, so content that does not fit has nowhere to go. An element that does not fit in the remaining space gets a NOTHING layout result and is dropped, whatever its overflow properties say. Property.OVERFLOW_Y set to hidden only clips content that extends beyond an element’s own fixed height (set with setHeight); it does not make an element fit in the canvas. The example sets it on the last paragraph to show that the paragraph is still dropped. The rectangle is new Rectangle(x, y, width, height), with the origin at the bottom left of the page and the y axis pointing up, the opposite of a screen.

package com.example.billing;

import com.itextpdf.kernel.colors.ColorConstants;
import com.itextpdf.kernel.geom.PageSize;
import com.itextpdf.kernel.geom.Rectangle;
import com.itextpdf.io.font.constants.StandardFonts;
import com.itextpdf.kernel.font.PdfFont;
import com.itextpdf.kernel.font.PdfFontFactory;
import com.itextpdf.kernel.pdf.PdfDocument;
import com.itextpdf.kernel.pdf.PdfPage;
import com.itextpdf.kernel.pdf.PdfWriter;
import com.itextpdf.kernel.pdf.canvas.PdfCanvas;
import com.itextpdf.layout.Canvas;
import com.itextpdf.layout.element.Paragraph;
import com.itextpdf.layout.element.Text;
import com.itextpdf.layout.properties.OverflowPropertyValue;
import com.itextpdf.layout.properties.Property;
import com.itextpdf.layout.properties.TextAlignment;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;

public class FixedAreaCanvas {

    public static byte[] render() throws IOException {
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        PdfFont bold = PdfFontFactory.createFont(StandardFonts.HELVETICA_BOLD);
        try (PdfDocument pdf = new PdfDocument(new PdfWriter(out))) {
            PdfPage page = pdf.addNewPage(PageSize.A4);
            Rectangle box = new Rectangle(54, 700, 240, 80); // x, y (bottom left), width, height

            PdfCanvas pdfCanvas = new PdfCanvas(page);
            pdfCanvas.setStrokeColor(ColorConstants.GRAY).rectangle(box).stroke();

            try (Canvas canvas = new Canvas(pdfCanvas, box)) {
                canvas.add(new Paragraph(new Text("Bank transfer details").setFont(bold)));
                canvas.add(new Paragraph("IBAN: ES00 0000 0000 0000 0000 0000 (placeholder)")
                        .setTextAlignment(TextAlignment.LEFT));
                // Does not fit in the 80 point box: dropped, OVERFLOW_Y hidden does not change that
                Paragraph overflow = new Paragraph("A line that does not fit in an 80 point high area.");
                overflow.setProperty(Property.OVERFLOW_Y, OverflowPropertyValue.HIDDEN);
                canvas.add(overflow);
            }
        }
        return out.toByteArray();
    }

    public static void main(String[] args) throws IOException {
        Files.write(Path.of("target/fixed-area-canvas.pdf"), render());
    }
}

In the output only the first two paragraphs appear: the third did not fit in the 80 point high rectangle and was dropped, not clipped. The Canvas also needs closing, here again with try-with-resources, which lays out the elements and flushes them to the PdfCanvas. The lower level drawing operators of the PdfCanvas itself (paths, colours, graphics state) are covered in Low-level Canvas & Graphics.

Summary

Element or property Use in the Bookshelf invoice

Paragraph, Text, Style

Title, labels, addresses, closing text

Tab, TabStop, LineSeparator

Right-aligned date, dotted leaders, rule under the title

Div, margins, padding, borders

Framed address box

List, ListItem

Payment steps

Link, AreaBreak

Link to the terms, appendix on a new page

Image, UnitValue

Logo, relative widths

Property.TRANSFORM, Footnote, HyphenationConfig

PAID stamp, VAT note, narrow columns

Canvas

Bank details box at a fixed position