{"id":13705203,"url":"https://github.com/cherfia/chromiumly","last_synced_at":"2026-04-01T21:54:46.766Z","repository":{"id":37834501,"uuid":"472728976","full_name":"cherfia/chromiumly","owner":"cherfia","description":"A lightweight Typescript library that interacts with Gotenberg's different modules to convert a variety of document formats to PDF files.","archived":false,"fork":false,"pushed_at":"2025-05-16T06:20:57.000Z","size":2101,"stargazers_count":106,"open_issues_count":4,"forks_count":12,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-05-16T10:04:27.034Z","etag":null,"topics":["chromium","docx","gotenberg","html","libreoffice","markdown","pdf","pptx","screenshots","xlsx"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/cherfia.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2022-03-22T11:04:55.000Z","updated_at":"2025-05-15T22:23:03.000Z","dependencies_parsed_at":"2023-09-24T06:44:28.548Z","dependency_job_id":"44a70248-c13d-491f-a089-6bc981b59cfd","html_url":"https://github.com/cherfia/chromiumly","commit_stats":null,"previous_names":[],"tags_count":49,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cherfia%2Fchromiumly","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cherfia%2Fchromiumly/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cherfia%2Fchromiumly/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cherfia%2Fchromiumly/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/cherfia","download_url":"https://codeload.github.com/cherfia/chromiumly/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254509476,"owners_count":22082891,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["chromium","docx","gotenberg","html","libreoffice","markdown","pdf","pptx","screenshots","xlsx"],"created_at":"2024-08-02T22:00:35.391Z","updated_at":"2026-04-01T21:54:46.480Z","avatar_url":"https://github.com/cherfia.png","language":"TypeScript","funding_links":[],"categories":["TypeScript","Clients"],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n  \u003cimg src=\"assets/logo.png\" alt=\"Chromiumly Logo\" width=\"128\" height=\"auto\"\u003e\n\u003c/p\u003e\n\n![build](https://github.com/cherfia/chromiumly/actions/workflows/build.yml/badge.svg)\n[![coverage](https://img.shields.io/codecov/c/gh/cherfia/chromiumly?style=flat-square)](https://codecov.io/gh/cherfia/chromiumly)\n[![vulnerabilities](https://snyk.io/test/github/cherfia/chromiumly/badge.svg?targetFile=package.json\u0026color=brightgreen\u0026style=flat-square)](https://snyk.io/test/github/cherfia/chromiumly?targetFile=package.json)\n[![Maintainability](https://qlty.sh/gh/cherfia/projects/chromiumly/maintainability.svg)](https://qlty.sh/gh/cherfia/projects/chromiumly)\n[![npm](https://img.shields.io/npm/v/chromiumly?color=brightgreen\u0026style=flat-square)](https://npmjs.org/package/chromiumly)\n[![downloads](https://img.shields.io/npm/dt/chromiumly.svg?color=brightgreen\u0026style=flat-square)](https://npm-stat.com/charts.html?package=chromiumly)\n![licence](https://img.shields.io/github/license/cherfia/chromiumly?style=flat-square)\n\nA 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.\n\n|                           | Self‑hosted [Gotenberg](https://github.com/gotenberg/gotenberg)     | [Chromiumly hosted API](https://chromiumly.dev)                                                    |\n| :------------------------ | :------------------------------------------------------------------ | :------------------------------------------------------------------------------------------------- |\n| **What it is**            | Official open‑source PDF stack (Docker image)                       | Managed service at [https://api.chromiumly.dev](https://api.chromiumly.dev)                        |\n| **What Chromiumly calls** | Documented Gotenberg routes (Chromium, LibreOffice, PDF engines, …) | Those same routes, **plus** [Templates](#templates-hosted-api-only) (not in open‑source Gotenberg) |\n| **Configuration**         | `GOTENBERG_ENDPOINT`                                                | `CHROMIUMLY_API_KEY` (no endpoint)                                                                 |\n\nEverything 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.\n\n## API Key Authentication (Hosted API)\n\nChromiumly 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.\n\nWhen using the hosted API, **you do not need to set `GOTENBERG_ENDPOINT`**. Just provide your API key:\n\n```bash\nCHROMIUMLY_API_KEY=your-api-key\n```\n\nOnce the environment variable is set, Chromiumly will automatically send all requests to the hosted API using your key.\n\nYou can also configure the hosted API programmatically without an endpoint:\n\n```typescript\nimport { Chromiumly } from \"chromiumly\";\n\nChromiumly.configure({\n  apiKey: \"your-api-key\",\n});\n```\n\n### Minimal usage example (hosted API)\n\n```typescript\nimport { UrlConverter } from \"chromiumly\";\n\nasync function run() {\n  const urlConverter = new UrlConverter();\n\n  const buffer = await urlConverter.convert({\n    url: \"https://www.example.com/\",\n  });\n\n  // Write the buffer to disk, send it over HTTP, etc.\n}\n\nrun();\n```\n\n# Table of Contents\n\n1. [API Key Authentication (Hosted API)](#api-key-authentication-hosted-api)\n2. [Getting Started](#getting-started)\n   - [Installation](#installation)\n   - [Prerequisites](#prerequisites)\n   - [Configuration](#configuration)\n3. [Authentication](#authentication)\n   - [Basic Authentication](#basic-authentication)\n   - [API Key Authentication](#api-key-authentication)\n   - [Advanced Authentication](#advanced-authentication)\n4. [Core Features](#core-features)\n   - [Chromium](#chromium)\n     - [URL](#url)\n     - [HTML](#html)\n     - [Markdown](#markdown)\n     - [Screenshot](#screenshot)\n   - [LibreOffice](#libreoffice)\n   - [PDF Engines](#pdf-engines)\n     - [Format Conversion](#format-conversion)\n     - [Merging](#merging)\n     - [PDF Rotation](#pdf-rotation)\n     - [Metadata Management](#metadata-management)\n     - [Bookmarks Management](#bookmarks-management)\n     - [File Generation](#file-generation)\n   - [System](#system)\n   - [PDF Splitting](#pdf-splitting)\n   - [PDF Flattening](#pdf-flattening)\n   - [PDF Encryption](#pdf-encryption)\n   - [Embedding Files](#embedding-files)\n   - [Templates (hosted API only)](#templates-hosted-api-only)\n   - [Watermark and stamp](#watermark-and-stamp)\n5. [Usage Example](#snippet)\n\n## Getting Started\n\n### Installation\n\nUsing npm:\n\n```bash\nnpm install chromiumly\n```\n\nUsing yarn:\n\n```bash\nyarn add chromiumly\n```\n\n### Prerequisites\n\nIf 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.\n\nIf you prefer to self‑host Gotenberg, be sure you install [Docker](https://www.docker.com/) if you have not already done so.\n\nAfter that, you can start a default Docker container of [Gotenberg](https://gotenberg.dev/) as follows:\n\n```bash\ndocker run --rm -p 3000:3000 gotenberg/gotenberg:8\n```\n\n### Configuration\n\nChromiumly supports configurations via both [dotenv](https://www.npmjs.com/package/dotenv)\nand [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**.\n\n#### dotenv\n\n```bash\nGOTENBERG_ENDPOINT=http://localhost:3000\n```\n\n#### config\n\n```json\n{\n  \"gotenberg\": {\n    \"endpoint\": \"http://localhost:3000\"\n  }\n}\n```\n\n#### code\n\n```typescript\nimport { Chromiumly } from \"chromiumly\";\n\nChromiumly.configure({ endpoint: \"http://localhost:3000\" });\n```\n\n## Authentication\n\n### Basic Authentication\n\nGotenberg 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:\n\n```bash\ndocker run --rm -p 3000:3000 \\\n-e GOTENBERG_API_BASIC_AUTH_USERNAME=user \\\n-e GOTENBERG_API_BASIC_AUTH_PASSWORD=pass \\\ngotenberg/gotenberg:8.4.0 gotenberg --api-enable-basic-auth\n\n```\n\nTo integrate this setup with Chromiumly, you need to update your configuration as outlined below:\n\n```bash\nGOTENBERG_ENDPOINT=http://localhost:3000\nGOTENBERG_API_BASIC_AUTH_USERNAME=user\nGOTENBERG_API_BASIC_AUTH_PASSWORD=pass\n```\n\nOr\n\n```json\n{\n  \"gotenberg\": {\n    \"endpoint\": \"http://localhost:3000\",\n    \"api\": {\n      \"basicAuth\": {\n        \"username\": \"user\",\n        \"password\": \"pass\"\n      }\n    }\n  }\n}\n```\n\nOr\n\n```typescript\nChromiumly.configure({\n  endpoint: \"http://localhost:3000\",\n  username: \"user\",\n  password: \"pass\",\n});\n```\n\n### API Key Authentication\n\nAPI 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.\n\n### Advanced Authentication\n\nTo 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.\n\nFor example, you can include a Bearer token for authentication along with a custom header as follows:\n\n```typescript\nconst token = await generateToken();\n\nChromiumly.configure({\n  endpoint: \"http://localhost:3000\",\n  customHttpHeaders: {\n    Authorization: `Bearer ${token}`,\n    \"X-Custom-Header\": \"value\",\n  },\n});\n```\n\n## Core Features\n\nChromiumly 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`).\n\nThe **`Templates`** class is the exception—it talks only to the Chromiumly hosted API and is documented [below](#templates-hosted-api-only).\n\n### Chromium\n\nThere are three different classes that come with a single method (i.e.`convert`) which calls one of\nChromium's [conversion routes](https://gotenberg.dev/docs/convert-with-chromium/convert-url-to-pdf) to convert `html` and `markdown` files, or\na `url` to a `buffer` which contains the converted PDF file content.\n\nSimilarly, 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`.\n\n#### URL\n\n```typescript\nimport { UrlConverter } from \"chromiumly\";\n\nconst urlConverter = new UrlConverter();\nconst buffer = await urlConverter.convert({\n  url: \"https://www.example.com/\",\n});\n```\n\n```typescript\nimport { UrlScreenshot } from \"chromiumly\";\n\nconst screenshot = new UrlScreenshot();\nconst buffer = await screenshot.capture({\n  url: \"https://www.example.com/\",\n});\n```\n\n#### HTML\n\nThe only requirement is that the file name should be `index.html`.\n\n```typescript\nimport { HtmlConverter } from \"chromiumly\";\n\nconst htmlConverter = new HtmlConverter();\nconst buffer = await htmlConverter.convert({\n  html: \"path/to/index.html\",\n});\n```\n\n```typescript\nimport { HtmlScreenshot } from \"chromiumly\";\n\nconst screenshot = new HtmlScreenshot();\nconst buffer = await screenshot.capture({\n  html: \"path/to/index.html\",\n});\n```\n\n#### Markdown\n\nThis route accepts an `index.html` file plus a markdown file.\n\n```typescript\nimport { MarkdownConverter } from \"chromiumly\";\n\nconst markdownConverter = new MarkdownConverter();\nconst buffer = await markdownConverter.convert({\n  html: \"path/to/index.html\",\n  markdown: \"path/to/file.md\",\n});\n```\n\n```typescript\nimport { MarkdownScreenshot } from \"chromiumly\";\n\nconst screenshot = new MarkdownScreenshot();\nconst buffer = await screenshot.capture({\n  html: \"path/to/index.html\",\n  markdown: \"path/to/file.md\",\n});\n```\n\nEach `convert()` method takes an optional `properties` parameter of the following type which dictates how the PDF generated\nfile will look like.\n\n```typescript\ntype PageProperties = {\n  singlePage?: boolean; // Print the entire content in one single page (default false)\n  size?: {\n    width: number | string; // Paper width (number in inches or string with units: 72pt, 96px, 1in, 25.4mm, 2.54cm, 6pc, default 8.5)\n    height: number | string; // Paper height (number in inches or string with units: 72pt, 96px, 1in, 25.4mm, 2.54cm, 6pc, default 11)\n  };\n  margins?: {\n    top: number | string; // Top margin (number in inches or string with units: 72pt, 96px, 1in, 25.4mm, 2.54cm, 6pc, default 0.39)\n    bottom: number | string; // Bottom margin (number in inches or string with units: 72pt, 96px, 1in, 25.4mm, 2.54cm, 6pc, default 0.39)\n    left: number | string; // Left margin (number in inches or string with units: 72pt, 96px, 1in, 25.4mm, 2.54cm, 6pc, default 0.39)\n    right: number | string; // Right margin (number in inches or string with units: 72pt, 96px, 1in, 25.4mm, 2.54cm, 6pc, default 0.39)\n  };\n  preferCssPageSize?: boolean; // Define whether to prefer page size as defined by CSS (default false)\n  printBackground?: boolean; // Print the background graphics (default false)\n  omitBackground?: boolean; // Hide the default white background and allow generating PDFs with transparency (default false)\n  landscape?: boolean; // Set the paper orientation to landscape (default false)\n  scale?: number; // The scale of the page rendering (default 1.0)\n  nativePageRanges?: { from: number; to: number }; // Page ranges to print\n};\n```\n\n**Page Size and Margins Units**\n\nBoth `size` and `margins` properties support two formats:\n\n1. **Numeric values** (in inches): For backward compatibility, you can continue using numbers which represent inches.\n2. **String values with units**: You can now specify explicit units using the following formats:\n   - `pt` (points): e.g., `\"72pt\"`\n   - `px` (pixels): e.g., `\"96px\"`\n   - `in` (inches): e.g., `\"1in\"`\n   - `mm` (millimeters): e.g., `\"25.4mm\"`\n   - `cm` (centimeters): e.g., `\"2.54cm\"`\n   - `pc` (picas): e.g., `\"6pc\"`\n\n**Examples:**\n\n```typescript\n// Using numeric values (inches)\nproperties: {\n  size: { width: 8.5, height: 11 },\n  margins: { top: 0.5, bottom: 0.5, left: 1, right: 1 }\n}\n\n// Using string values with units\nproperties: {\n  size: { width: \"210mm\", height: \"297mm\" }, // A4 size\n  margins: { top: \"1cm\", bottom: \"1cm\", left: \"2cm\", right: \"2cm\" }\n}\n\n// Mixing numeric and string values\nproperties: {\n  size: { width: 8.5, height: \"11in\" },\n  margins: { top: \"10mm\", bottom: 0.5, left: \"72pt\", right: 1 }\n}\n```\n\nIn 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:\n\n```typescript\ntype ConversionOptions = {\n  properties?: PageProperties; // Customize the appearance of the generated PDF\n  pdfFormat?: PdfFormat; // Define the PDF format for the conversion\n  pdfUA?: boolean; // Enable PDF for Universal Access for optimal accessibility (default false)\n  userAgent?: string; // Customize the user agent string sent during conversion\n  header?: PathLikeOrReadStream; // Specify a custom header for the PDF\n  footer?: PathLikeOrReadStream; // Specify a custom footer for the PDF\n  emulatedMediaType?: EmulatedMediaType; // Specify the emulated media type for conversion\n  emulatedMediaFeatures?: EmulatedMediaFeature[]; // Override CSS media features (e.g., prefers-color-scheme). Default: None.\n  waitDelay?: string; // Duration (e.g., '5s') to wait when loading an HTML document before conversion\n  waitForExpression?: string; // JavaScript expression to wait before converting an HTML document into PDF\n  waitForSelector?: string; // CSS selector to wait for before converting an HTML document into PDF until it matches a node\n  extraHttpHeaders?: Record\u003cstring, string\u003e; // Include additional HTTP headers in the request\n  failOnHttpStatusCodes?: number[]; // List of HTTP status codes triggering a 409 Conflict response (default [499, 599])\n  failOnConsoleExceptions?: boolean; // Return a 409 Conflict response if there are exceptions in the Chromium console (default false)\n  failOnResourceHttpStatusCodes?: number[]; // Return a 409 Conflict response if resource HTTP status code is in the list (default [499,599])\n  ignoreResourceHttpStatusDomains?: string[]; // Domains to exclude from resource HTTP status code checks (matches exact domains or subdomains)\n  failOnResourceLoadingFailed?: boolean; // Return a 409 Conflict response if resource loading failed (default false)\n  skipNetworkIdleEvent?: boolean; // Do not wait for Chromium network to be idle (default true)\n  skipNetworkAlmostIdleEvent?: boolean; // Do not wait for Chromium network to be almost idle (default true)\n  metadata?: Metadata; // Metadata to be written.\n  cookies?: Cookie[]; // Cookies to be written.\n  downloadFrom?: DownloadFrom; // Download a file from one or multiple URLs. Each URL must return a Content-Disposition header with a filename parameter.\n  webhook?: WebhookOptions; // Request-level webhook headers for async callbacks.\n  split?: SplitOptions; // Split the PDF file into multiple files.\n  userPassword?: string; // Password for opening the resulting PDF(s).\n  ownerPassword?: string; // Password for full access on the resulting PDF(s).\n  embeds?: PathLikeOrReadStream[]; // Files to embed in the generated PDF.\n  watermark?: PdfEngineWatermark; // Optional PDF-engine post-processing watermark (behind page content).\n  stamp?: PdfEngineStamp; // Optional PDF-engine post-processing stamp (on top of page content).\n};\n```\n\n```typescript\ntype DownloadFromEntry = {\n  url: string;\n  extraHttpHeaders?: Record\u003cstring, string\u003e;\n  embedded?: boolean; // Legacy flag, prefer field\n  field?: \"embedded\" | \"watermark\" | \"stamp\" | \"\";\n};\n\ntype DownloadFrom = DownloadFromEntry | DownloadFromEntry[];\n\ntype WebhookOptions = {\n  webhookUrl: string;\n  webhookErrorUrl: string;\n  webhookMethod?: \"POST\" | \"PUT\" | \"PATCH\";\n  webhookErrorMethod?: \"POST\" | \"PUT\" | \"PATCH\";\n  webhookExtraHttpHeaders?: Record\u003cstring, string\u003e;\n  webhookEventsUrl?: string;\n};\n```\n\nOptional `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.\n\n```typescript\ntype PdfEngineWatermark = {\n  source?: \"text\" | \"image\" | \"pdf\";\n  expression?: string; // Text, or filename of the uploaded asset when source is image or pdf\n  pages?: string; // Page ranges (e.g. \"1-3\"); omit for all pages\n  options?: Record\u003cstring, unknown\u003e; // Serialized as JSON (engine-specific)\n  file?: PathLikeOrReadStream | Buffer; // Required when source is image or pdf\n};\n\ntype PdfEngineStamp = {\n  source?: \"text\" | \"image\" | \"pdf\";\n  expression?: string;\n  pages?: string;\n  options?: Record\u003cstring, unknown\u003e;\n  file?: PathLikeOrReadStream | Buffer;\n};\n```\n\n#### Screenshot\n\nSimilarly, the `capture()` method takes an optional `properties` parameter of the specified type, influencing the appearance of the captured screenshot file.\n\n```typescript\ntype ImageProperties = {\n  format: \"png\" | \"jpeg\" | \"webp\"; //The image compression format, either \"png\", \"jpeg\" or \"webp\".\n  quality?: number; // The compression quality from range 0 to 100 (jpeg only).\n  omitBackground?: boolean; // Hide the default white background and allow generating screenshots with transparency.\n  width?: number; // The device screen width in pixels (default 800).\n  height?: number; // The device screen height in pixels (default 600).\n  clip?: boolean; // Define whether to clip the screenshot according to the device dimensions (default false).\n};\n```\n\nFurthermore, 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:\n\n```typescript\ntype ScreenshotOptions = {\n  properties?: ImageProperties;\n  header?: PathLikeOrReadStream;\n  footer?: PathLikeOrReadStream;\n  emulatedMediaType?: EmulatedMediaType;\n  emulatedMediaFeatures?: EmulatedMediaFeature[]; // Override CSS media features (e.g., prefers-color-scheme). Default: None.\n  waitDelay?: string; // Duration (e.g, '5s') to wait when loading an HTML document before convertion.\n  waitForExpression?: string; // JavaScript's expression to wait before converting an HTML document into PDF until it returns true.\n  waitForSelector?: string; // CSS selector to wait for before converting an HTML document into PDF until it matches a node.\n  extraHttpHeaders?: Record\u003cstring, string\u003e;\n  failOnHttpStatusCodes?: number[]; // Return a 409 Conflict response if the HTTP status code is in the list (default [499,599])\n  failOnConsoleExceptions?: boolean; // Return a 409 Conflict response if there are exceptions in the Chromium console (default false)\n  failOnResourceHttpStatusCodes?: number[]; // Return a 409 Conflict response if resource HTTP status code is in the list (default [499,599])\n  ignoreResourceHttpStatusDomains?: string[]; // Domains to exclude from resource HTTP status code checks (matches exact domains or subdomains)\n  failOnResourceLoadingFailed?: boolean; // Return a 409 Conflict response if resource loading failed (default false)\n  skipNetworkIdleEvent?: boolean; // Do not wait for Chromium network to be idle (default true)\n  skipNetworkAlmostIdleEvent?: boolean; // Do not wait for Chromium network to be almost idle (default true)\n  optimizeForSpeed?: boolean; // Define whether to optimize image encoding for speed, not for resulting size.\n  cookies?: Cookie[]; // Cookies to be written.\n  downloadFrom?: DownloadFrom; // Download files from one or multiple URLs.\n  webhook?: WebhookOptions; // Request-level webhook headers for async callbacks.\n  userPassword?: string; // Password for opening the resulting PDF(s).\n  ownerPassword?: string; // Password for full access on the resulting PDF(s).\n  embeds?: PathLikeOrReadStream[]; // Files to embed in the generated PDF.\n};\n```\n\n### LibreOffice\n\nThe `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\naccepted [here](https://gotenberg.dev/docs/convert-with-libreoffice/convert-to-pdf).\n\n```typescript\nimport { LibreOffice } from \"chromiumly\";\n\nconst buffer = await LibreOffice.convert({\n  files: [\n    \"path/to/file.docx\",\n    \"path/to/file.png\",\n    { data: xlsxFileBuffer, ext: \"xlsx\" },\n  ],\n});\n```\n\nSimilarly to Chromium's route `convert` method, this method takes the following optional parameters :\n\n- `properties`: changes how the PDF generated file will look like. It also includes a `password` parameter to open the source file.\n- `pdfa`: PDF format of the conversion resulting file (i.e. `PDF/A-1a`, `PDF/A-2b`, `PDF/A-3b`).\n- `pdfUA`: enables PDF for Universal Access for optimal accessibility.\n- `merge`: merges all the resulting files from the conversion into an individual PDF file.\n- `metadata`: writes metadata to the generated PDF file.\n- `losslessImageCompression`: allows turning lossless compression on or off to tweak image conversion performance.\n- `reduceImageResolution`: allows turning on or off image resolution reduction to tweak image conversion performance.\n- `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.\n- `maxImageResolution`: specifies if all images will be reduced to the specified DPI value. Possible values are: `75`, `150`, `300`, `600`, and `1200`.\n- `initialView`: initial PDF view mode (`0`: none, `1`: outline, `2`: thumbnails).\n- `initialPage`: page number opened by default.\n- `magnification`: initial magnification mode (`0`: default, `1`: fit page, `2`: fit width, `3`: fit visible, `4`: explicit zoom).\n- `zoom`: initial zoom percentage when `magnification` is `4`.\n- `pageLayout`: initial layout (`0`: default, `1`: single page, `2`: one column, `3`: two columns).\n- `firstPageOnLeft`: place first page on the left when using two-column layout.\n- `resizeWindowToInitialPage`: resize the viewer window to the first page dimensions.\n- `centerWindow`: center the PDF viewer window on screen.\n- `openInFullScreenMode`: open the PDF in full-screen mode.\n- `displayPDFDocumentTitle`: display PDF title in viewer title bar instead of filename.\n- `hideViewerMenubar`: hide the viewer menu bar.\n- `hideViewerToolbar`: hide the viewer toolbar.\n- `hideViewerWindowControls`: hide viewer window controls.\n- `useTransitionEffects`: use transition effects for Impress slides.\n- `openBookmarkLevels`: number of bookmark levels opened on load (`-1` opens all levels).\n- `downloadFrom`: download files remotely (`DownloadFromEntry` or `DownloadFromEntry[]`).\n- `flatten`: a boolean that, when set to true, flattens the split PDF files, making form fields and annotations uneditable.\n- `userPassword`: password for opening the resulting PDF(s).\n- `ownerPassword`: password for full access on the resulting PDF(s).\n- `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.\n- `webhook`: request-level webhook headers for async callbacks.\n- **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).\n- **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`.\n\n### PDF Engines\n\nThe `PDFEngines` class interacts with Gotenberg's [PDF Engines](https://gotenberg.dev/docs/manipulate-pdfs/pdfa-pdfua) routes to manipulate PDF files.\n\n#### Format Conversion\n\nThis 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.\n\n```typescript\nimport { PDFEngines } from \"chromiumly\";\n\nconst buffer = await PDFEngines.convert({\n  files: [\"path/to/file_1.pdf\", \"path/to/file_2.pdf\"],\n  pdfa: PdfFormat.A_2b,\n  pdfUA: true,\n});\n```\n\n#### Merging\n\nThis method interacts with [PDF Engines](https://gotenberg.dev/docs/manipulate-pdfs/merge-pdfs) merge route which gathers different\nengines that can manipulate and merge PDF files such\nas: [PDFtk](https://gitlab.com/pdftk-java/pdftk), [PDFcpu](https://github.com/pdfcpu/pdfcpu), [QPDF](https://github.com/qpdf/qpdf),\nand [UNO](https://github.com/unoconv/unoconv).\n\n```typescript\nimport { PDFEngines } from \"chromiumly\";\n\nconst buffer = await PDFEngines.merge({\n  files: [\"path/to/file_1.pdf\", \"path/to/file_2.pdf\"],\n  pdfa: PdfFormat.A_2b,\n  pdfUA: true,\n});\n```\n\nOptional `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.\n\nOptional `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.\n\n#### PDF Rotation\n\n`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).\n\n```typescript\nimport { PDFEngines } from \"chromiumly\";\n\nconst rotated = await PDFEngines.rotate({\n  files: [\"path/to/document.pdf\"],\n  angle: 90,\n  pages: \"1-3\", // optional; omit for all pages\n});\n```\n\n#### Watermark and stamp (dedicated routes)\n\nThese 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).\n\n```typescript\nimport { PDFEngines } from \"chromiumly\";\n\nconst watermarked = await PDFEngines.watermark({\n  files: [\"path/to/document.pdf\"],\n  watermark: {\n    source: \"text\",\n    expression: \"CONFIDENTIAL\",\n    options: { opacity: 0.25, rotation: 45 },\n  },\n});\n\nconst stamped = await PDFEngines.stamp({\n  files: [\"path/to/document.pdf\"],\n  stamp: {\n    source: \"text\",\n    expression: \"APPROVED\",\n    options: { opacity: 0.5, rotation: 0 },\n  },\n});\n```\n\n#### Metadata Management\n\n##### readMetadata\n\nThis method reads metadata from the provided PDF files.\n\n```typescript\nimport { PDFEngines } from \"chromiumly\";\n\nconst metadataBuffer = await PDFEngines.readMetadata([\n  \"path/to/file_1.pdf\",\n  \"path/to/file_2.pdf\",\n]);\n```\n\n##### writeMetadata\n\nThis method writes metadata to the provided PDF files.\n\n```typescript\nimport { PDFEngines } from \"chromiumly\";\n\nconst buffer = await PDFEngines.writeMetadata({\n  files: [\"path/to/file_1.pdf\", \"path/to/file_2.pdf\"],\n  metadata: {\n    Author: \"Taha Cherfia\",\n    Title: \"Chromiumly\",\n    Keywords: [\"pdf\", \"html\", \"gotenberg\"],\n  },\n});\n```\n\nPlease consider referring to [ExifTool](https://exiftool.org/TagNames/XMP.html#pdf) for a comprehensive list of accessible metadata options.\n\n#### Bookmarks Management\n\n##### readBookmarks\n\nThis method reads bookmarks (outline / table of contents) from the provided PDF files.\n\n```typescript\nimport { PDFEngines } from \"chromiumly\";\n\nconst bookmarks = await PDFEngines.readBookmarks([\n  \"path/to/file_1.pdf\",\n  \"path/to/file_2.pdf\",\n]);\n```\n\n##### writeBookmarks\n\nThis method writes bookmarks to the provided PDF files.\n\n```typescript\nimport { PDFEngines } from \"chromiumly\";\n\nconst updated = await PDFEngines.writeBookmarks({\n  files: [\"path/to/file_1.pdf\"],\n  bookmarks: [\n    {\n      title: \"Chapter 1\",\n      page: 1,\n      children: [],\n    },\n  ],\n});\n```\n\n#### File Generation\n\nIt is just a generic complementary method that takes the `buffer` returned by the `convert` method, and a\nchosen `filename` to generate the PDF file.\n\nPlease note that all the PDF files can be found `__generated__` folder in the root folder of your project.\n\n### PDF Splitting\n\nEach [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:\n\n- `mode`: the mode of the split. It can be `pages` or `intervals`.\n- `span`: the span of the split. It is a string that represents the range of pages to split.\n- `unify`: a boolean that allows unifying the split files. Only works when `mode` is `pages`.\n- `flatten`: a boolean that, when set to true, flattens the split PDF files, making form fields and annotations uneditable.\n\n```typescript\nimport { UrlConverter } from \"chromiumly\";\nconst buffer = await UrlConverter.convert({\n  url: \"https://www.example.com/\",\n  split: {\n    mode: \"pages\",\n    span: \"1-2\",\n    unify: true,\n  },\n});\n```\n\nOn 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.\n\n```typescript\nimport { PDFEngines } from \"chromiumly\";\n\nconst buffer = await PDFEngines.split({\n  files: [\"path/to/file_1.pdf\", \"path/to/file_2.pdf\"],\n  options: {\n    mode: \"pages\",\n    span: \"1-2\",\n    unify: true,\n  },\n});\n```\n\n`PDFEngines.split` also accepts optional `watermark`, `stamp`, and `rotate` for the same PDF-engine post-processing as merge.\n\n\u003e ⚠️ **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.\n\n### PDF Flattening\n\nPDF flattening converts interactive elements like forms and annotations into a static PDF. This ensures the document looks the same everywhere and prevents further edits.\n\n```typescript\nimport { PDFEngines } from \"chromiumly\";\n\nconst buffer = await PDFEngines.flatten([\n  \"path/to/file_1.pdf\",\n  \"path/to/file_2.pdf\",\n]);\n```\n\n### PDF Encryption\n\nEach [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.\n\n```typescript\nimport { UrlConverter } from \"chromiumly\";\n\nconst buffer = await UrlConverter.convert({\n  url: \"https://www.example.com/\",\n  userPassword: \"my_user_password\",\n  ownerPassword: \"my_owner_password\",\n});\n```\n\n### Embedding Files\n\nEach [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.\n\nYou can embed multiple files by passing an array of file paths, buffers, or read streams:\n\n```typescript\nimport { HtmlConverter } from \"chromiumly\";\n\nconst htmlConverter = new HtmlConverter();\nconst buffer = await htmlConverter.convert({\n  html: \"path/to/index.html\",\n  embeds: [\n    \"path/to/invoice.xml\",\n    \"path/to/logo.png\",\n    Buffer.from(\"additional data\"),\n  ],\n});\n```\n\nAll embedded files will be attached to the generated PDF and can be extracted using PDF readers that support file attachments.\n\n### Templates (hosted API only)\n\nThe `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.\n\nThe following template types are currently available:\n\n| Type                 | Description        |\n| -------------------- | ------------------ |\n| `invoice_saas`       | SaaS-style invoice |\n| `invoice_freelancer` | Freelancer invoice |\n| `invoice_classic`    | Classic invoice    |\n| `invoice_minimal`    | Minimal invoice    |\n| `invoice_modern`     | Modern invoice     |\n\n#### Basic Usage\n\n```typescript\nimport { Templates } from \"chromiumly\";\n\nconst templates = new Templates();\nconst buffer = await templates.generate({\n  type: \"invoice_saas\",\n  data: {\n    invoiceNumber: \"INV-319\",\n    createdDate: \"2026-03-19\",\n    dueDate: \"2026-04-02\",\n    companyLogo: \"https://cdn.acmecloud.com/assets/logo-mark.png\",\n    sender: {\n      name: \"Acme Cloud LLC\",\n      addressLine1: \"450 Madison Ave\",\n      addressLine2: \"New York, NY 10022\",\n    },\n    receiver: {\n      name: \"Northwind Health Inc.\",\n      addressLine1: \"221 Harbor Blvd\",\n      addressLine2: \"San Diego, CA 92101\",\n    },\n    items: [\n      {\n        description: \"Platform Subscription (Annual)\",\n        qty: 1,\n        unitPrice: \"1500.00\",\n        amount: \"1500.00\",\n      },\n      {\n        description: \"Onboarding\",\n        qty: 1,\n        unitPrice: \"300.00\",\n        amount: \"300.00\",\n      },\n    ],\n    currency: \"USD\",\n    subTotal: \"1800.00\",\n    taxRate: 8.25,\n    taxAmount: \"148.50\",\n    total: \"1948.50\",\n    footerNote: \"Payment due in 14 days.\",\n    footerDisclaimer: \"Late fees may apply.\",\n  },\n});\n```\n\n#### Payload Validation\n\nPass `{ 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.\n\n```typescript\nconst buffer = await templates.generate(request, { validate: true });\n```\n\n#### Payload Shape\n\n```typescript\ntype TemplateRequest\u003cTType extends TemplateType\u003e = {\n  type: TType;\n  data: TemplateDataByType[TType];\n};\n\ninterface InvoiceSaasTemplateData {\n  invoiceNumber: string;\n  createdDate: string;\n  dueDate: string;\n  companyLogo: string;\n  sender: TemplateParty;\n  receiver: TemplateParty;\n  items: InvoiceItem[];\n  currency: Currency;\n  subTotal: string;\n  taxRate: number;\n  taxAmount: string;\n  total: string;\n  footerNote: string;\n  footerDisclaimer: string;\n}\n\ninterface TemplateParty {\n  name: string;\n  addressLine1: string;\n  addressLine2: string;\n  tax?: string;\n  iban?: string;\n  bic?: string;\n}\n\ninterface InvoiceItem {\n  description: string;\n  qty: number;\n  unitPrice: string;\n  amount: string;\n}\n```\n\nThe `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\"`).\n\n### Watermark and stamp\n\nGotenberg 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:\n\n| API                                                                | What to pass                                                                         |\n| ------------------------------------------------------------------ | ------------------------------------------------------------------------------------ |\n| `UrlConverter` / `HtmlConverter` / `MarkdownConverter` `convert()` | `watermark`, `stamp`, `rotate` on the options object (see `ConversionOptions` above) |\n| `LibreOffice.convert()`                                            | Native fields (`nativeWatermarkText`, …) and/or `watermark`, `stamp`, `rotate`       |\n| `PDFEngines.merge()` / `PDFEngines.split()`                        | Optional `watermark`, `stamp`, `rotate`                                              |\n| `PDFEngines.rotate()`                                              | Dedicated endpoint; `files`, `angle` (`90` \\| `180` \\| `270`), optional `pages`      |\n| `PDFEngines.watermark()` / `PDFEngines.stamp()`                    | Dedicated endpoints; `watermark` or `stamp` config is required                       |\n\nFor 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.\n\n### System\n\nThe `System` class exposes Gotenberg system endpoints:\n\n```typescript\nimport { System } from \"chromiumly\";\n\nconst health = await System.getHealth(); // GET /health\nconst heartbeat = await System.headHealth(); // HEAD /health\nconst version = await System.getVersion(); // GET /version\nconst debug = await System.getDebug(); // GET /debug\nconst metrics = await System.getPrometheusMetrics(); // GET /prometheus/metrics\n```\n\n## Snippet\n\nThe following is a short snippet of how to use the library.\n\n```typescript\nimport { PDFEngines, UrlConverter } from \"chromiumly\";\n\nasync function run() {\n  const urlConverter = new UrlConverter();\n  const buffer = await urlConverter.convert({\n    url: \"https://gotenberg.dev/\",\n    properties: {\n      singlePage: true,\n      size: {\n        width: 8.5,\n        height: 11,\n      },\n    },\n    emulatedMediaType: \"screen\",\n    emulatedMediaFeatures: [\n      { name: \"prefers-color-scheme\", value: \"dark\" },\n      { name: \"prefers-reduced-motion\", value: \"reduce\" },\n    ],\n    failOnHttpStatusCodes: [404],\n    failOnConsoleExceptions: true,\n    skipNetworkIdleEvent: false,\n    skipNetworkAlmostIdleEvent: false,\n    optimizeForSpeed: true,\n    split: {\n      mode: \"pages\",\n      span: \"1-2\",\n      unify: true,\n    },\n  });\n\n  await PDFEngines.generate(\"gotenberg.pdf\", buffer);\n}\n\nrun();\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcherfia%2Fchromiumly","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcherfia%2Fchromiumly","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcherfia%2Fchromiumly/lists"}