UNPKG

chromiumly

Version:

A lightweight Typescript library that interacts with Gotenberg's different modules to convert a variety of document formats to PDF files.

962 lines (756 loc) 39.5 kB
<p align="center"> <img src="assets/logo.png" alt="Chromiumly Logo" width="128" height="auto"> </p> ![build](https://github.com/cherfia/chromiumly/actions/workflows/build.yml/badge.svg) [![coverage](https://img.shields.io/codecov/c/gh/cherfia/chromiumly?style=flat-square)](https://codecov.io/gh/cherfia/chromiumly) [![vulnerabilities](https://snyk.io/test/github/cherfia/chromiumly/badge.svg?targetFile=package.json&color=brightgreen&style=flat-square)](https://snyk.io/test/github/cherfia/chromiumly?targetFile=package.json) [![Maintainability](https://qlty.sh/gh/cherfia/projects/chromiumly/maintainability.svg)](https://qlty.sh/gh/cherfia/projects/chromiumly) [![npm](https://img.shields.io/npm/v/chromiumly?color=brightgreen&style=flat-square)](https://npmjs.org/package/chromiumly) [![downloads](https://img.shields.io/npm/dt/chromiumly.svg?color=brightgreen&style=flat-square)](https://npm-stat.com/charts.html?package=chromiumly) ![licence](https://img.shields.io/github/license/cherfia/chromiumly?style=flat-square) A lightweight TypeScript client for [Gotenberg](https://gotenberg.dev/)’s HTTP API. Use it against your own Gotenberg container or against the **Chromiumly hosted API**—same client, different backend. | | Self‑hosted [Gotenberg](https://github.com/gotenberg/gotenberg) | [Chromiumly hosted API](https://chromiumly.dev) | | :------------------------ | :------------------------------------------------------------------ | :------------------------------------------------------------------------------------------------- | | **What it is** | Official open‑source PDF stack (Docker image) | Managed service at [https://api.chromiumly.dev](https://api.chromiumly.dev) | | **What Chromiumly calls** | Documented Gotenberg routes (Chromium, LibreOffice, PDF engines, ) | Those same routes, **plus** [Templates](#templates-hosted-api-only) (not in open‑source Gotenberg) | | **Configuration** | `GOTENBERG_ENDPOINT` | `CHROMIUMLY_API_KEY` (no endpoint) | Everything in this README that maps to [gotenberg.dev](https://gotenberg.dev/) applies to **both** backends unless a section says otherwise. **[Templates](#templates-hosted-api-only) are Chromiumly hosted API only**—they are not a Gotenberg feature and do not work with a self‑hosted instance. ## API Key Authentication (Hosted API) Chromiumly provides a managed API at [https://api.chromiumly.dev](https://api.chromiumly.dev). Hosted API support was introduced in `chromiumly@5.0.0` and is available only in Chromiumly `5.0.0+`. If you prefer not to run Gotenberg yourself, you avoid Docker and server ops. Sign up at [https://chromiumly.dev](https://chromiumly.dev), get an API key, and call the same conversion routes as with self‑hosting. The hosted API also exposes **[Templates](#templates-hosted-api-only)** (invoice PDFs from structured data), which are **not** available on self‑hosted Gotenberg. When using the hosted API, **you do not need to set `GOTENBERG_ENDPOINT`**. Just provide your API key: ```bash CHROMIUMLY_API_KEY=your-api-key ``` Once the environment variable is set, Chromiumly will automatically send all requests to the hosted API using your key. You can also configure the hosted API programmatically without an endpoint: ```typescript import { Chromiumly } from "chromiumly"; Chromiumly.configure({ apiKey: "your-api-key", }); ``` ### Minimal usage example (hosted API) ```typescript import { UrlConverter } from "chromiumly"; async function run() { const urlConverter = new UrlConverter(); const buffer = await urlConverter.convert({ url: "https://www.example.com/", }); // Write the buffer to disk, send it over HTTP, etc. } run(); ``` # Table of Contents 1. [API Key Authentication (Hosted API)](#api-key-authentication-hosted-api) 2. [Getting Started](#getting-started) - [Installation](#installation) - [Prerequisites](#prerequisites) - [Configuration](#configuration) 3. [Authentication](#authentication) - [Basic Authentication](#basic-authentication) - [API Key Authentication](#api-key-authentication) - [Advanced Authentication](#advanced-authentication) 4. [Core Features](#core-features) - [Chromium](#chromium) - [URL](#url) - [HTML](#html) - [Markdown](#markdown) - [Screenshot](#screenshot) - [LibreOffice](#libreoffice) - [PDF Engines](#pdf-engines) - [Format Conversion](#format-conversion) - [Merging](#merging) - [PDF Rotation](#pdf-rotation) - [Metadata Management](#metadata-management) - [Bookmarks Management](#bookmarks-management) - [File Generation](#file-generation) - [System](#system) - [PDF Splitting](#pdf-splitting) - [PDF Flattening](#pdf-flattening) - [PDF Encryption](#pdf-encryption) - [Embedding Files](#embedding-files) - [Templates (hosted API only)](#templates-hosted-api-only) - [Watermark and stamp](#watermark-and-stamp) 5. [Usage Example](#snippet) ## Getting Started ### Installation Using npm: ```bash npm install chromiumly ``` Using yarn: ```bash yarn add chromiumly ``` ### Prerequisites If you are using the hosted API key option at [https://api.chromiumly.dev](https://api.chromiumly.dev), you **do not need Docker** or a local Gotenberg instance — the service is fully managed for you. If you prefer to self‑host Gotenberg, be sure you install [Docker](https://www.docker.com/) if you have not already done so. After that, you can start a default Docker container of [Gotenberg](https://gotenberg.dev/) as follows: ```bash docker run --rm -p 3000:3000 gotenberg/gotenberg:8 ``` ### Configuration Chromiumly supports configurations via both [dotenv](https://www.npmjs.com/package/dotenv) and [config](https://www.npmjs.com/package/config) configuration libraries or directly via code to add a Gotenberg endpoint to your project when you are **self‑hosting**. #### dotenv ```bash GOTENBERG_ENDPOINT=http://localhost:3000 ``` #### config ```json { "gotenberg": { "endpoint": "http://localhost:3000" } } ``` #### code ```typescript import { Chromiumly } from "chromiumly"; Chromiumly.configure({ endpoint: "http://localhost:3000" }); ``` ## Authentication ### Basic Authentication Gotenberg introduces basic authentication support starting from version [8.4.0](https://github.com/gotenberg/gotenberg/releases/tag/v8.4.0). Suppose you are running a Docker container using the command below: ```bash docker run --rm -p 3000:3000 \ -e GOTENBERG_API_BASIC_AUTH_USERNAME=user \ -e GOTENBERG_API_BASIC_AUTH_PASSWORD=pass \ gotenberg/gotenberg:8.4.0 gotenberg --api-enable-basic-auth ``` To integrate this setup with Chromiumly, you need to update your configuration as outlined below: ```bash GOTENBERG_ENDPOINT=http://localhost:3000 GOTENBERG_API_BASIC_AUTH_USERNAME=user GOTENBERG_API_BASIC_AUTH_PASSWORD=pass ``` Or ```json { "gotenberg": { "endpoint": "http://localhost:3000", "api": { "basicAuth": { "username": "user", "password": "pass" } } } } ``` Or ```typescript Chromiumly.configure({ endpoint: "http://localhost:3000", username: "user", password: "pass", }); ``` ### API Key Authentication API key authentication is primarily intended for the **hosted Chromiumly API** at [https://api.chromiumly.dev](https://api.chromiumly.dev). For setup and examples, see [API Key Authentication (Hosted API)](#api-key-authentication-hosted-api). When both API key and basic auth are configured, the API key takes precedence. ### Advanced Authentication To implement advanced authentication or add custom HTTP headers to your requests, you can use the `customHttpHeaders` option within the `configure` method. This allows you to pass additional headers, such as authentication tokens or custom metadata, with each API call. For example, you can include a Bearer token for authentication along with a custom header as follows: ```typescript const token = await generateToken(); Chromiumly.configure({ endpoint: "http://localhost:3000", customHttpHeaders: { Authorization: `Bearer ${token}`, "X-Custom-Header": "value", }, }); ``` ## Core Features Chromiumly wraps Gotenberg’s HTTP API: classes mirror the routes described in [Gotenberg’s docs](https://gotenberg.dev/docs/getting-started/introduction). Methods that take files accept a path `string`, `Buffer`, or `ReadStream` (e.g. `html`, `header`, `footer`, `markdown`). The **`Templates`** class is the exception—it talks only to the Chromiumly hosted API and is documented [below](#templates-hosted-api-only). ### Chromium There are three different classes that come with a single method (i.e.`convert`) which calls one of Chromium's [conversion routes](https://gotenberg.dev/docs/convert-with-chromium/convert-url-to-pdf) to convert `html` and `markdown` files, or a `url` to a `buffer` which contains the converted PDF file content. Similarly, a new set of classes have been added to harness the recently introduced Gotenberg [screenshot routes](https://gotenberg.dev/docs/convert-with-chromium/screenshot-url). These classes include a single method called `capture`, which allows capturing full-page screenshots of `html`, `markdown`, and `url`. #### URL ```typescript import { UrlConverter } from "chromiumly"; const urlConverter = new UrlConverter(); const buffer = await urlConverter.convert({ url: "https://www.example.com/", }); ``` ```typescript import { UrlScreenshot } from "chromiumly"; const screenshot = new UrlScreenshot(); const buffer = await screenshot.capture({ url: "https://www.example.com/", }); ``` #### HTML The only requirement is that the file name should be `index.html`. ```typescript import { HtmlConverter } from "chromiumly"; const htmlConverter = new HtmlConverter(); const buffer = await htmlConverter.convert({ html: "path/to/index.html", }); ``` ```typescript import { HtmlScreenshot } from "chromiumly"; const screenshot = new HtmlScreenshot(); const buffer = await screenshot.capture({ html: "path/to/index.html", }); ``` #### Markdown This route accepts an `index.html` file plus a markdown file. ```typescript import { MarkdownConverter } from "chromiumly"; const markdownConverter = new MarkdownConverter(); const buffer = await markdownConverter.convert({ html: "path/to/index.html", markdown: "path/to/file.md", }); ``` ```typescript import { MarkdownScreenshot } from "chromiumly"; const screenshot = new MarkdownScreenshot(); const buffer = await screenshot.capture({ html: "path/to/index.html", markdown: "path/to/file.md", }); ``` Each `convert()` method takes an optional `properties` parameter of the following type which dictates how the PDF generated file will look like. ```typescript type PageProperties = { singlePage?: boolean; // Print the entire content in one single page (default false) size?: { width: number | string; // Paper width (number in inches or string with units: 72pt, 96px, 1in, 25.4mm, 2.54cm, 6pc, default 8.5) height: number | string; // Paper height (number in inches or string with units: 72pt, 96px, 1in, 25.4mm, 2.54cm, 6pc, default 11) }; margins?: { top: number | string; // Top margin (number in inches or string with units: 72pt, 96px, 1in, 25.4mm, 2.54cm, 6pc, default 0.39) bottom: number | string; // Bottom margin (number in inches or string with units: 72pt, 96px, 1in, 25.4mm, 2.54cm, 6pc, default 0.39) left: number | string; // Left margin (number in inches or string with units: 72pt, 96px, 1in, 25.4mm, 2.54cm, 6pc, default 0.39) right: number | string; // Right margin (number in inches or string with units: 72pt, 96px, 1in, 25.4mm, 2.54cm, 6pc, default 0.39) }; preferCssPageSize?: boolean; // Define whether to prefer page size as defined by CSS (default false) printBackground?: boolean; // Print the background graphics (default false) omitBackground?: boolean; // Hide the default white background and allow generating PDFs with transparency (default false) landscape?: boolean; // Set the paper orientation to landscape (default false) scale?: number; // The scale of the page rendering (default 1.0) nativePageRanges?: { from: number; to: number }; // Page ranges to print }; ``` **Page Size and Margins Units** Both `size` and `margins` properties support two formats: 1. **Numeric values** (in inches): For backward compatibility, you can continue using numbers which represent inches. 2. **String values with units**: You can now specify explicit units using the following formats: - `pt` (points): e.g., `"72pt"` - `px` (pixels): e.g., `"96px"` - `in` (inches): e.g., `"1in"` - `mm` (millimeters): e.g., `"25.4mm"` - `cm` (centimeters): e.g., `"2.54cm"` - `pc` (picas): e.g., `"6pc"` **Examples:** ```typescript // Using numeric values (inches) properties: { size: { width: 8.5, height: 11 }, margins: { top: 0.5, bottom: 0.5, left: 1, right: 1 } } // Using string values with units properties: { size: { width: "210mm", height: "297mm" }, // A4 size margins: { top: "1cm", bottom: "1cm", left: "2cm", right: "2cm" } } // Mixing numeric and string values properties: { size: { width: 8.5, height: "11in" }, margins: { top: "10mm", bottom: 0.5, left: "72pt", right: 1 } } ``` In addition to the `PageProperties` customization options, the `convert()` method also accepts a set of parameters to further enhance the versatility of the conversion process. Here's an overview of the full list of parameters: ```typescript type ConversionOptions = { properties?: PageProperties; // Customize the appearance of the generated PDF pdfFormat?: PdfFormat; // Define the PDF format for the conversion pdfUA?: boolean; // Enable PDF for Universal Access for optimal accessibility (default false) userAgent?: string; // Customize the user agent string sent during conversion header?: PathLikeOrReadStream; // Specify a custom header for the PDF footer?: PathLikeOrReadStream; // Specify a custom footer for the PDF emulatedMediaType?: EmulatedMediaType; // Specify the emulated media type for conversion emulatedMediaFeatures?: EmulatedMediaFeature[]; // Override CSS media features (e.g., prefers-color-scheme). Default: None. waitDelay?: string; // Duration (e.g., '5s') to wait when loading an HTML document before conversion waitForExpression?: string; // JavaScript expression to wait before converting an HTML document into PDF waitForSelector?: string; // CSS selector to wait for before converting an HTML document into PDF until it matches a node extraHttpHeaders?: Record<string, string>; // Include additional HTTP headers in the request failOnHttpStatusCodes?: number[]; // List of HTTP status codes triggering a 409 Conflict response (default [499, 599]) failOnConsoleExceptions?: boolean; // Return a 409 Conflict response if there are exceptions in the Chromium console (default false) failOnResourceHttpStatusCodes?: number[]; // Return a 409 Conflict response if resource HTTP status code is in the list (default [499,599]) ignoreResourceHttpStatusDomains?: string[]; // Domains to exclude from resource HTTP status code checks (matches exact domains or subdomains) failOnResourceLoadingFailed?: boolean; // Return a 409 Conflict response if resource loading failed (default false) skipNetworkIdleEvent?: boolean; // Do not wait for Chromium network to be idle (default true) skipNetworkAlmostIdleEvent?: boolean; // Do not wait for Chromium network to be almost idle (default true) metadata?: Metadata; // Metadata to be written. cookies?: Cookie[]; // Cookies to be written. downloadFrom?: DownloadFrom; // Download a file from one or multiple URLs. Each URL must return a Content-Disposition header with a filename parameter. webhook?: WebhookOptions; // Request-level webhook headers for async callbacks. split?: SplitOptions; // Split the PDF file into multiple files. userPassword?: string; // Password for opening the resulting PDF(s). ownerPassword?: string; // Password for full access on the resulting PDF(s). embeds?: PathLikeOrReadStream[]; // Files to embed in the generated PDF. watermark?: PdfEngineWatermark; // Optional PDF-engine post-processing watermark (behind page content). stamp?: PdfEngineStamp; // Optional PDF-engine post-processing stamp (on top of page content). }; ``` ```typescript type DownloadFromEntry = { url: string; extraHttpHeaders?: Record<string, string>; embedded?: boolean; // Legacy flag, prefer field field?: "embedded" | "watermark" | "stamp" | ""; }; type DownloadFrom = DownloadFromEntry | DownloadFromEntry[]; type WebhookOptions = { webhookUrl: string; webhookErrorUrl: string; webhookMethod?: "POST" | "PUT" | "PATCH"; webhookErrorMethod?: "POST" | "PUT" | "PATCH"; webhookExtraHttpHeaders?: Record<string, string>; webhookEventsUrl?: string; }; ``` Optional `watermark` and `stamp` use the same multipart field names as [Gotenberg’s PDF-engine watermark/stamp](https://gotenberg.dev/docs/manipulate-pdfs/watermark-pdfs): text, image, or PDF sources, with JSON `options` depending on your configured engine (e.g. pdfcpu). See [Watermark PDFs](https://gotenberg.dev/docs/manipulate-pdfs/watermark-pdfs) and [Stamp PDFs](https://gotenberg.dev/docs/manipulate-pdfs/stamp-pdfs) in the official docs. ```typescript type PdfEngineWatermark = { source?: "text" | "image" | "pdf"; expression?: string; // Text, or filename of the uploaded asset when source is image or pdf pages?: string; // Page ranges (e.g. "1-3"); omit for all pages options?: Record<string, unknown>; // Serialized as JSON (engine-specific) file?: PathLikeOrReadStream | Buffer; // Required when source is image or pdf }; type PdfEngineStamp = { source?: "text" | "image" | "pdf"; expression?: string; pages?: string; options?: Record<string, unknown>; file?: PathLikeOrReadStream | Buffer; }; ``` #### Screenshot Similarly, the `capture()` method takes an optional `properties` parameter of the specified type, influencing the appearance of the captured screenshot file. ```typescript type ImageProperties = { format: "png" | "jpeg" | "webp"; //The image compression format, either "png", "jpeg" or "webp". quality?: number; // The compression quality from range 0 to 100 (jpeg only). omitBackground?: boolean; // Hide the default white background and allow generating screenshots with transparency. width?: number; // The device screen width in pixels (default 800). height?: number; // The device screen height in pixels (default 600). clip?: boolean; // Define whether to clip the screenshot according to the device dimensions (default false). }; ``` Furthermore, alongside the customization options offered by `ImageProperties`, the `capture()` method accommodates a variety of parameters to expand the versatility of the screenshot process. Below is a comprehensive overview of all parameters available: ```typescript type ScreenshotOptions = { properties?: ImageProperties; header?: PathLikeOrReadStream; footer?: PathLikeOrReadStream; emulatedMediaType?: EmulatedMediaType; emulatedMediaFeatures?: EmulatedMediaFeature[]; // Override CSS media features (e.g., prefers-color-scheme). Default: None. waitDelay?: string; // Duration (e.g, '5s') to wait when loading an HTML document before convertion. waitForExpression?: string; // JavaScript's expression to wait before converting an HTML document into PDF until it returns true. waitForSelector?: string; // CSS selector to wait for before converting an HTML document into PDF until it matches a node. extraHttpHeaders?: Record<string, string>; failOnHttpStatusCodes?: number[]; // Return a 409 Conflict response if the HTTP status code is in the list (default [499,599]) failOnConsoleExceptions?: boolean; // Return a 409 Conflict response if there are exceptions in the Chromium console (default false) failOnResourceHttpStatusCodes?: number[]; // Return a 409 Conflict response if resource HTTP status code is in the list (default [499,599]) ignoreResourceHttpStatusDomains?: string[]; // Domains to exclude from resource HTTP status code checks (matches exact domains or subdomains) failOnResourceLoadingFailed?: boolean; // Return a 409 Conflict response if resource loading failed (default false) skipNetworkIdleEvent?: boolean; // Do not wait for Chromium network to be idle (default true) skipNetworkAlmostIdleEvent?: boolean; // Do not wait for Chromium network to be almost idle (default true) optimizeForSpeed?: boolean; // Define whether to optimize image encoding for speed, not for resulting size. cookies?: Cookie[]; // Cookies to be written. downloadFrom?: DownloadFrom; // Download files from one or multiple URLs. webhook?: WebhookOptions; // Request-level webhook headers for async callbacks. userPassword?: string; // Password for opening the resulting PDF(s). ownerPassword?: string; // Password for full access on the resulting PDF(s). embeds?: PathLikeOrReadStream[]; // Files to embed in the generated PDF. }; ``` ### LibreOffice The `LibreOffice` class comes with a single method `convert`. This method interacts with [LibreOffice](https://gotenberg.dev/docs/convert-with-libreoffice/convert-to-pdf) route to convert different documents to PDF files. You can find the file extensions accepted [here](https://gotenberg.dev/docs/convert-with-libreoffice/convert-to-pdf). ```typescript import { LibreOffice } from "chromiumly"; const buffer = await LibreOffice.convert({ files: [ "path/to/file.docx", "path/to/file.png", { data: xlsxFileBuffer, ext: "xlsx" }, ], }); ``` Similarly to Chromium's route `convert` method, this method takes the following optional parameters : - `properties`: changes how the PDF generated file will look like. It also includes a `password` parameter to open the source file. - `pdfa`: PDF format of the conversion resulting file (i.e. `PDF/A-1a`, `PDF/A-2b`, `PDF/A-3b`). - `pdfUA`: enables PDF for Universal Access for optimal accessibility. - `merge`: merges all the resulting files from the conversion into an individual PDF file. - `metadata`: writes metadata to the generated PDF file. - `losslessImageCompression`: allows turning lossless compression on or off to tweak image conversion performance. - `reduceImageResolution`: allows turning on or off image resolution reduction to tweak image conversion performance. - `quality`: specifies the quality of the JPG export. The value ranges from 1 to 100, with higher values producing higher-quality images and larger file sizes. - `maxImageResolution`: specifies if all images will be reduced to the specified DPI value. Possible values are: `75`, `150`, `300`, `600`, and `1200`. - `initialView`: initial PDF view mode (`0`: none, `1`: outline, `2`: thumbnails). - `initialPage`: page number opened by default. - `magnification`: initial magnification mode (`0`: default, `1`: fit page, `2`: fit width, `3`: fit visible, `4`: explicit zoom). - `zoom`: initial zoom percentage when `magnification` is `4`. - `pageLayout`: initial layout (`0`: default, `1`: single page, `2`: one column, `3`: two columns). - `firstPageOnLeft`: place first page on the left when using two-column layout. - `resizeWindowToInitialPage`: resize the viewer window to the first page dimensions. - `centerWindow`: center the PDF viewer window on screen. - `openInFullScreenMode`: open the PDF in full-screen mode. - `displayPDFDocumentTitle`: display PDF title in viewer title bar instead of filename. - `hideViewerMenubar`: hide the viewer menu bar. - `hideViewerToolbar`: hide the viewer toolbar. - `hideViewerWindowControls`: hide viewer window controls. - `useTransitionEffects`: use transition effects for Impress slides. - `openBookmarkLevels`: number of bookmark levels opened on load (`-1` opens all levels). - `downloadFrom`: download files remotely (`DownloadFromEntry` or `DownloadFromEntry[]`). - `flatten`: a boolean that, when set to true, flattens the split PDF files, making form fields and annotations uneditable. - `userPassword`: password for opening the resulting PDF(s). - `ownerPassword`: password for full access on the resulting PDF(s). - `embeds`: files to embed in the generated PDF (repeatable). This feature enables the creation of PDFs compatible with standards like [ZUGFeRD / Factur-X](https://fnfe-mpe.org/factur-x/), which require embedding XML invoices and other files within the PDF. - `webhook`: request-level webhook headers for async callbacks. - **Native LibreOffice watermarks** (applied during export): `nativeWatermarkText`, `nativeWatermarkColor`, `nativeWatermarkFontHeight`, `nativeWatermarkRotateAngle`, `nativeWatermarkFontName`, `nativeTiledWatermarkText` — see [Convert to PDF](https://gotenberg.dev/docs/convert-with-libreoffice/convert-to-pdf). - **PDF-engine watermark/stamp** (post-processing after conversion): `watermark` and `stamp` — same shapes as in Chromium `ConversionOptions` (`PdfEngineWatermark` / `PdfEngineStamp`). For `{ data, ext }` file objects, use the same pattern as in `files`. ### PDF Engines The `PDFEngines` class interacts with Gotenberg's [PDF Engines](https://gotenberg.dev/docs/manipulate-pdfs/pdfa-pdfua) routes to manipulate PDF files. #### Format Conversion This method interacts with [PDF Engines](https://gotenberg.dev/docs/manipulate-pdfs/pdfa-pdfua) convertion route to transform PDF files into the requested PDF/A format and/or PDF/UA. ```typescript import { PDFEngines } from "chromiumly"; const buffer = await PDFEngines.convert({ files: ["path/to/file_1.pdf", "path/to/file_2.pdf"], pdfa: PdfFormat.A_2b, pdfUA: true, }); ``` #### Merging This method interacts with [PDF Engines](https://gotenberg.dev/docs/manipulate-pdfs/merge-pdfs) merge route which gathers different engines that can manipulate and merge PDF files such as: [PDFtk](https://gitlab.com/pdftk-java/pdftk), [PDFcpu](https://github.com/pdfcpu/pdfcpu), [QPDF](https://github.com/qpdf/qpdf), and [UNO](https://github.com/unoconv/unoconv). ```typescript import { PDFEngines } from "chromiumly"; const buffer = await PDFEngines.merge({ files: ["path/to/file_1.pdf", "path/to/file_2.pdf"], pdfa: PdfFormat.A_2b, pdfUA: true, }); ``` Optional `watermark` and `stamp` (`PdfEngineWatermark` / `PdfEngineStamp`) apply PDF-engine post-processing to the merged output, matching [Merge PDFs](https://gotenberg.dev/docs/manipulate-pdfs/merge-pdfs) in the Gotenberg docs. Optional `rotate` (`{ angle: 90 | 180 | 270; pages?: string }`) rotates pages after merge via the PDF engine; omit `pages` or leave it empty to rotate all pages. #### PDF Rotation `PDFEngines.rotate()` calls Gotenberg’s [rotate route](https://gotenberg.dev/docs/manipulate-pdfs/rotate-pdfs) to rotate existing PDFs. The same post-processing is available on `PDFEngines.merge()`, `PDFEngines.split()`, and on Chromium and LibreOffice `convert()` through the optional `rotate` property (Gotenberg generates the PDF, then rotates selected pages—an extra pass). ```typescript import { PDFEngines } from "chromiumly"; const rotated = await PDFEngines.rotate({ files: ["path/to/document.pdf"], angle: 90, pages: "1-3", // optional; omit for all pages }); ``` #### Watermark and stamp (dedicated routes) These methods call [`/forms/pdfengines/watermark`](https://gotenberg.dev/docs/manipulate-pdfs/watermark-pdfs) and [`/forms/pdfengines/stamp`](https://gotenberg.dev/docs/manipulate-pdfs/stamp-pdfs). ```typescript import { PDFEngines } from "chromiumly"; const watermarked = await PDFEngines.watermark({ files: ["path/to/document.pdf"], watermark: { source: "text", expression: "CONFIDENTIAL", options: { opacity: 0.25, rotation: 45 }, }, }); const stamped = await PDFEngines.stamp({ files: ["path/to/document.pdf"], stamp: { source: "text", expression: "APPROVED", options: { opacity: 0.5, rotation: 0 }, }, }); ``` #### Metadata Management ##### readMetadata This method reads metadata from the provided PDF files. ```typescript import { PDFEngines } from "chromiumly"; const metadataBuffer = await PDFEngines.readMetadata([ "path/to/file_1.pdf", "path/to/file_2.pdf", ]); ``` ##### writeMetadata This method writes metadata to the provided PDF files. ```typescript import { PDFEngines } from "chromiumly"; const buffer = await PDFEngines.writeMetadata({ files: ["path/to/file_1.pdf", "path/to/file_2.pdf"], metadata: { Author: "Taha Cherfia", Title: "Chromiumly", Keywords: ["pdf", "html", "gotenberg"], }, }); ``` Please consider referring to [ExifTool](https://exiftool.org/TagNames/XMP.html#pdf) for a comprehensive list of accessible metadata options. #### Bookmarks Management ##### readBookmarks This method reads bookmarks (outline / table of contents) from the provided PDF files. ```typescript import { PDFEngines } from "chromiumly"; const bookmarks = await PDFEngines.readBookmarks([ "path/to/file_1.pdf", "path/to/file_2.pdf", ]); ``` ##### writeBookmarks This method writes bookmarks to the provided PDF files. ```typescript import { PDFEngines } from "chromiumly"; const updated = await PDFEngines.writeBookmarks({ files: ["path/to/file_1.pdf"], bookmarks: [ { title: "Chapter 1", page: 1, children: [], }, ], }); ``` #### File Generation It is just a generic complementary method that takes the `buffer` returned by the `convert` method, and a chosen `filename` to generate the PDF file. Please note that all the PDF files can be found `__generated__` folder in the root folder of your project. ### PDF Splitting Each [Chromium](#chromium) and [LibreOffice](#libreoffice) route has a `split` parameter that allows splitting the PDF file into multiple files. The `split` parameter is an object with the following properties: - `mode`: the mode of the split. It can be `pages` or `intervals`. - `span`: the span of the split. It is a string that represents the range of pages to split. - `unify`: a boolean that allows unifying the split files. Only works when `mode` is `pages`. - `flatten`: a boolean that, when set to true, flattens the split PDF files, making form fields and annotations uneditable. ```typescript import { UrlConverter } from "chromiumly"; const buffer = await UrlConverter.convert({ url: "https://www.example.com/", split: { mode: "pages", span: "1-2", unify: true, }, }); ``` On the other hand, PDFEngines' has a `split` method that interacts with [PDF Engines](https://gotenberg.dev/docs/manipulate-pdfs/split-pdfs) split route which splits PDF files into multiple files. ```typescript import { PDFEngines } from "chromiumly"; const buffer = await PDFEngines.split({ files: ["path/to/file_1.pdf", "path/to/file_2.pdf"], options: { mode: "pages", span: "1-2", unify: true, }, }); ``` `PDFEngines.split` also accepts optional `watermark`, `stamp`, and `rotate` for the same PDF-engine post-processing as merge. > ⚠️ **Note**: Gotenberg does not currently validate the `span` value when `mode` is set to `pages`, as the validation depends on the chosen engine for the split feature. See [PDF Engines module configuration](https://gotenberg.dev/docs/configuration#pdf-engines) for more details. ### PDF Flattening PDF flattening converts interactive elements like forms and annotations into a static PDF. This ensures the document looks the same everywhere and prevents further edits. ```typescript import { PDFEngines } from "chromiumly"; const buffer = await PDFEngines.flatten([ "path/to/file_1.pdf", "path/to/file_2.pdf", ]); ``` ### PDF Encryption Each [Chromium](#chromium) and [LibreOffice](#libreoffice) route supports PDF encryption through the `userPassword` and `ownerPassword` parameters. The `userPassword` is required to open the PDF, while the `ownerPassword` provides full access permissions. ```typescript import { UrlConverter } from "chromiumly"; const buffer = await UrlConverter.convert({ url: "https://www.example.com/", userPassword: "my_user_password", ownerPassword: "my_owner_password", }); ``` ### Embedding Files Each [Chromium](#chromium) and [LibreOffice](#libreoffice) route supports embedding files into the generated PDF through the `embeds` parameter. This feature enables the creation of PDFs compatible with standards like [ZUGFeRD / Factur-X](https://fnfe-mpe.org/factur-x/), which require embedding XML invoices and other files within the PDF. You can embed multiple files by passing an array of file paths, buffers, or read streams: ```typescript import { HtmlConverter } from "chromiumly"; const htmlConverter = new HtmlConverter(); const buffer = await htmlConverter.convert({ html: "path/to/index.html", embeds: [ "path/to/invoice.xml", "path/to/logo.png", Buffer.from("additional data"), ], }); ``` All embedded files will be attached to the generated PDF and can be extracted using PDF readers that support file attachments. ### Templates (hosted API only) The `Templates` class is **not** part of open‑source Gotenberg. It generates PDFs from structured payloads on the Chromiumly hosted API and **requires `CHROMIUMLY_API_KEY`**. Hosted API features (including `Templates`) were introduced in `chromiumly@5.0.0` and require Chromiumly `5.0.0+`—pointing Chromiumly at `GOTENBERG_ENDPOINT` (self‑hosted Gotenberg) will not enable this feature. The following template types are currently available: | Type | Description | | -------------------- | ------------------ | | `invoice_saas` | SaaS-style invoice | | `invoice_freelancer` | Freelancer invoice | | `invoice_classic` | Classic invoice | | `invoice_minimal` | Minimal invoice | | `invoice_modern` | Modern invoice | #### Basic Usage ```typescript import { Templates } from "chromiumly"; const templates = new Templates(); const buffer = await templates.generate({ type: "invoice_saas", data: { invoiceNumber: "INV-319", createdDate: "2026-03-19", dueDate: "2026-04-02", companyLogo: "https://cdn.acmecloud.com/assets/logo-mark.png", sender: { name: "Acme Cloud LLC", addressLine1: "450 Madison Ave", addressLine2: "New York, NY 10022", }, receiver: { name: "Northwind Health Inc.", addressLine1: "221 Harbor Blvd", addressLine2: "San Diego, CA 92101", }, items: [ { description: "Platform Subscription (Annual)", qty: 1, unitPrice: "1500.00", amount: "1500.00", }, { description: "Onboarding", qty: 1, unitPrice: "300.00", amount: "300.00", }, ], currency: "USD", subTotal: "1800.00", taxRate: 8.25, taxAmount: "148.50", total: "1948.50", footerNote: "Payment due in 14 days.", footerDisclaimer: "Late fees may apply.", }, }); ``` #### Payload Validation Pass `{ validate: true }` to run runtime validation on the data before sending the request. An error is thrown if the payload does not match the expected structure for the given template type. ```typescript const buffer = await templates.generate(request, { validate: true }); ``` #### Payload Shape ```typescript type TemplateRequest<TType extends TemplateType> = { type: TType; data: TemplateDataByType[TType]; }; interface InvoiceSaasTemplateData { invoiceNumber: string; createdDate: string; dueDate: string; companyLogo: string; sender: TemplateParty; receiver: TemplateParty; items: InvoiceItem[]; currency: Currency; subTotal: string; taxRate: number; taxAmount: string; total: string; footerNote: string; footerDisclaimer: string; } interface TemplateParty { name: string; addressLine1: string; addressLine2: string; tax?: string; iban?: string; bic?: string; } interface InvoiceItem { description: string; qty: number; unitPrice: string; amount: string; } ``` The `currency` field accepts any [ISO 4217](https://en.wikipedia.org/wiki/ISO_4217) currency code exported as the `Currency` type (e.g. `"USD"`, `"EUR"`, `"GBP"`). ### Watermark and stamp Gotenberg can apply a **watermark** (behind content) and/or **stamp** (on top of content) using the configured PDF engine after the main conversion or PDF operation. Types `PdfEngineWatermark` and `PdfEngineStamp` are exported from `chromiumly` if you want them explicitly in your code. Chromiumly exposes this on: | API | What to pass | | ------------------------------------------------------------------ | ------------------------------------------------------------------------------------ | | `UrlConverter` / `HtmlConverter` / `MarkdownConverter` `convert()` | `watermark`, `stamp`, `rotate` on the options object (see `ConversionOptions` above) | | `LibreOffice.convert()` | Native fields (`nativeWatermarkText`, ) and/or `watermark`, `stamp`, `rotate` | | `PDFEngines.merge()` / `PDFEngines.split()` | Optional `watermark`, `stamp`, `rotate` | | `PDFEngines.rotate()` | Dedicated endpoint; `files`, `angle` (`90` \| `180` \| `270`), optional `pages` | | `PDFEngines.watermark()` / `PDFEngines.stamp()` | Dedicated endpoints; `watermark` or `stamp` config is required | For image or PDF sources, set `source` to `image` or `pdf`, set `expression` to the **filename** of the uploaded asset, and pass the file in `file`. Chromium screenshot routes do not document these fields; use HTML/CSS overlays or convert-to-PDF flows instead. ### System The `System` class exposes Gotenberg system endpoints: ```typescript import { System } from "chromiumly"; const health = await System.getHealth(); // GET /health const heartbeat = await System.headHealth(); // HEAD /health const version = await System.getVersion(); // GET /version const debug = await System.getDebug(); // GET /debug const metrics = await System.getPrometheusMetrics(); // GET /prometheus/metrics ``` ## Snippet The following is a short snippet of how to use the library. ```typescript import { PDFEngines, UrlConverter } from "chromiumly"; async function run() { const urlConverter = new UrlConverter(); const buffer = await urlConverter.convert({ url: "https://gotenberg.dev/", properties: { singlePage: true, size: { width: 8.5, height: 11, }, }, emulatedMediaType: "screen", emulatedMediaFeatures: [ { name: "prefers-color-scheme", value: "dark" }, { name: "prefers-reduced-motion", value: "reduce" }, ], failOnHttpStatusCodes: [404], failOnConsoleExceptions: true, skipNetworkIdleEvent: false, skipNetworkAlmostIdleEvent: false, optimizeForSpeed: true, split: { mode: "pages", span: "1-2", unify: true, }, }); await PDFEngines.generate("gotenberg.pdf", buffer); } run(); ```