CLI reference
Every option of the sghtmltopdf command.
sghtmltopdf [OPTIONS] <INPUT.HTML>
sghtmltopdf server [OPTIONS]
The first converts a file; the second runs the HTTP server.
For how these map to wkhtmltopdf’s options, including the full list of the ones that are not supported, see the wkhtmltopdf option mapping.
Basics
# The simplest form; the system fonts are used
sghtmltopdf invoice.html -o invoice.pdf
# Without an output path, the input name with the extension changed to .pdf
sghtmltopdf invoice.html
# Read from standard input and write to standard output
cat invoice.html | sghtmltopdf - -o - > invoice.pdf
Input and output
| Option | Default | Description |
|---|---|---|
<INPUT.HTML> | (required) | The input HTML; - means standard input |
-o, --output <PATH> | The input name with .pdf | Where to write. - means standard output. It cannot be omitted when reading from standard input |
--base-url <URL|DIR> | The directory of the input HTML | The base for resolving relative references. An http(s) URL becomes the default <base href>; a <base href> in the HTML wins |
--encoding <NAME> | Detected | The character encoding of the input. The order is BOM, then --encoding, then <meta charset>, then UTF-8 |
--streaming | Off | Process in streaming mode |
The output is written to a temporary file and then renamed, so a failure never leaves a broken PDF behind.
Page setup
| Option | Default | Description |
|---|---|---|
-s, --page-size <SIZE> | A4 | A3, A4, A5, Letter, or Legal, case insensitive |
--page-width <LENGTH> | The paper width; takes precedence over --page-size | |
--page-height <LENGTH> | The paper height; takes precedence over --page-size | |
-O, --orientation <O> | Portrait | Landscape swaps width and height at the end |
-T, --margin-top <LENGTH> | 1in (96px) | Top margin |
-B, --margin-bottom <LENGTH> | 1in | Bottom margin |
-L, --margin-left <LENGTH> | 1in | Left margin |
-R, --margin-right <LENGTH> | 1in | Right margin |
Lengths take the units mm, cm, in, pt, and px. A bare number means mm, as in wkhtmltopdf.
How this relates to
@pagein CSSThese options are initial values. If the CSS in the HTML says
@page { size: … }or@page { margin: … }, the CSS wins, property by property. Note that this is the opposite of wkhtmltopdf.
Fonts
| Option | Description |
|---|---|
--font <PATH> | The font to use; may be given more than once. Without it, system fonts are used |
--font-index <N> | The face index inside a TrueType Collection (.ttc), for the preceding --font |
--gothic-font <PATH> (with --gothic-font-index) | The font behind font-family: sans-serif |
--serif-font <PATH> (with --serif-font-index) | The font behind font-family: serif |
--mono-font <PATH> (with --mono-font-index) | The font behind font-family: monospace |
--disable-system-fonts | Never search the system fonts; typeset with only the fonts you passed |
Fonts are resolved in the order --font, then @font-face, then a system search by font-family name. Only if none of those finds anything does a system sans-serif candidate become the default font.
Without
--font, the output depends on the fonts of the machine it runs on. To keep output stable on a server or in CI, name the fonts with--fontor@font-face. See Fonts for details.
Even when you name fonts with --font, a face that is missing from them (the italic of font-family: serif, for example) is filled in from the system, so the output still depends on the machine it runs on. --disable-system-fonts stops that search altogether, so the same HTML gives the same PDF on any machine; characters that could not be covered are reported as a warning and left undrawn.
PDF output and metadata
| Option | Default | Description |
|---|---|---|
--title <TEXT> | The <title> of the HTML | The /Title of the PDF |
--author, --subject, --keywords <TEXT> | The matching entries of the Info dictionary; specific to sghtmltopdf | |
-d, --dpi <DPI> | 96 | How many dpi a CSS px stands for. At 72, 1px equals 1pt |
--zoom <FACTOR> | 1.0 | A scale factor, multiplied into the --dpi factor |
-g, --grayscale | Off | Convert fills and strokes to greyscale, by sRGB relative luminance |
--no-pdf-compression | Off | Turn off Flate compression of PDF objects; image data is unaffected |
/Producer and /CreationDate are always written.
The limits of greyscale
JPEG images, which pass through as
/DCTDecode, and CMYK images have no decoder here, so they stay in colour.
What gets drawn
| Option | Description |
|---|---|
--no-images | Do not load <img> or the CSS background-image |
--no-background | Do not paint element backgrounds, neither colour nor image |
--user-style-sheet <PATH> | CSS in the user origin; may be given more than once. Stronger than the UA stylesheet, weaker than the author’s CSS |
--minimum-font-size <PX> | A lower bound on the computed font-size |
--disable-external-links | Do not create PDF annotations for external http(s) links |
--disable-internal-links | Do not create PDF annotations for internal #id links |
--keep-relative-links | Write relative external links as they are, without making them absolute |
--load-media-error-handling <ignore|abort> | What to do when an image, stylesheet, or font cannot be fetched; ignore by default |
Headers and footers
There are two ways to do this. If both are given for the same side, --header-html wins.
1. As text
These map onto the margin boxes of @page.
sghtmltopdf report.html \
--header-center "Quarterly report" \
--footer-right "[page] / [topage]" \
--header-line
| Option | Description |
|---|---|
--header-left, --header-center, --header-right <TEXT> | The three positions across the header |
--footer-left, --footer-center, --footer-right <TEXT> | The three positions across the footer |
--header-font-name, --header-font-size | The header font; the footer has the same pair |
--header-line, --footer-line | Draw a rule |
--header-spacing, --footer-spacing <MM> | The gap from the body text; the margin grows by that much |
--default-header | A default header with the title and the page number |
--replace <NAME=VALUE> | Replace any [NAME] with a value; may be given more than once |
The placeholders are [page] for the current page, [topage] for the total page count, [frompage], [title] and [doctitle], [date], [time], and any name you define with --replace.
[section], [subsection], [webpage], [sitepage], and [sitepages] are not supported.
2. As HTML
An HTML string can be passed directly with header_html_content: / footer_html_content: (CLI: --header-html-content / --footer-html-content), with no temporary file. header_html: / footer_html: still take a file path. Giving both a path and a string for the same side is an error. Placeholders, images and margins behave the same as with a file. The HTTP server accepts them too, but the HTML then travels in the query string, so mind URL length limits and access logs.
sghtmltopdf report.html --header-html header.html --footer-html footer.html
A separate HTML file is rendered into the margin area of every page. Placeholders are substituted in the HTML as text; JavaScript is not executed.
- Anything that does not fit in the margin is clipped; the margin does not grow to accommodate it
- Images embedded as
data:URLs work in<img>andbackground-image(PNG, JPEG, WebP, SVG; SVG needs thesvgfeature).--no-imagesapplies here too - No external resources are fetched. Images from local files or remote URLs, and external CSS, are not supported
@font-faceinside the header or footer HTML is not loaded. Name any font used only there with--font
Cover page and table of contents
sghtmltopdf report.html --cover cover.html --toc --footer-center "[page]"
They are written in the order cover, table of contents, body.
| Option | Default | Description |
|---|---|---|
--cover <PATH> | The HTML to use as the cover. It is not counted in the page numbers and gets no header or footer | |
--toc | Off | Insert a table of contents before the body; not available in streaming mode |
--toc-header-text <TEXT> | Table of Contents | The <h1> of the table of contents |
--toc-level-indentation <WIDTH> | 1em | The indent added per level |
--toc-text-size-shrink <REAL> | 0.8 | The text size ratio applied per level |
--disable-dotted-lines | (drawn) | Do not draw the dotted leader under each entry |
--disable-toc-links | (linked) | Do not link the entries to their headings |
--enable-toc-back-links | (not linked) | Link each heading back to the table of contents |
--page-offset <N> | 0 | Shift where the page numbering starts |
The HTML structure and default styling of the table of contents follow what wkhtmltopdf’s default TOC XSL produces: nested <ul> for the levels, and <div><a>heading</a><span>page number</span></div> for each entry. To change how it looks, apply CSS with --user-style-sheet; XSLT is not supported.
Headings are collected from h1 through h6. A heading without an id is given a generated destination name.
Access control
| Option | CLI default | Server default |
|---|---|---|
--enable-local-file-access, --disable-local-file-access | Allowed | Denied |
--allow-path <PATH> | No restriction | No restriction |
--allow-remote-assets | Denied | Denied |
Given one or more --allow-path directories, local references are confined to those directories. This applies to <img src>, external CSS, and @font-face alike.
Ranges
References outside the base directory
Local references stay inside the base directory (--base-url, defaulting to the directory the input HTML lives in) by default. A ../ that would escape it is an error. This keeps untrusted HTML from reading arbitrary files through a reference such as <img src="../../../../etc/passwd">.
A ../ that resolves within the base directory, such as assets/../images/logo.png, keeps working as before.
Name the range explicitly with --allow-path when you mean to reference a file outside it. Once --allow-path is given, the boundary is the allowed directories rather than the base directory.
$ sghtmltopdf pages/index.html -o out.pdf
error: ../images/logo.png: the reference points outside the base directory (pages).
To read files outside it, name the directory with --allow-path
$ sghtmltopdf pages/index.html --allow-path . -o out.pdf
The check is lexical, so symlinks under the base directory are followed. Use --allow-path when you want the boundary to hold across symlinks as well (that check resolves real paths).
A reference starting with /
A reference starting with /, such as /assets/logo.png, is first resolved relative to the site root, that is, to the base directory (<base directory>/assets/logo.png). That is the shape the Rails asset pipeline emits, so pointing the base directory at a precompiled public/ resolves it as it is.
Only when there is no file there is the same string read again as a filesystem path. It is the fallback for a document written as <img src="/var/www/app/public/logo.png">. What may be read is then decided by the same rules as for any other reference: an absolute path inside the base directory is read as it is, one outside it needs --allow-path.
When neither reading finds a file, the error names both paths.
Limits on input size
HTML with more than roughly 500,000 nodes is rejected. Computed styles, the box tree and the layout result all grow in proportion to the node count; measured at 472 B to 1210 B per node.
Even a document of several thousand pages stays in the hundreds of thousands of nodes, so real documents practically never hit this. If you do hit it, split the document or use streaming mode. Streaming releases each processed part as it goes, so it can convert documents whose total exceeds the limit.
Memory that scales with the amount of text is not covered by this limit (three elements holding 10 MiB of text still use about 1.7 GiB). In HTTP server mode --max-body-size plays that role.
Logging and exit codes
--log-level <none|error|warn|info>, info by default, and -q or --quiet, which is the same as --log-level none.
| Code | Meaning |
|---|---|
| 0 | Success |
| 1 | Usage error: an unknown option, a malformed value, or an option that is not supported |
| 2 | Input or resource error: a missing file, a font that cannot be read, or a failed fetch under abort |
| 3 | Rendering error, such as breaking one of the streaming mode restrictions |
| 4 | Time limit exceeded (HTTP server mode’s --timeout only; the CLI has no time limit, so this never appears) |