PDF Maker is a library for generating PDF documents in JavaScript.
npm install pdf-maker
A PdfMaker
instance creates PDF data from a given document
definition.
// Define the contents of the document
const doc = {
content: [text('Hello World!')],
};
// create an instance of PdfMaker and register fonts
const pdfMaker = new PdfMaker();
pdfMaker.registerFont(await readFile('path/to/Roboto-Regular.ttf'));
// create a PDF from the document
const pdfData = await pdfMaker.makePdf(doc);
await writeFile(`hello.pdf`, pdfData);
Fonts need to be registered before they can be used in a document. The
registerFont()
method accepts font data in TTF
or OTF
format.
Fonts will be selected based on their font family, font style, and font
weight. This information is extracted from the font by registerFont()
,
it but can also be provided as a second parameter if needed.
pdfMaker.registerFont(await readFile('path/to/Roboto-Regular.ttf'));
pdfMaker.registerFont(await readFile('path/to/Roboto-Italic.ttf'));
...
const doc = {
// Define the default font
defaultStyle: { fontFamily: 'Roboto', fontSize: 12 },
content: [
// This block will use the Roboto-Regular font
text('Hello World!')
// This block will use the Roboto-Italic font
text('Hello World!', { fontStyle: 'italic' })
],
};
If no font family is specified in the document, the first matching font will be used.
The content of a document is composed of blocks. There are different
types of blocks, such as text blocks, image blocks, column and row
layout blocks. The content
property of the document definition accepts
a list of blocks.
Text blocks can be created using the text()
function. Besides the
text to display, this function accepts block and
text properties.
const block = text('Lorem ipsum', { fontStyle: 'italic', fontSize: 12 });
The text property can be a single string or an array of strings or text
spans. A text span is an inline stretch of text with a specific style,
such as an emphasized word or a link. Text spans can be created using
the span()
function. Text spans support text
properties.
const block = text([
'This is some text with an ',
span('emphasized text span', { fontStyle: 'italic' }),
' in the middle.',
]);
This block will result in the following text:
This is some text with an emphasized text span in the middle.
Text spans can also be nested:
span(['This is ', span('super', { fontWeight: 'bold' }), ' important'], { fontStyle: 'italic' });
This is super important.
For convenience, the functions bold()
and italic()
can be used to
create bold and italic text spans:
italic(['This is ', bold('super'), ' important']);
Line breaks are inserted automatically at word boundaries. Explicit
line breaks can be inserted as line feed characters (\n
):
text('Explicit line\nbreaks are\nsupported.');
Subscripts and superscripts can be created using the span()
function
with the rise
property. This property does not affect the line height.
text(['H', span('₂', { rise: -3 }), 'O']);
text(['10', span('⁻³', { rise: 3 }), '.']);
Text spans can also be used to create links to external URLs or to anchors in the document.
span('example.com', { link: 'https://example.com', color: 'blue' });
Text alignment can be set using the textAlign
property. The value can
be left
, center
, or right
. The default is left
.
text('Centered text', { textAlign: 'center' });
The text alignment can be defined for an entire block and is propagated to all children in the block. Children can override the alignment.
rows(
[
text("I'm centered."),
text("I'm centered."),
text("I'm not!", { textAlign: 'left' }),
],
{ textAlign: 'center' },
),
Text properties can be set for an entire text block or for individual text spans. The following text properties are supported:
fontFamily
: The font family to use.fontStyle
: The font style. Can benormal
,italic
, orbold
.fontWeight
: The font weight. Can be a number between100
and900
. The literal valuesnormal
andbold
are also supported.fontSize
: The font size in pt.lineHeight
: The line height as a multiple of the font size (default:1.2
).color
: The text color.link
: Renders the text as a link to the given target. Can be a URL or an anchor reference.rise
: Vertical offset in pt for baseline shifts. Positive values shift the baseline up, negative values shift it down.letterSpacing
: The character spacing in pt. Positive values increase the space between characters, negative values decrease it.
Images can be included in image blocks, which can be created using the
image()
function. This function accepts the image URL and an optional
object containing image and
block properties. Images are supported in JPG and
PNG format. URLs can be data:
, http:
, https:
, or file:
URLs. The
size of an image can be confined using the width
and height
properties.
const block = image('file:/images/logo.png', { width: 200, height: 100 });
When the same image URL is used multiple times in the document, the image data is embedded in the PDF only once.
imageAlign
: Aligns the image within the block. The alignment of the image within the block. Supported values areleft
,center
, andright
. The default iscenter
.
Each block can have a graphics
property that accepts a list of
shapes to draw into that block. Alternatively, this property accepts a
function that returns a list of shapes. The function will be called with
the block's width and height. This can be used to draw shapes that
depend on the block's size. The coordinate system for graphics shapes
starts at the top left corner of the block.
Shapes can be lines, rectangles, circles, or SVG paths. They can be
created using the line()
, rect()
, circle()
, and path()
functions.
In the following example, the graphics
property is used to draw a
yellow background behind the text and a blue border at the left edge.
text('Lorem ipsum', {
graphics: ({ width, height }) => [
rect(0, 0, width, height, { fillColor: 'yellow' }),
line(0, 0, 0, height, { lineColor: 'blue', lineWidth: 2 }),
],
padding: { left: 5 },
});
Lines are defined by the coordinates of the start and end points of the line. They support stroke and transform properties.
line(10, 20, 90, 20, { lineColor: 'blue', lineWidth: 2 });
Rectangles are defined by the coordinates of the top-left corner, the width, and height of the rectangle. They support stroke, fill, and transform properties.
rect(10, 20, 50, 25, { lineColor: '#4488cc', lineJoin: 'round' });
Circles are defined by the coordinates of the center and the radius of the circle. They support stroke, fill, and transform properties.
circle(cx, cy, 20, { fillColor: 'red' });
Paths allow to create arbitrary shapes using a series of drawing commands. These commands are accepted in the format of an SVG path data string (see MDN for details). Paths support stroke, fill, and transform properties.
path('M45,0 L15,94 L90,34 L0,34 L75,94 Z', {
fillColor: '#4488cc',
translate: { x: 100 },
});
Stroke properties define the appearance of stroked lines. The following stroke properties are supported:
lineWidth
: The width of stroked lines in pt.lineColor
: The color of stroked lines.lineOpacity
: The opacity of stroked lines as a number between0
and1
.lineCap
: The shape at the end of open paths when they are stroked.butt
: indicates that the stroke for each subpath does not extend beyond its two endpoints. On a zero length subpath, the path will not be rendered at all.round
: indicates that at the end of each subpath the stroke will be extended by a half circle with a diameter equal to the stroke width. On a zero length subpath, the stroke consists of a full circle centered at the subpath's point.square
: indicates that at the end of each subpath the stroke will be extended by a rectangle with a width equal to half the width of the stroke and a height equal to the width of the stroke. On a zero length subpath, the stroke consists of a square with its width equal to the stroke width, centered at the subpath's point.
lineJoin
: The shape to be used at the corners of paths or basic shapes when they are stroked.miter
: indicates that the outer edges of the strokes for the two segments should be extended until they meet at an angle, as in a picture frame.round
: indicates that the outer edges of the strokes for the two segments should be rounded off by a circular arc with a radius equal to half the line width.bevel
: indicates that the two segments should be finished with butt caps and the resulting notch should be filled with a triangle.
lineDash
(number[]
): The dash pattern to use for drawing paths. Each element defines the length of a dash or a gap, in pt, starting with the first dash. If the array contains an odd number of elements, then the elements are repeated to yield an even number of elements. An empty array stands for no dash pattern, i.e. a continuous line.
Fill properties define the appearance of the areas enclosed by paths or basic shapes. The following fill properties are supported:
fillColor
: The color to use for filling the shape.fillOpacity
: The opacity to use for filling the shape as number between0
and1
.
Transform properties can be used to move, resize, rotate, or transform shapes in other ways. The following transform properties are supported:
translate
({ x: number, y: number }
): Moves the shape byx
andy
pt.scale
({ x: number, y: number }
): Stretches the shape byx
andy
pt.rotate
({ angle: number, cx?: number, cy?: number }
): Rotates the shape byangle
degrees clockwise about the point[cx,cy]
. Ifcx
andcy
are omitted, the rotation is about the origin of the coordinate system.skew
({ x: number, y: number }
): Skews the shape byx
degrees along the x axis and byy
degrees along the y axis.matrix
(number[]
): Applies a custom transformation matrix to the shape. The matrix is given as an array of six values[a, b, c, d, e, f]
that represent the transformation matrix:| a c e | | b d f | | 0 0 1 |
Colors can be specified in the format #rrggbb
where rr
, gg
, and
bb
are the red, green, and blue components respectively, in
hexadecimal notation.
In addition, the named colors black
, gray
, white
, red
, blue
,
green
, cyan
, magenta
, yellow
, lightgray
, darkgray
are
supported.
To arrange blocks horizontally, they can be included in a columns
block. The width of each column can be set using the width
property.
When the width is set to auto
, the column will shrink to the width of
its content. The remaining space is distributed evenly across all
columns that don't have a fixed width.
Column blocks can be created using the columns()
function, which takes
an array of blocks and an optional object with
block and text properties. Text
properties will be inherited by included text blocks.
const block = columns([
text('Column 1', { width: 100 }), // 100 pt wide
text('Column 2'), // gets half of the remaining width
text('Column 3'), // gets half of the remaining width
]);
To arrange blocks vertically, they can be included in a rows block. This can be useful to group multiple rows into a single block, e.g. to apply common properties or to enclose rows in a surrounding columns layout.
Row blocks can be created using the rows()
function, which takes an
array of blocks and an optional object with
block and text properties. Text
properties will be inherited by included text blocks.
const block = rows(
[text('Row 1'), text('Row 2'), text('Row 3')],
{ margin: 10, fontSize: 18 }, // fontSize is applied to all rows
);
The margin
property can be used to add space around blocks. It
accepts either a single value (applies to all four edges) an object with
any of the properties top
, right
, bottom
, left
, x
, and y
.
The properties x
and y
can be used as shorthands to set both left
and right
, or top
and bottom
, respectively. Values can be given
as numbers (in pt) or as strings with a unit. If a string is given, it
must contain one of the units pt
, in
, mm
, or cm
;
text('Lorem ipsum', { margin: 5 }); // 5 pt margin on all sides
text('Lorem ipsum', { margin: { y: '5cm' } }); // 5 cm top and bottom margin
The top
and bottom
margins of adjacent blocks
are collapsed into a single margin whose size is the maximum of the two
margins. Column margins don't collapse.
rows([
text('Lorem ipsum', { margin: { y: 5 } }),
// only 5 pt margin between the two blocks
text('dolor sit amet', { margin: { y: 5 } }),
]);
The padding
property can be used to add space between the content and
the edges of blocks. It accepts the same values as the margin
property.
text('Lorem ipsum', { padding: { x: '5pt', y: '2pt' });
The following properties can be set for all types of blocks:
padding
: Space to leave between the content and the edges of the block.margin
: Space to surround the block.width
: The width of the block. If this property is set toauto
, the block will use the width of the widest element in the block.height
: The height of the block. If this property is not set, the height of the block is defined by its content.verticalAlign
: Aligns the block vertically within a columns block. Supported values aretop
,middle
, andbottom
. By default, blocks are top-aligned.id
: An optional unique id for the element that can be used as an anchor.graphics
: A list of graphic elements to draw in the area covered by the block. A function can be passed to take the final size of the block into account.breakBefore
: Controls whether a page break may occur before the block.auto
(default): Insert a page break when needed.always
: Always insert a page break before this block.avoid
: Do not insert a page break before this block if it can be avoided.
breakAfter
: Controls whether a page break may occur after the block.auto
(default): Insert a page break when needed.always
: Always insert a page break after this block.avoid
: Do not insert a page break after this block if it can be avoided.
The top-level pageSize
property can be used to set the page size.
Various standard sizes are supported, such as A4
, Letter
, and
Legal
. The default is A4. A custom page size can be specified as an
object with the properties width
and height
. Values can be given as
numbers (in pt) or as strings with a unit.
const document = {
pageSize: { width: '20cm', height: '20cm' }
content: [text('Lorem ipsum')],
};
The pageOrientation
property can be used to set the page orientation.
The value can be either portrait
or landscape
. The default is
portrait.
const document = {
pageSize: 'A5',
pageOrientation: 'landscape',
content: [text('Lorem ipsum')],
};
Headers and footers that repeat on each page can be defined using the
optional header
and footer
properties. Both accept either a single
block or a function that returns a block. The function will be called
with the page number and the total number of pages. The page number
starts at 1.
const document = {
footer: ({ pageNumber, pageCount }) =>
text(`Page ${pageNumber} of ${pageCount}`, {
textAlign: 'right',
margin: { x: '20mm', bottom: '7mm' },
}),
content: [text('Lorem ipsum'), text('dolor sit amet')],
};
Page breaks are included automatically. When a block does not fit on the
current page, a new page is added to the document. To insert a page
break before or after a block, set the breakBefore
or breakAfter
property of a block to always
. To prevent a page break, set this
property to avoid
.
Page breaks are also automatically inserted between the lines of a text
block. To prevent a page break within a text block, set the
breakInside
property to avoid
.
const document = {
content: [
text('Lorem ipsum'),
text('This text will go on a new page', { breakBefore: 'always' }),
],
};
Anchors can be used to create links to other locations in the document.
An anchor is created by setting a unique id
property to the block that
should be the target of the link:
text('Section Two', { id: 'section2' });
An internal reference to this anchor can be created by setting the
link
property of a text span to a hash sign (#
), followed by the
id
of the target block:
text([
'See ',
span('Section Two', { link: '#section2' }), // Link to a section
' for more information.'
]);
...
PDF documents can include metadata such as the title, author, subject,
and keywords. This information can be set using the info
property of
the document.
const document = {
info: {
title: 'Invoice Dec 2024',
author: 'John Doe',
},
content: [text('Hello World!')],
};
The following properties are supported:
title
: The document’s title.author
: The name of the person who created the document.subject
: The subject of the document.keywords
: Keywords associated with the document.creationDate
: The date and time the document was created. If not set, the current time is used.modificationDate
: The date and time the document was last modified. If not set, the current time is used.creator
: The name of the application that created the original content.producer
: The name of the application that converted the original content into a PDF.
This feature can be used to visually inspect the structure and layout of blocks in the PDF document during development. When enabled, it includes some visual guides in the generated PDF:
- Each block is rendered with a gray border to indicate its bounds.
- Page headers and footers are separated from the page content by a horizontal line.
- Each line of text is surrounded by a thin green border. Another thin green line indicates the text baseline.
- Margins and paddings get a semi-transparent overlay. Margins are shown in yellow, paddings in purple. Overlapping margins will result in a darker shade of yellow.
To enable visual debugging, set the dev.guides
property in the
document to true
:
const document = {
dev: { guides: true }, // Enable visual debugging
content: [
text('Hello World!'),
],
...
};
This project is inspired by pdfmake and builds on pdf-lib and fontkit. It would not exist without the great work and the profound knowledge contributed by the authors of those projects.