{"id":13705210,"url":"https://github.com/cherfia/jotenberg","last_synced_at":"2026-01-29T11:29:58.090Z","repository":{"id":61551296,"uuid":"552349700","full_name":"cherfia/jotenberg","owner":"cherfia","description":"A Java 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-11-21T23:11:24.000Z","size":138,"stargazers_count":27,"open_issues_count":0,"forks_count":5,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-11-22T00:20:32.833Z","etag":null,"topics":["chromium","dotx","gotenberg","html","libreoffice","markdown","pdf","pptx","screenshots","xslx"],"latest_commit_sha":null,"homepage":"","language":"Java","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":null,"contributing":null,"funding":null,"license":"LICENSE.md","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,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2022-10-16T11:50:30.000Z","updated_at":"2025-11-21T23:05:31.000Z","dependencies_parsed_at":"2025-05-15T19:37:40.222Z","dependency_job_id":"f3e9ee1b-ea16-4b43-932f-893322491483","html_url":"https://github.com/cherfia/jotenberg","commit_stats":null,"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"purl":"pkg:github/cherfia/jotenberg","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cherfia%2Fjotenberg","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cherfia%2Fjotenberg/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cherfia%2Fjotenberg/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cherfia%2Fjotenberg/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/cherfia","download_url":"https://codeload.github.com/cherfia/jotenberg/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cherfia%2Fjotenberg/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28876687,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-29T10:31:27.438Z","status":"ssl_error","status_checked_at":"2026-01-29T10:31:01.017Z","response_time":59,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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","dotx","gotenberg","html","libreoffice","markdown","pdf","pptx","screenshots","xslx"],"created_at":"2024-08-02T22:00:35.477Z","updated_at":"2026-01-29T11:29:58.081Z","avatar_url":"https://github.com/cherfia.png","language":"Java","funding_links":[],"categories":["Clients"],"sub_categories":[],"readme":"![build](https://github.com/cherfia/jotenberg/actions/workflows/build.yml/badge.svg)\n![licence](https://img.shields.io/github/license/cherfia/jotenberg?style=flat-square)\n\nA lightweight Java library that interacts with [Gotenberg](https://gotenberg.dev/)'s different routes to convert\na variety of document formats to PDF files.\n\n# Table of Contents\n\n1. [Getting Started](#getting-started)\n   - [Installation](#installation)\n   - [Prerequisites](#prerequisites)\n   - [Configuration](#configuration)\n2. [Authentication](#authentication)\n   - [Basic Authentication](#basic-authentication)\n   - [Advanced Authentication](#advanced-authentication)\n3. [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     - [Metadata Management](#metadata-management)\n   - [PDF Splitting](#pdf-splitting)\n   - [PDF Flattening](#pdf-flattening)\n   - [PDF Encryption](#pdf-encryption)\n   - [Embedding Files](#embedding-files)\n4. [Usage Example](#usage-example)\n\n## Getting Started\n\n### Installation\n\n#### Apache Maven\n\nFirst, add the GitHub Packages repository to your `pom.xml`:\n\n```xml\n\u003crepositories\u003e\n    \u003crepository\u003e\n        \u003cid\u003egithub\u003c/id\u003e\n        \u003curl\u003ehttps://maven.pkg.github.com/cherfia/jotenberg\u003c/url\u003e\n    \u003c/repository\u003e\n\u003c/repositories\u003e\n```\n\nThen add the dependency:\n\n```xml\n\u003cdependency\u003e\n    \u003cgroupId\u003eio.bitizens\u003c/groupId\u003e\n    \u003cartifactId\u003ejotenberg\u003c/artifactId\u003e\n    \u003cversion\u003e2.0.0\u003c/version\u003e\n\u003c/dependency\u003e\n```\n\n**Note:** You'll need to authenticate with GitHub Packages. Create a personal access token with `read:packages`\npermission and add it to your `~/.m2/settings.xml`:\n\n```xml\n\u003csettings\u003e\n    \u003cservers\u003e\n        \u003cserver\u003e\n            \u003cid\u003egithub\u003c/id\u003e\n            \u003cusername\u003eYOUR_GITHUB_USERNAME\u003c/username\u003e\n            \u003cpassword\u003eYOUR_GITHUB_TOKEN\u003c/password\u003e\n        \u003c/server\u003e\n    \u003c/servers\u003e\n\u003c/settings\u003e\n```\n\n#### Gradle\n\nFirst, add the GitHub Packages repository to your `build.gradle`:\n\n```gradle\nrepositories {\n    maven {\n        name = \"GitHubPackages\"\n        url = uri(\"https://maven.pkg.github.com/cherfia/jotenberg\")\n        credentials {\n            username = project.findProperty(\"gpr.user\") ?: System.getenv(\"GITHUB_ACTOR\")\n            password = project.findProperty(\"gpr.token\") ?: System.getenv(\"GITHUB_TOKEN\")\n        }\n    }\n}\n```\n\nThen add the dependency:\n\n```gradle\nimplementation group: 'io.bitizens', name: 'jotenberg', version: '2.0.0'\n```\n\n**Note:** You'll need to authenticate with GitHub Packages. Create a personal access token with `read:packages`\npermission and set it as an environment variable `GITHUB_TOKEN` or as a Gradle property `gpr.token`.\n\n### Prerequisites\n\nBefore attempting to use Jotenberg, be sure you install [Docker](https://www.docker.com/) if you have not already done\nso.\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\nJotenberg uses constructor-based configuration. Create an instance of `Jotenberg` class and pass your Gotenberg endpoint URL as a constructor parameter:\n\n```java\nimport io.bitizens.Jotenberg;\n\nJotenberg client = new Jotenberg(\"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\nTo integrate this setup with Jotenberg, you need to configure the HTTP client with basic authentication. You can extend the `Jotenberg` class or configure the underlying HTTP client to include authentication headers.\n\n### Advanced Authentication\n\nTo implement advanced authentication or add custom HTTP headers to your requests, you can configure the underlying HTTP client used by Jotenberg. 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 by configuring the HTTP client:\n\n```java\nimport io.bitizens.Jotenberg;\nimport org.apache.http.impl.client.CloseableHttpClient;\nimport org.apache.http.impl.client.HttpClients;\nimport org.apache.http.client.methods.HttpRequestBase;\nimport org.apache.http.HttpRequest;\n\n// Create a custom HTTP client with authentication\nCloseableHttpClient httpClient = HttpClients.custom()\n    .addInterceptorFirst((HttpRequest request) -\u003e {\n        request.addHeader(\"Authorization\", \"Bearer \" + token);\n        request.addHeader(\"X-Custom-Header\", \"value\");\n    })\n    .build();\n\n// Note: Jotenberg currently uses an internal HTTP client.\n// For advanced authentication, you may need to extend the class or modify the HTTPRequestManager.\n```\n\n## Core Features\n\nJotenberg introduces different classes that serve as wrappers to\nGotenberg's [routes](https://gotenberg.dev/docs/routes). These classes encompass methods featuring an\ninput file parameter, such as `html`, `header`, `footer`, and `markdown`, capable of accepting inputs in the form of a\n`File` or `List\u003cFile\u003e`.\n\n### Chromium\n\nThere are three different methods that come with the `Jotenberg` class (i.e. `convert`) which calls one of\nChromium's [Conversion routes](https://gotenberg.dev/docs/routes#convert-with-chromium) to convert `html` and `markdown` files, or\na `url` to a `CloseableHttpResponse` which contains the `HttpEntity` that holds the content of the converted PDF file.\n\nSimilarly, a new set of methods have been added to harness the recently introduced Gotenberg [Screenshot routes](https://gotenberg.dev/docs/routes#screenshots-route). These methods include a single method called `capture`, which allows capturing full-page screenshots of `html`, `markdown`, and `url`.\n\n#### URL\n\n```java\nimport io.bitizens.Jotenberg;\nimport io.bitizens.chromium.ChromiumPageProperties;\nimport io.bitizens.chromium.ChromiumOptions;\nimport org.apache.http.client.methods.CloseableHttpResponse;\n\nJotenberg client = new Jotenberg(\"http://localhost:3000\");\n\nChromiumPageProperties pageProperties = new ChromiumPageProperties.Builder().build();\nChromiumOptions options = new ChromiumOptions.Builder().build();\n\nCloseableHttpResponse response = client.convert(\n    \"https://www.example.com/\",\n    pageProperties,\n    options\n);\n```\n\n```java\nimport io.bitizens.Jotenberg;\nimport io.bitizens.screenshots.ImageProperties;\nimport io.bitizens.screenshots.ScreenshotOptions;\nimport org.apache.http.client.methods.CloseableHttpResponse;\n\nJotenberg client = new Jotenberg(\"http://localhost:3000\");\n\nImageProperties imageProperties = new ImageProperties.Builder()\n    .addFormat(\"png\")\n    .addWidth(1920)\n    .addHeight(1080)\n    .build();\n\nScreenshotOptions screenshotOptions = new ScreenshotOptions.Builder().build();\n\nCloseableHttpResponse response = client.capture(\n    \"https://www.example.com/\",\n    imageProperties,\n    screenshotOptions\n);\n```\n\n#### HTML\n\nThe only requirement is that the file name should be `index.html`.\n\n```java\nimport io.bitizens.Jotenberg;\nimport io.bitizens.chromium.ChromiumPageProperties;\nimport io.bitizens.chromium.ChromiumOptions;\nimport org.apache.http.client.methods.CloseableHttpResponse;\nimport java.io.File;\n\nJotenberg client = new Jotenberg(\"http://localhost:3000\");\n\nFile file = new File(\"path/to/index.html\");\nChromiumPageProperties pageProperties = new ChromiumPageProperties.Builder().build();\nChromiumOptions options = new ChromiumOptions.Builder().build();\n\nCloseableHttpResponse response = client.convert(file, pageProperties, options);\n```\n\n```java\nimport io.bitizens.Jotenberg;\nimport io.bitizens.screenshots.ImageProperties;\nimport io.bitizens.screenshots.ScreenshotOptions;\nimport org.apache.http.client.methods.CloseableHttpResponse;\nimport java.io.File;\n\nJotenberg client = new Jotenberg(\"http://localhost:3000\");\n\nFile file = new File(\"path/to/index.html\");\nImageProperties imageProperties = new ImageProperties.Builder()\n    .addFormat(\"png\")\n    .build();\nScreenshotOptions screenshotOptions = new ScreenshotOptions.Builder().build();\n\nCloseableHttpResponse response = client.capture(file, imageProperties, screenshotOptions);\n```\n\n#### Markdown\n\nThis route accepts an `index.html` file plus a markdown file.\n\n```java\nimport io.bitizens.Jotenberg;\nimport io.bitizens.chromium.ChromiumPageProperties;\nimport io.bitizens.chromium.ChromiumOptions;\nimport org.apache.http.client.methods.CloseableHttpResponse;\nimport java.io.File;\nimport java.util.ArrayList;\nimport java.util.List;\n\nJotenberg client = new Jotenberg(\"http://localhost:3000\");\n\nList\u003cFile\u003e files = new ArrayList\u003c\u003e();\nfiles.add(new File(\"path/to/index.html\"));\nfiles.add(new File(\"path/to/file.md\"));\n\nChromiumPageProperties pageProperties = new ChromiumPageProperties.Builder().build();\nChromiumOptions options = new ChromiumOptions.Builder().build();\n\nCloseableHttpResponse response = client.convert(files, pageProperties, options);\n```\n\n```java\nimport io.bitizens.Jotenberg;\nimport io.bitizens.screenshots.ImageProperties;\nimport io.bitizens.screenshots.ScreenshotOptions;\nimport org.apache.http.client.methods.CloseableHttpResponse;\nimport java.io.File;\nimport java.util.ArrayList;\nimport java.util.List;\n\nJotenberg client = new Jotenberg(\"http://localhost:3000\");\n\nList\u003cFile\u003e files = new ArrayList\u003c\u003e();\nfiles.add(new File(\"path/to/index.html\"));\nfiles.add(new File(\"path/to/file.md\"));\n\nImageProperties imageProperties = new ImageProperties.Builder()\n    .addFormat(\"png\")\n    .build();\nScreenshotOptions screenshotOptions = new ScreenshotOptions.Builder().build();\n\nCloseableHttpResponse response = client.capture(files, imageProperties, screenshotOptions);\n```\n\nEach `convert()` method takes a `ChromiumPageProperties` parameter which dictates how the PDF generated\nfile will look like. The `ChromiumPageProperties` class supports the following properties:\n\n- `paperWidth` - Paper width, in inches (default 8.5)\n- `paperHeight` - Paper height, in inches (default 11)\n- `marginTop` - Top margin, in inches (default 0.39)\n- `marginBottom` - Bottom margin, in inches (default 0.39)\n- `marginLeft` - Left margin, in inches (default 0.39)\n- `marginRight` - Right margin, in inches (default 0.39)\n- `preferCssPageSize` - Define whether to prefer page size as defined by CSS (default false)\n- `printBackground` - Print the background graphics (default false)\n- `omitBackground` - Hide the default white background and allow generating PDFs with transparency (default false)\n- `landscape` - Set the paper orientation to landscape (default false)\n- `scale` - The scale of the page rendering (default 1.0)\n- `nativePageRanges` - Page ranges to print\n- `singlePage` - Print the entire content in one single page (default false)\n- `pdfa` - PDF format of the conversion resulting file\n- `nativePdfFormat` - Native PDF format (default PDF/A-1a)\n\nIn addition to the `ChromiumPageProperties` customization options, the `convert()` method also accepts a `ChromiumOptions` parameter to further enhance the versatility of the conversion process. Here's an overview of the available options:\n\n- `header` - Header HTML file\n- `footer` - Footer HTML file\n- `emulatedMediaType` - Emulated media type (screen or print)\n- `waitDelay` - Duration to wait when loading an HTML document before conversion\n- `waitForExpression` - JavaScript expression to wait before converting\n- `waitForSelector` - CSS selector to wait for before converting\n- `extraHttpHeaders` - Additional HTTP headers as JSON object\n- `failOnHttpStatusCodes` - HTTP status codes to fail on\n- `failOnResourceHttpStatusCodes` - HTTP status codes to fail on for resources\n- `ignoreResourceHttpStatusDomains` - Domains to exclude from resource HTTP status code checks\n- `failOnResourceLoadingFailed` - Whether to fail on resource loading failed\n- `failOnConsoleExceptions` - Whether to fail on console exceptions\n- `skipNetworkIdleEvent` - Whether to skip network idle event\n- `generateDocumentOutline` - Whether to generate document outline\n- `cookies` - Cookies to be written (as JSON string)\n- `downloadFrom` - Download a file from a URL (as JSON string)\n- `metadata` - Metadata to be written (as JSON string)\n- `split` - Split the PDF into multiple files (as JSON string)\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\n\n#### Screenshot\n\nSimilarly, the `capture()` method takes an `ImageProperties` parameter, influencing the appearance of the captured screenshot file.\n\n- `format` - The image compression format, either \"png\", \"jpeg\" or \"webp\" (default \"png\")\n- `quality` - The compression quality from range 0 to 100 (jpeg only)\n- `omitBackground` - Hide the default white background and allow generating screenshots with transparency (default false)\n- `width` - The device screen width in pixels (default 800)\n- `height` - The device screen height in pixels (default 600)\n- `clip` - Define whether to clip the screenshot according to the device dimensions (default false)\n\nFurthermore, alongside the customization options offered by `ImageProperties`, the `capture()` method accommodates a `ScreenshotOptions` parameter to expand the versatility of the screenshot process. Below is a comprehensive overview of all options available:\n\n- `header` - Header HTML file\n- `footer` - Footer HTML file\n- `emulatedMediaType` - Emulated media type (screen or print)\n- `waitDelay` - Duration (e.g, '5s') to wait when loading an HTML document before conversion\n- `waitForExpression` - JavaScript's expression to wait before converting an HTML document into PDF until it returns true\n- `waitForSelector` - CSS selector (e.g., '#id', '.class') to wait for before converting\n- `extraHttpHeaders` - Additional HTTP headers as JSON object\n- `failOnHttpStatusCodes` - Return a 409 Conflict response if the HTTP status code is in the list (default [499,599])\n- `failOnResourceHttpStatusCodes` - HTTP status codes to fail on for resources\n- `ignoreResourceHttpStatusDomains` - Domains to exclude from resource HTTP status code checks\n- `failOnConsoleExceptions` - Return a 409 Conflict response if there are exceptions in the Chromium console (default false)\n- `skipNetworkIdleEvent` - Do not wait for Chromium network to be idle (default true)\n- `optimizeForSpeed` - Define whether to optimize image encoding for speed, not for resulting size\n- `cookies` - Cookies to be written (as JSON string)\n- `downloadFrom` - Download the file from a specific URL. It must return a Content-Disposition header with a filename parameter\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\n\n### LibreOffice\n\nThe `Jotenberg` class comes with a `convertWithLibreOffice` method. This method interacts with [LibreOffice](https://gotenberg.dev/docs/routes#convert-with-libreoffice) route to convert different documents to PDF files. You can find the file extensions\naccepted [here](https://gotenberg.dev/docs/routes#convert-with-libreoffice).\n\n```java\nimport io.bitizens.Jotenberg;\nimport io.bitizens.libreoffice.LibreOfficePageProperties;\nimport io.bitizens.libreoffice.LibreOfficeOptions;\nimport org.apache.http.client.methods.CloseableHttpResponse;\nimport java.io.File;\nimport java.util.ArrayList;\nimport java.util.List;\n\nJotenberg client = new Jotenberg(\"http://localhost:3000\");\n\nList\u003cFile\u003e files = new ArrayList\u003c\u003e();\nfiles.add(new File(\"path/to/file.docx\"));\nfiles.add(new File(\"path/to/file.png\"));\n\nLibreOfficePageProperties pageProperties = new LibreOfficePageProperties.Builder().build();\nLibreOfficeOptions options = new LibreOfficeOptions.Builder().build();\n\nCloseableHttpResponse response = client.convertWithLibreOffice(files, pageProperties, options);\n```\n\nSimilarly to Chromium's route `convert` method, this method takes the following optional parameters:\n\n- `pageProperties`: changes how the PDF generated file will look like. It also includes a `password` parameter to open the source file.\n- `options`: includes:\n  - `merge` - merges all the resulting files from the conversion into an individual PDF 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  - `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  - `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\n### PDF Engines\n\nThe `Jotenberg` class interacts with Gotenberg's [PDF Engines](https://gotenberg.dev/docs/routes#convert-into-pdfa--pdfua-route) routes to manipulate PDF files.\n\n#### Format Conversion\n\nThis method interacts with [PDF Engines](https://gotenberg.dev/docs/routes#convert-into-pdfa--pdfua-route) conversion route to transform PDF files into the requested PDF/A format and/or PDF/UA.\n\n```java\nimport io.bitizens.Jotenberg;\nimport io.bitizens.pdfengines.PDFEnginesConversionOptions;\nimport io.bitizens.common.PdfFormat;\nimport org.apache.http.client.methods.CloseableHttpResponse;\nimport java.io.File;\nimport java.util.ArrayList;\nimport java.util.List;\n\nJotenberg client = new Jotenberg(\"http://localhost:3000\");\n\nList\u003cFile\u003e files = new ArrayList\u003c\u003e();\nfiles.add(new File(\"path/to/file_1.pdf\"));\nfiles.add(new File(\"path/to/file_2.pdf\"));\n\nPDFEnginesConversionOptions options = new PDFEnginesConversionOptions.Builder()\n    .addPdfa(PdfFormat.A_2B)\n    .addPdfua(true)\n    .build();\n\nCloseableHttpResponse response = client.convertWithPdfEngines(files, options);\n```\n\n#### Merging\n\nThis method interacts with [PDF Engines](https://gotenberg.dev/docs/routes#merge-pdfs-route) 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```java\nimport io.bitizens.Jotenberg;\nimport io.bitizens.pdfengines.PDFEnginesMergeOptions;\nimport io.bitizens.common.PdfFormat;\nimport org.apache.http.client.methods.CloseableHttpResponse;\nimport org.json.JSONObject;\nimport java.io.File;\nimport java.util.ArrayList;\nimport java.util.List;\n\nJotenberg client = new Jotenberg(\"http://localhost:3000\");\n\nList\u003cFile\u003e files = new ArrayList\u003c\u003e();\nfiles.add(new File(\"path/to/file_1.pdf\"));\nfiles.add(new File(\"path/to/file_2.pdf\"));\n\nJSONObject metadata = new JSONObject();\nmetadata.put(\"Title\", \"Merged Document\");\n\nPDFEnginesMergeOptions options = new PDFEnginesMergeOptions.Builder()\n    .addPdfa(PdfFormat.A_2B)\n    .addPdfua(true)\n    .addMetadata(metadata)\n    .addFlatten(true)\n    .build();\n\nCloseableHttpResponse response = client.mergeWithPdfEngines(files, options);\n```\n\n#### Metadata Management\n\n##### readMetadata\n\nThis method reads metadata from the provided PDF files.\n\n```java\nimport io.bitizens.Jotenberg;\nimport org.apache.http.client.methods.CloseableHttpResponse;\nimport java.io.File;\nimport java.util.ArrayList;\nimport java.util.List;\n\nJotenberg client = new Jotenberg(\"http://localhost:3000\");\n\nList\u003cFile\u003e files = new ArrayList\u003c\u003e();\nfiles.add(new File(\"path/to/file_1.pdf\"));\nfiles.add(new File(\"path/to/file_2.pdf\"));\n\nCloseableHttpResponse response = client.readMetadataWithPdfEngines(files);\n```\n\n##### writeMetadata\n\nThis method writes metadata to the provided PDF files.\n\n```java\nimport io.bitizens.Jotenberg;\nimport org.apache.http.client.methods.CloseableHttpResponse;\nimport org.json.JSONObject;\nimport java.io.File;\nimport java.util.ArrayList;\nimport java.util.List;\n\nJotenberg client = new Jotenberg(\"http://localhost:3000\");\n\nList\u003cFile\u003e files = new ArrayList\u003c\u003e();\nfiles.add(new File(\"path/to/file_1.pdf\"));\nfiles.add(new File(\"path/to/file_2.pdf\"));\n\nJSONObject metadata = new JSONObject();\nmetadata.put(\"Author\", \"Taha Cherfia\");\nmetadata.put(\"Title\", \"Jotenberg\");\nmetadata.put(\"Keywords\", new String[]{\"pdf\", \"html\", \"gotenberg\"});\n\nCloseableHttpResponse response = client.writeMetadataWithPdfEngines(files, metadata.toString());\n```\n\nPlease consider referring to [ExifTool](https://exiftool.org/TagNames/XMP.html#pdf) for a comprehensive list of accessible metadata options.\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 passed as a JSON string in the options 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```java\nimport io.bitizens.Jotenberg;\nimport io.bitizens.chromium.ChromiumPageProperties;\nimport io.bitizens.chromium.ChromiumOptions;\nimport org.apache.http.client.methods.CloseableHttpResponse;\nimport org.json.JSONObject;\n\nJotenberg client = new Jotenberg(\"http://localhost:3000\");\n\nJSONObject split = new JSONObject();\nsplit.put(\"mode\", \"pages\");\nsplit.put(\"span\", \"1-2\");\nsplit.put(\"unify\", true);\n\nChromiumPageProperties pageProperties = new ChromiumPageProperties.Builder().build();\nChromiumOptions options = new ChromiumOptions.Builder()\n    .addSplit(split.toString())\n    .build();\n\nCloseableHttpResponse response = client.convert(\n    \"https://www.example.com/\",\n    pageProperties,\n    options\n);\n```\n\nOn the other hand, PDFEngines' has a `splitWithPdfEngines` method that interacts with [PDF Engines](https://gotenberg.dev/docs/routes#split-pdfs-route) split route which splits PDF files into multiple files.\n\n```java\nimport io.bitizens.Jotenberg;\nimport org.apache.http.client.methods.CloseableHttpResponse;\nimport java.io.File;\nimport java.util.ArrayList;\nimport java.util.List;\n\nJotenberg client = new Jotenberg(\"http://localhost:3000\");\n\nList\u003cFile\u003e files = new ArrayList\u003c\u003e();\nfiles.add(new File(\"path/to/file_1.pdf\"));\nfiles.add(new File(\"path/to/file_2.pdf\"));\n\nCloseableHttpResponse response = client.splitWithPdfEngines(\n    files,\n    \"pages\",  // split mode\n    \"1-2\",    // split span\n    true,     // unify\n    false     // flatten\n);\n```\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```java\nimport io.bitizens.Jotenberg;\nimport org.apache.http.client.methods.CloseableHttpResponse;\nimport java.io.File;\nimport java.util.ArrayList;\nimport java.util.List;\n\nJotenberg client = new Jotenberg(\"http://localhost:3000\");\n\nList\u003cFile\u003e files = new ArrayList\u003c\u003e();\nfiles.add(new File(\"path/to/file_1.pdf\"));\nfiles.add(new File(\"path/to/file_2.pdf\"));\n\nCloseableHttpResponse response = client.flattenWithPdfEngines(files);\n```\n\n### PDF Encryption\n\nEach [Chromium](#chromium) and [LibreOffice](#libreoffice) route supports PDF encryption through the `userPassword` and `ownerPassword` parameters in their respective options. The `userPassword` is required to open the PDF, while the `ownerPassword` provides full access permissions.\n\n```java\nimport io.bitizens.Jotenberg;\nimport io.bitizens.chromium.ChromiumPageProperties;\nimport io.bitizens.chromium.ChromiumOptions;\nimport org.apache.http.client.methods.CloseableHttpResponse;\n\nJotenberg client = new Jotenberg(\"http://localhost:3000\");\n\nChromiumPageProperties pageProperties = new ChromiumPageProperties.Builder().build();\nChromiumOptions options = new ChromiumOptions.Builder()\n    .addUserPassword(\"my_user_password\")\n    .addOwnerPassword(\"my_owner_password\")\n    .build();\n\nCloseableHttpResponse response = client.convert(\n    \"https://www.example.com/\",\n    pageProperties,\n    options\n);\n```\n\nAlternatively, you can use the `encryptWithPdfEngines` method:\n\n```java\nimport io.bitizens.Jotenberg;\nimport io.bitizens.pdfengines.PDFEnginesEncryptOptions;\nimport org.apache.http.client.methods.CloseableHttpResponse;\nimport java.io.File;\nimport java.util.ArrayList;\nimport java.util.List;\n\nJotenberg client = new Jotenberg(\"http://localhost:3000\");\n\nList\u003cFile\u003e files = new ArrayList\u003c\u003e();\nfiles.add(new File(\"path/to/document.pdf\"));\n\nPDFEnginesEncryptOptions encryptOptions = new PDFEnginesEncryptOptions.Builder()\n    .addUserPassword(\"user123\")\n    .addOwnerPassword(\"owner123\")\n    .build();\n\nCloseableHttpResponse response = client.encryptWithPdfEngines(files, encryptOptions);\n```\n\n### Embedding Files\n\nEach [Chromium](#chromium) and [LibreOffice](#libreoffice) route supports embedding files into the generated PDF through the `embeds` parameter in their respective options. 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 a list of files:\n\n```java\nimport io.bitizens.Jotenberg;\nimport io.bitizens.chromium.ChromiumPageProperties;\nimport io.bitizens.chromium.ChromiumOptions;\nimport org.apache.http.client.methods.CloseableHttpResponse;\nimport java.io.File;\nimport java.util.ArrayList;\nimport java.util.List;\n\nJotenberg client = new Jotenberg(\"http://localhost:3000\");\n\nList\u003cFile\u003e embeds = new ArrayList\u003c\u003e();\nembeds.add(new File(\"path/to/invoice.xml\"));\nembeds.add(new File(\"path/to/logo.png\"));\n\nChromiumPageProperties pageProperties = new ChromiumPageProperties.Builder().build();\nChromiumOptions options = new ChromiumOptions.Builder()\n    .addEmbeds(embeds)\n    .build();\n\nFile htmlFile = new File(\"path/to/index.html\");\nCloseableHttpResponse response = client.convert(htmlFile, pageProperties, options);\n```\n\nAll embedded files will be attached to the generated PDF and can be extracted using PDF readers that support file attachments.\n\nAlternatively, you can use the `embedWithPdfEngines` method:\n\n```java\nimport io.bitizens.Jotenberg;\nimport org.apache.http.client.methods.CloseableHttpResponse;\nimport java.io.File;\nimport java.util.ArrayList;\nimport java.util.List;\n\nJotenberg client = new Jotenberg(\"http://localhost:3000\");\n\nList\u003cFile\u003e pdfFiles = new ArrayList\u003c\u003e();\npdfFiles.add(new File(\"path/to/document.pdf\"));\n\nList\u003cFile\u003e embeds = new ArrayList\u003c\u003e();\nembeds.add(new File(\"path/to/attachment.txt\"));\nembeds.add(new File(\"path/to/image.png\"));\n\nCloseableHttpResponse response = client.embedWithPdfEngines(pdfFiles, embeds);\n```\n\n## Usage Example\n\nThe following is a short snippet of how to use the library.\n\n```java\nimport io.bitizens.Jotenberg;\nimport io.bitizens.chromium.ChromiumPageProperties;\nimport io.bitizens.chromium.ChromiumOptions;\nimport io.bitizens.pdfengines.PDFEnginesMergeOptions;\nimport io.bitizens.common.PdfFormat;\nimport org.apache.http.client.methods.CloseableHttpResponse;\nimport org.apache.commons.io.FileUtils;\nimport org.json.JSONObject;\n\nimport java.io.File;\nimport java.nio.file.Files;\nimport java.nio.file.Paths;\nimport java.util.ArrayList;\nimport java.util.List;\n\npublic class Main {\n    public static void main(String[] args) {\n        try (Jotenberg client = new Jotenberg(\"http://localhost:3000\")) {\n            String url = \"https://gotenberg.dev/\";\n\n            ChromiumPageProperties pageProperties = new ChromiumPageProperties.Builder()\n                .addSinglePage(true)\n                .addPaperWidth(8.5f)\n                .addPaperHeight(11f)\n                .build();\n\n            JSONObject split = new JSONObject();\n            split.put(\"mode\", \"pages\");\n            split.put(\"span\", \"1-2\");\n            split.put(\"unify\", true);\n\n            ChromiumOptions options = new ChromiumOptions.Builder()\n                .addSplit(split.toString())\n                .build();\n\n            CloseableHttpResponse response = client.convert(url, pageProperties, options);\n            \n            var status = response.getStatusLine().getStatusCode();\n            if (status != 200) {\n                throw new RuntimeException(\"Gotenberg PDF rendering failed with status: \" + status);\n            }\n\n            // Save the PDF to a file\n            var projectDir = Paths.get(\"\").toAbsolutePath().normalize();\n            var tempDir = Files.createTempDirectory(projectDir, \"temp_\");\n            var tempFile = Files.createTempFile(tempDir, \"PDF_\", \".pdf\").toFile();\n            var pdfContent = response.getEntity().getContent();\n            FileUtils.copyInputStreamToFile(pdfContent, tempFile);\n        } catch (Exception e) {\n            throw new RuntimeException(e);\n        }\n    }\n}\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcherfia%2Fjotenberg","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcherfia%2Fjotenberg","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcherfia%2Fjotenberg/lists"}