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,TableandLineSeparator. They start on a new line and stack vertically. -
Leaf elements (
ILeafElement) are the inline content inside a block:Text,Image,TabandLink, which is aTextwith an action. They are placed one after the other on a line and wrapped at the right edge.Imageimplements onlyILeafElement, butDocumenthas a separateadd(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 thesetMarginTop,setMarginRight,setMarginBottomandsetMarginLeftvariants. ADocumenthas its own page margins, set withdocument.setMargins(...), in the same top, right, bottom, left order. -
Border is the line around the element:
setBorder(...)for all sides andsetBorderTop,setBorderLeftand so on for a single side. Thelayout.borderspackage hasSolidBorder,DashedBorder,DottedBorder,DoubleBorder,RoundDotsBorder,Border3Dvariants andBorder.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.
Lists, links, page breaks and images
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
PdfDocumentandDocument(and forPdfWriterandPdfReaderwhen 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,hashCodeandtoStringfor 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 andformatted(...)for the few values that change. -
Immutable collections and
var:List.of(...)for fixed data and localvarwhere the type is obvious from the right-hand side. A note on names: the iText classcom.itextpdf.layout.element.Listclashes withjava.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 |
|---|---|
|
Title, labels, addresses, closing text |
|
Right-aligned date, dotted leaders, rule under the title |
|
Framed address box |
|
Payment steps |
|
Link to the terms, appendix on a new page |
|
Logo, relative widths |
|
PAID stamp, VAT note, narrow columns |
|
Bank details box at a fixed position |