PDF Document API
The com.hsyco.pdf API creates PDF documents from plugins and drivers: flowing text, lists, tables with repeated header rows, images, Java2D drawings, headers and footers with page numbers. It is built on Apache PDFBox 3.0.8, embedded in hsyco.jar (see Office and PDF Libraries), and gives access to PDFBox for everything it doesn't cover.
import com.hsyco.pdf.*;
import java.awt.Color;
import java.io.File;
try (Document doc = new Document(PageSize.A4, 40, 40, 60, 50)) { // margins: left, right, top, bottom, in points
doc.addTitle("Energy report");
doc.setFooter("Building A", "", "Page {page} of {pages}", new Font(Font.SANS, 8));
Paragraph title = new Paragraph("Energy report - October 2026", new Font(Font.SANS, 16, Font.BOLD));
title.setSpacingAfter(12);
title.setKeepWithNext(true);
doc.add(title);
Table table = new Table(new float[] {3, 1, 1}); // relative widths
table.setWidthPercentage(100);
table.setHeaderRows(1); // repeated on each page
table.setAlternateRowColor(new Color(0xF2F4F7));
table.getDefaultCell().setPadding(4); // also for the Cells below
Font bold = new Font(Font.SANS, 9, Font.BOLD);
table.addCell(new Cell(new Phrase("Meter", bold)));
table.addCell(new Cell(new Phrase("kWh", bold)));
table.addCell(new Cell(new Phrase("Cost", bold)));
for (Reading r : readings) {
table.addCell(r.name);
Cell kwh = new Cell(String.format("%.1f", r.kwh));
kwh.setHorizontalAlignment(Element.ALIGN_RIGHT);
table.addCell(kwh);
table.addCell(String.format("%.2f €", r.cost));
}
doc.add(table);
// a bar chart in Java2D: points, origin at the top left of the block
doc.add(Drawing.graphics2D(150, (g, width, height) -> {
g.setColor(new Color(0x3366CC));
for (int i = 0; i < daily.length; i++) {
int h = (int) (daily[i] / maxDaily * (height - 15));
g.fillRect(i * 16, (int) height - h, 12, h);
}
g.setColor(Color.BLACK);
g.setFont(new java.awt.Font("SansSerif", java.awt.Font.PLAIN, 8));
g.drawString("kWh per day", 0, 10);
}));
doc.save(new File("reports/energy-2026-10.pdf")); // creates reports/ if needed
}
Layout rules
- Elements are placed top to bottom in the content area of the page, in the order they are added. What doesn't fit moves to a new page, with the same size and margins, or the ones set with
setPageSize()andsetMargins()for the following pages. All lengths are in points (1/72 inch);Units.mm()converts millimeters. - Lines break at spaces, measured with the font metrics. A word longer than the line is broken between characters. There is no hyphenation.
- Text is normalized to composed characters. A tab is drawn as four spaces;
\r\nand\rare line breaks like\n; a no-break space (U+00A0) looks like a space but never breaks; a soft hyphen and other control characters are removed. - Justified text widens the real spaces, so text extraction, search and copy and paste see one space between words. The last line of a paragraph and a line ended by
\nare not justified. - A paragraph splits between lines across pages unless
setKeepTogether(true).setKeepWithNext(true)moves a heading to the next page together with the first line of the following element. - A table row is not split if it fits on an empty page: it moves to the next page, and the header rows are repeated first. Header rows are never left alone at the bottom of a page. A row taller than a full page is split between text lines of its cells; an image in a cell is scaled down to fit. Column widths are computed once per table. The alternate row color counts body rows from the first one, across pages.
- A character that the font doesn't have is drawn as
?and logged once per document in the HSYCO error log. WithTIMESandCOURIERthis applies to everything outside Western European text. - Right-to-left text and scripts that need glyph shaping (Arabic, Indic) are not supported. Chinese, Japanese and Korean work with a TrueType font registered with
Font.registerFamily()or loaded withFont.load(), with line breaks between characters. - Page content is kept in temporary files under
.tmp/pdfin the HSYCO folder, not in memory, so long reports fit on a Raspberry Pi.close()deletes them; use try-with-resources. On a Raspberry Pi, a 50-page report takes about 10 seconds: build reports in a separate thread, not in callbacks that must return quickly.
Fonts
SANS is the default family: Liberation Sans, embedded, for all the Latin, Greek and Cyrillic text it covers, and metrically compatible with Helvetica and Arial. HELVETICA is the same font. TIMES (also TIMES_ROMAN) and COURIER are PDF standard fonts, not embedded, for Western European text only. Only the characters used are embedded. Other TrueType fonts are added with Font.registerFamily() or Font.load().
Names shared with other packages
Cell also exists in Apache POI (org.apache.poi.ss.usermodel), Font and Image in java.awt, Document and Element in org.w3c.dom. With wildcard imports of both packages the compiler reports the ambiguity: next to import com.hsyco.pdf.*;, import other classes by their single name (import java.awt.Color;), and write a clashing one fully qualified, such as java.awt.Font in a Java2D drawing.
Document
A PDF document laid out from elements added in order. Headers, footers and page decorators are drawn at save(), when the page count is known.
Document(PageSize size)
Document(PageSize size, float left, float right, float top, float bottom)
Creates a document. The margins are in points; without them, 36 points on every side.
add
void add(Element element) throws IOException
Lays out an element after the previous ones: a Paragraph, Phrase, ItemList, Table, Image, Drawing, LineSeparator, Spacer or PageBreak. An element with setKeepWithNext(true) waits for the next one. Throws IllegalStateException after save().
newPage
void newPage() throws IOException
Starts a new page for the content that follows. Has no effect on a page without content, and a break that no content follows adds no page.
setPageSize
void setPageSize(PageSize size)
The page size of the following pages.
setMargins
void setMargins(float left, float right, float top, float bottom)
The margins of the following pages, in points.
addTitle
void addTitle(String title)
void addAuthor(String author)
void addSubject(String subject)
void addKeywords(String keywords)
void addCreator(String creator)
Document properties.
setHeader
void setHeader(String text, int alignment, Font font)
void setHeader(String left, String center, String right, Font font)
A header in the top margin, with its baseline at half the top margin: one text aligned Element.ALIGN_LEFT, ALIGN_CENTER or ALIGN_RIGHT, or three texts. {page} and {pages} are replaced by the page number and count.
setFooter
void setFooter(String text, int alignment, Font font)
void setFooter(String left, String center, String right, Font font)
A footer in the bottom margin, with its baseline at half the bottom margin, like setHeader.
setPageDecorator
void setPageDecorator(PageDecorator decorator)
Draws on every page at save(), after the header and footer: logos, watermarks, custom headers.
getPDDocument
PDDocument getPDDocument()
The PDFBox document being built, for everything this API doesn't cover.
save
void save(File file) throws IOException
void save(OutputStream out) throws IOException
Writes the document. save(File) creates missing folders. The first save draws headers, footers and decorators; no element can be added after it.
close
void close() throws IOException
Deletes the temporary files. A document with content that was closed without save() writes nothing, and logs HSYCO PDF: document closed without save(), nothing written.
PageSize
A page size in points. rotate() gives the landscape size.
PageSize(float width, float height)
A custom size in points.
PageSize constants
PageSize A3, A4, A5, LETTER, LEGAL
A3 841.89 × 1190.55, A4 595.28 × 841.89, A5 419.53 × 595.28, LETTER 612 × 792, LEGAL 612 × 1008.
rotate
PageSize rotate()
The same size, landscape.
getWidth
float getWidth()
float getHeight()
Units
mm
static float mm(float mm)
Converts millimeters to points.
Font
A font: family, size in points, style and color (java.awt.Color, black by default).
Font(String family, float size)
Font(String family, float size, int style)
Font(String family, float size, int style, Color color)
Font constants
String SANS, HELVETICA, TIMES, TIMES_ROMAN, COURIER
int NORMAL, BOLD, ITALIC, BOLDITALIC, UNDERLINE
Families and styles; styles combine, such as Font.BOLD | Font.UNDERLINE.
registerFamily
static void registerFamily(String name, File regular, File bold, File italic, File boldItalic)
Adds a TrueType family. A style without a file (null) uses the regular file; no bold or italic is synthesized. Throws IllegalArgumentException when a file can't be read.
load
static Font load(File ttf, float size, int style, Color color)
A shortcut for a family of one TrueType file.
getFamily
String getFamily()
float getSize()
int getStyle()
Color getColor()
Chunk
A run of text in one font. A chunk without its own font takes the font of its phrase.
Chunk(String text)
Chunk(String text, Font font)
NEWLINE
static Chunk NEWLINE
A forced line break.
setBackground
Chunk setBackground(Color color)
Phrase
A sequence of chunks with no paragraph spacing, used in cells and as the base of Paragraph. The default font is SANS 12.
Phrase()
Phrase(String text)
Phrase(String text, Font font)
add
Phrase add(Chunk chunk)
Paragraph
A phrase laid out as a block. \n in the text is a line break.
Paragraph()
Paragraph(String text)
Paragraph(String text, Font font)
Paragraph(Phrase phrase)
setAlignment
void setAlignment(int alignment)
Element.ALIGN_LEFT (default), ALIGN_CENTER, ALIGN_RIGHT or ALIGN_JUSTIFIED.
setLeading
void setLeading(float points)
A fixed line height.
setMultipliedLeading
void setMultipliedLeading(float factor)
A line height of factor times the largest font size of each line; 1.25 by default.
setSpacingBefore
void setSpacingBefore(float points)
void setSpacingAfter(float points)
Space before (dropped at the top of a page) and after.
setIndentationLeft
void setIndentationLeft(float points)
void setIndentationRight(float points)
void setFirstLineIndent(float points)
setKeepTogether
void setKeepTogether(boolean keep)
Moves the whole paragraph to the next page when it doesn't fit the rest of the page and fits on an empty one.
setKeepWithNext
void setKeepWithNext(boolean keep)
Keeps the paragraph on the same page as the first line of the following element.
ItemList
A bulleted or numbered list. Not called List, to avoid the clash with java.util.List.
ItemList(boolean numbered)
add
ItemList add(String text)
ItemList add(Paragraph item)
setSymbol
void setSymbol(String symbol)
The bullet, • by default. Numbered lists use 1., 2., …
setIndentation
void setIndentation(float points)
Distance of the item text from the symbol, 20 points by default.
Table
A table of cells, filled row by row. Cells added as String, Phrase or Cell all take the values they don't set from the default cell, as it is when they are added.
Table(int columns)
Table(float[] relativeWidths)
setWidthPercentage
void setWidthPercentage(float percent)
Percentage of the content width, 80 by default.
setTotalWidth
void setTotalWidth(float points)
An absolute width, which wins over the percentage.
setHorizontalAlignment
void setHorizontalAlignment(int alignment)
Placement of the table, Element.ALIGN_CENTER by default.
setHeaderRows
void setHeaderRows(int rows)
The first rows, repeated at the top of each page.
getDefaultCell
Cell getDefaultCell()
The default values of the cells added after: padding 2, a 0.5-point black box border, left and top aligned, colspan 1. Changing it affects only the cells added afterwards, so a header can be added with one default and the body with another.
addCell
void addCell(String text)
void addCell(Phrase phrase)
void addCell(Cell cell)
void addCell(Image image)
Adds a copy of the cell: changing a Cell after adding it has no effect.
setSpacingBefore
void setSpacingBefore(float points)
void setSpacingAfter(float points)
setAlternateRowColor
void setAlternateRowColor(Color color)
The background of every second body row, counted from the first body row across pages.
completeRow
void completeRow()
Fills the current row with empty cells. An incomplete last row is completed automatically.
Cell
A table cell. Every property not set takes the value the table's default cell has when the cell is added.
Cell(String text)
Cell(Phrase phrase)
Cell(Paragraph paragraph)
Cell(Image image)
An image is scaled down to the cell's width.
Cell constants
int NO_BORDER, TOP, BOTTOM, LEFT, RIGHT, BOX
Border sides, combined in a mask.
setColspan
void setColspan(int columns)
setHorizontalAlignment
void setHorizontalAlignment(int alignment)
void setVerticalAlignment(int alignment)
ALIGN_LEFT, ALIGN_CENTER, ALIGN_RIGHT, ALIGN_JUSTIFIED; vertically ALIGN_TOP, ALIGN_MIDDLE, ALIGN_BOTTOM.
setPadding
void setPadding(float points)
void setPaddingLeft(float points)
void setPaddingRight(float points)
void setPaddingTop(float points)
void setPaddingBottom(float points)
setBackgroundColor
void setBackgroundColor(Color color)
setBorder
void setBorder(int mask)
void setBorderWidth(float points)
void setBorderColor(Color color)
setMinimumHeight
void setMinimumHeight(float points)
setNoWrap
void setNoWrap(boolean noWrap)
Keeps each line of text on one line, clipped to the cell.
Image
An image, as a block or in a cell. JPEG files are embedded as they are; other files, and a BufferedImage without a quality, are converted losslessly. Unscaled, 1 pixel is 1 point; an image larger than the content area is scaled down to fit.
getInstance
static Image getInstance(File file) throws IOException
static Image getInstance(String path) throws IOException
static Image getInstance(byte[] data) throws IOException
static Image getInstance(BufferedImage image)
static Image getInstance(BufferedImage image, float jpegQuality)
A path is a local file; URLs aren't fetched. With a JPEG quality (0.8 for photos and camera frames), a BufferedImage is embedded as JPEG; without it, losslessly, which suits charts and screenshots but makes a photo several MB per page.
scaleToFit
void scaleToFit(float width, float height)
void scalePercent(float percent)
void scaleAbsolute(float width, float height)
setAlignment
void setAlignment(int alignment)
void setSpacingBefore(float points)
void setSpacingAfter(float points)
LineSeparator
LineSeparator(float widthPercent, float thickness, Color color)
A horizontal rule, centered.
Spacer
Spacer(float points)
Vertical space.
PageBreak
PageBreak()
A forced page break, like newPage(): a break at the end of the document adds no page.
Drawing
A block of the given height in the flow, as wide as the content area, drawn by a callback. It moves to the next page if it doesn't fit; the engine saves and restores the graphics state around the callback.
Drawing(float height, ContentStreamPainter painter)
interface ContentStreamPainter { void paint(PDPageContentStream cs, PDRectangle box) throws IOException; }
PDFBox drawing in PDF coordinates (origin at the bottom left); box is the block's rectangle.
graphics2D
static Drawing graphics2D(float height, Graphics2DPainter painter)
interface Graphics2DPainter { void paint(Graphics2D g, float width, float height) throws IOException; }
Java2D drawing, with the origin at the top left of the block, in points. The output is vector graphics. Text in the SansSerif, Dialog, Arial or Helvetica fonts is written as real text in Liberation Sans; other fonts become outlines. Text metrics come from AWT. A drawing taller than the content area is scaled down to fit.
PageDecorator
decorate
void decorate(PageContext context) throws IOException
Called at save() for each page. An exception stops save().
PageContext
The page a PageDecorator draws on, in PDF points from the page's bottom-left corner.
getPageNumber
int getPageNumber()
int getPageCount()
getContentStream
PDPageContentStream getContentStream()
PDFBox drawing over the page.
getPageRectangle
PDRectangle getPageRectangle()
PDRectangle getContentRectangle()
The whole page, and the area inside its margins.
showText
void showText(String text, float x, float y, Font font, int alignment) throws IOException
One line of text with its baseline at y, its left end, center or right end at x for ALIGN_LEFT, ALIGN_CENTER or ALIGN_RIGHT.
Element
The interface of everything Document.add() accepts.
Element constants
int ALIGN_LEFT, ALIGN_CENTER, ALIGN_RIGHT, ALIGN_JUSTIFIED, ALIGN_TOP, ALIGN_MIDDLE, ALIGN_BOTTOM