Gotenberg logo Gotenberg A Docker-powered stateless API for converting HTML, Markdown and Office documents to PDF.

Introduction

Gotenberg is a Docker-powered stateless API for converting HTML, Markdown and Office documents to PDF.

  • HTML and Markdown conversions using Google Chrome headless
  • Office conversions (.txt, .rtf, .docx, .doc, .odt, .pptx, .ppt, .odp and so on) using unoconv
  • Performance: Google Chrome and LibreOffice (unoconv) started once in the background thanks to PM2
  • Failure prevention: PM2 automatically restarts previous processes if they fail
  • Assets: send your header, footer, images, fonts, stylesheets and so on for converting your HTML and Markdown to beaufitul PDFs!
  • Easily interact with the API using our Go and PHP libraries

Install

Gotenberg is shipped within a Docker image.

You may start it with:

$ docker run --rm -p 3000:3000 thecodingmachine/gotenberg:5

The API will be available at http://localhost:3000.

Docker Compose

You may also add it in your Docker Compose stack:

version: '3'

services:

  # your others services

  gotenberg:
    image: thecodingmachine/gotenberg:5

The API will be available under gotenberg:3000 in your Docker Compose network.

Kubernetes

It may also be deployed with Kubernetes.

Make sure to provide enough memory and CPU requests (for instance 512Mi and 0.2 CPU). Otherwise the API will not be able to launch Google Chrome and LibreOffice (unoconv).

The more resources are granted, the quicker will be the conversions.

In the following examples, we will assume your Gotenberg API is available at http://localhost:3000.

Clients

We provide clients in various languages for easing the interactions with the API.

Go client

$ go get -u github.com/thecodingmachine/gotenberg-go-client/v5

PHP client

Unless your project already has a PSR7 HttpClient, install php-http/guzzle6-adapter:

$ composer require php-http/guzzle6-adapter

Then the PHP client:

$ composer require thecodingmachine/gotenberg-php-client

Environment variables

You may customize the API behaviour thanks to environment variables.

Disable Google Chrome

In order to save some resources, the Gotenberg image accepts the environment variable DISABLE_GOOGLE_CHROME.

It takes the strings "0" or "1" as value.

If Google Chrome is disabled, the following conversions will not be available anymore: HTML, URL and Markdown

Disable LibreOffice (unoconv)

You may also disable LibreOffice (unoconv) with DISABLE_UNOCONV.

If LibreOffice (unoconv) is disabled, the following conversion will not be available anymore: Office

Default wait timeout

By default, the API will wait 10 seconds before it considers the conversion to be unsuccessful.

You may customize this timeout thanks to the environment variable DEFAULT_WAIT_TIMEOUT.

It takes a string representation of a float as value (e.g "2.5" for 2.5 seconds).

The default timeout may also be overridden per request thanks to the form field waitTimeout. See the timeout section.

Disable logging on healthcheck

By default, the API will add a log entry when the healthcheck endpoint is called.

You may turn off this logging so as to avoid unnecessary entries in your logs with the environment variable DISABLE_HEALTHCHECK_LOGGING.

This environment variable operates in the same manner as the DISABLE_GOOGLE_CHROME and DISABLE_UNOCONV variables operate in that it accepts the strings "0" or "1" as values.

Default listen port

By default, the API will listen on port 3000. For most use cases this is perfectly fine, but at times there may be cases where you need to change this due to port conflicts.

You may customize this port location with the environment variable DEFAULT_LISTEN_PORT.

This environment variable accepts any string that can be turned into a port number (e.g., the string "0" up to the string "65535").

HTML

Gotenberg provides the endpoint /convert/html for HTML conversions.

It accepts POST requests with a multipart/form-data Content-Type.

Basic

The only requirement is to send a file named index.html: it is the file which will be converted to PDF.

For instance:

<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <title>My PDF</title>
  </head>
  <body>
    <h1>Hello world!</h1>
  </body>
</html>

cURL

$ curl --request POST \
    --url http://localhost:3000/convert/html \
    --header 'Content-Type: multipart/form-data' \
    --form files=@index.html \
    -o result.pdf

Go

import "github.com/thecodingmachine/gotenberg-go-client/v5"

func main() {
    c := &gotenberg.Client{Hostname: "http://localhost:3000"}
    req, _ := gotenberg.NewHTMLRequest("index.html")
    dest := "result.pdf"
    c.Store(req, dest)
}

PHP

use TheCodingMachine\Gotenberg\Client;
use TheCodingMachine\Gotenberg\DocumentFactory;
use TheCodingMachine\Gotenberg\HTMLRequest;

$client = new Client('http://localhost:3000', new \Http\Adapter\Guzzle6\Client());
$index = DocumentFactory::makeFromPath('index.html', 'index.html');
$request = new HTMLRequest($index);
$dirPath = "/foo";
$filename = $client->store($request, $dirPath);

Header and footer

You may also add a header and/or a footer in the resulting PDF. Respectively, a file named header.html and footer.html.

Each of them has to be a complete HTML document:

<html>
    <head>
        <style>
            body {
                font-size: 8rem;
                margin: 4rem auto;
            }
        </style>
    </head>
    <body>
        <p>
            <span class="pageNumber"></span> of <span class="totalPages"></span>
        </p>
    </body>
</html>

The following classes allow you to inject printing values:

  • date: formatted print date
  • title: document title
  • pageNumber: current page number
  • totalPage: total pages in the document

Attention: the CSS properties are independant of the ones used in the index.html file. Also, footer.html CSS properties override the ones from header.html.

cURL

$ curl --request POST \
    --url http://localhost:3000/convert/html \
    --header 'Content-Type: multipart/form-data' \
    --form files=@index.html \
    --form files=@header.html \
    --form files=@footer.html \
    -o result.pdf

Go

import "github.com/thecodingmachine/gotenberg-go-client/v5"

func main() {
    c := &gotenberg.Client{Hostname: "http://localhost:3000"}
    req, _ := gotenberg.NewHTMLRequest("index.html")
    req.Header("header.html")
    req.Footer("footer.html")
    dest := "result.pdf"
    c.Store(req, dest)
}

PHP

use TheCodingMachine\Gotenberg\Client;
use TheCodingMachine\Gotenberg\DocumentFactory;
use TheCodingMachine\Gotenberg\HTMLRequest;

$client = new Client('http://localhost:3000', new \Http\Adapter\Guzzle6\Client());
$index = DocumentFactory::makeFromPath('index.html', 'index.html');
$header = DocumentFactory::makeFromPath('header.html', 'header.html');
$footer = DocumentFactory::makeFromPath('footer.html', 'footer.html');
$request = new HTMLRequest($index);
$request->setHeader($header);
$request->setFooter($footer);
$dirPath = "/foo";
$filename = $client->store($request, $dirPath);

Assets

You may also send additional files. For instance: images, fonts, stylesheets and so on.

The only requirement is to make sure that their paths are on the same level as the index.html file.

In others words, this will work:

<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <title>My PDF</title>
  </head>
  <body>
    <h1>Hello world!</h1>
    <img src="img.png">
  </body>
</html>

But this won’t:

<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <title>My PDF</title>
  </head>
  <body>
    <h1>Hello world!</h1>
    <img src="/foo/img.png">
  </body>
</html>

You may also use remote paths for Google fonts, images and so on.

If you want to install fonts directly in the Gotenberg Docker image, see to the fonts section.

cURL

$ curl --request POST \
    --url http://localhost:3000/convert/html \
    --header 'Content-Type: multipart/form-data' \
    --form files=@index.html \
    --form files=@style.css \
    --form files=@img.png \
    --form files=@font.woff \
    -o result.pdf

Go

import "github.com/thecodingmachine/gotenberg-go-client/v5"

func main() {
    c := &gotenberg.Client{Hostname: "http://localhost:3000"}
    req, _ := gotenberg.NewHTMLRequest("index.html")
    req.Assets("font.woff", "img.gif", "style.css")
    dest := "result.pdf"
    c.Store(req, dest)
}

PHP

use TheCodingMachine\Gotenberg\Client;
use TheCodingMachine\Gotenberg\DocumentFactory;
use TheCodingMachine\Gotenberg\HTMLRequest;

$client = new Client('http://localhost:3000', new \Http\Adapter\Guzzle6\Client());
$index = DocumentFactory::makeFromPath('index.html', 'index.html');
$assets = [
    DocumentFactory::makeFromPath('style.css', 'style.css'),
    DocumentFactory::makeFromPath('img.png', 'img.png'),
    DocumentFactory::makeFromPath('font.woff', 'font.woff'),
];
$request = new HTMLRequest($index);
$request->setAssets($assets);
$dest = "result.pdf";
$client->store($request, $dest);

Paper size, margins, orientation

You may also customize the resulting PDF format.

By default, it will be rendered with A4 size, 1 inch margins and portrait orientation.

Paper size and margins have to be provided in inches. Same for margins.

cURL

$ curl --request POST \
    --url http://localhost:3000/convert/html \
    --header 'Content-Type: multipart/form-data' \
    --form files=@index.html \
    --form paperWidth=8.27 \
    --form paperHeight=11.27 \
    --form marginTop=0 \
    --form marginBottom=0 \
    --form marginLeft=0 \
    --form marginRight=0 \
    --form landscape=true \
    -o result.pdf

Go

import "github.com/thecodingmachine/gotenberg-go-client/v5"

func main() {
    c := &gotenberg.Client{Hostname: "http://localhost:3000"}
    req, _ := gotenberg.NewHTMLRequest("index.html")
    req.PaperSize(gotenberg.A4)
    req.Margins(gotenberg.NoMargins)
    req.Landscape(true)
    dest := "result.pdf"
    c.Store(req, dest)
}

PHP

use TheCodingMachine\Gotenberg\Client;
use TheCodingMachine\Gotenberg\DocumentFactory;
use TheCodingMachine\Gotenberg\HTMLRequest;
use TheCodingMachine\Gotenberg\Request;

$client = new Client('http://localhost:3000', new \Http\Adapter\Guzzle6\Client());
$index = DocumentFactory::makeFromPath('index.html', 'index.html');
$request = new HTMLRequest($index);
$request->setPaperSize(Request::A4);
$request->setMargins(Request::NO_MARGINS);
$request->setLandscape(true);
$dest = "result.pdf";
$client->store($request, $dest);

Wait delay

In some cases, you may want to wait a certain amount of time to make sure the page you’re trying to generate is fully rendered.

The wait delay is a duration in seconds (e.g 2.5 for 2.5 seconds).

cURL

$ curl --request POST \
    --url http://localhost:3000/convert/html \
    --header 'Content-Type: multipart/form-data' \
    --form files=@index.html \
    --form waitDelay=5.5 \
    -o result.pdf

Go

import "github.com/thecodingmachine/gotenberg-go-client/v5"

func main() {
    c := &gotenberg.Client{Hostname: "http://localhost:3000"}
    req, _ := gotenberg.NewHTMLRequest("index.html")
    req.WaitDelay(5.5)
    dest := "result.pdf"
    c.Store(req, dest)
}

PHP

use TheCodingMachine\Gotenberg\Client;
use TheCodingMachine\Gotenberg\DocumentFactory;
use TheCodingMachine\Gotenberg\HTMLRequest;
use TheCodingMachine\Gotenberg\Request;

$client = new Client('http://localhost:3000', new \Http\Adapter\Guzzle6\Client());
$index = DocumentFactory::makeFromPath('index.html', 'index.html');
$request = new HTMLRequest($index);
$request->setWaitDelay(5.5);
$dest = "result.pdf";
$client->store($request, $dest);

URL

Gotenberg provides the endpoint /convert/url for remote URL conversions.

It accepts POST requests with a multipart/form-data Content-Type.

Basic

This endpoint does not accept an index.html file nor assets files but a form field named remoteURL instead. Otherwise, URL conversions work the same as HTML conversions.

Attention: when converting a website to PDF, you should remove all margins. If not, some of the content of the page might be hidden.

cURL

$ curl --request POST \
    --url http://localhost:3000/convert/url \
    --header 'Content-Type: multipart/form-data' \
    --form remoteURL=https://google.com \
    --form marginTop=0 \
    --form marginBottom=0 \
    --form marginLeft=0 \
    --form marginRight=0 \
    -o result.pdf

Go

import "github.com/thecodingmachine/gotenberg-go-client/v5"

func main() {
    c := &gotenberg.Client{Hostname: "http://localhost:3000"}
    req := gotenberg.NewURLRequest("https://google.com")
    req.Margins(gotenberg.NoMargins)
    dest := "result.pdf"
    c.Store(req, dest)
}

PHP

use TheCodingMachine\Gotenberg\Client;
use TheCodingMachine\Gotenberg\URLRequest;

$client = new Client('http://localhost:3000', new \Http\Adapter\Guzzle6\Client());
$request = new URLRequest('https://google.com');
$request->setMargins(Request::NO_MARGINS);
$dest = "result.pdf";
$client->store($request, $dest);

Markdown

Gotenberg provides the endpoint /convert/markdown for Markdown conversions.

It accepts POST requests with a multipart/form-data Content-Type.

Basic

Markdown conversions work the same as HTML conversions.

Only difference is that you have access to the Go template function toHTML in the file index.html. This function will convert a given markdown file to HTML.

For instance:

<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <title>My PDF</title>
  </head>
  <body>
    {{ toHTML .DirPath "file.md" }}
  </body>
</html>

cURL

$ curl --request POST \
    --url http://localhost:3000/convert/markdown \
    --header 'Content-Type: multipart/form-data' \
    --form files=@index.html \
    --form files=@file.md \
    -o result.pdf

Go

import "github.com/thecodingmachine/gotenberg-go-client/v5"

func main() {
    c := &gotenberg.Client{Hostname: "http://localhost:3000"}
    req, _ := gotenberg.NewMarkdownRequest("index.html", "file.md")
    dest := "result.pdf"
    c.Store(req, dest)
}

PHP

use TheCodingMachine\Gotenberg\Client;
use TheCodingMachine\Gotenberg\DocumentFactory;
use TheCodingMachine\Gotenberg\MarkdownRequest;

$client = new Client('http://localhost:3000', new \Http\Adapter\Guzzle6\Client());
$index = DocumentFactory::makeFromPath('index.html', 'index.html');
$markdowns = [
    DocumentFactory::makeFromPath('file.md', 'file.md'),
];
$request = new MarkdownRequest($index, $markdowns);
$dest = "result.pdf";
$client->store($request, $dest);

Office

Gotenberg provides the endpoint /convert/office for Office document conversions.

It accepts POST requests with a multipart/form-data Content-Type.

Basic

You may send one or more Office documents. Following file extensions are accepted:

  • .txt
  • .rtf
  • .fodt
  • .doc
  • .docx
  • .odt
  • .xls
  • .xlsx
  • .ods
  • .ppt
  • .pptx
  • .odp

All files will be merged into a single resulting PDF.

Attention: currently, unoconv cannot perform concurrent conversions. That’s why for Office conversions, the API does only one conversion at a time. See the scalability section to find how to mitigate this issue.

cURL

$ curl --request POST \
    --url http://localhost:3000/convert/office \
    --header 'Content-Type: multipart/form-data' \
    --form files=@document.docx \
    --form files=@document2.docx \
    -o result.pdf

Go

import "github.com/thecodingmachine/gotenberg-go-client/v5"

func main() {
    c := &gotenberg.Client{Hostname: "http://localhost:3000"}
    req, _ := gotenberg.NewOfficeRequest("document.docx", "document2.docx")
    dest := "result.pdf"
    c.Store(req, dest)
}

PHP

use TheCodingMachine\Gotenberg\Client;
use TheCodingMachine\Gotenberg\DocumentFactory;
use TheCodingMachine\Gotenberg\OfficeRequest;

$client = new Client('http://localhost:3000', new \Http\Adapter\Guzzle6\Client());
$files = [
    DocumentFactory::makeFromPath('document.docx', 'document.docx'),
    DocumentFactory::makeFromPath('document2.docx', 'document2.docx'),
];
$request = new OfficeRequest($files);
$dest = "result.pdf";
$client->store($request, $dest);

Orientation

You may also customize the resulting PDF format.

By default, it will be rendered with portrait orientation.

cURL

$ curl --request POST \
    --url http://localhost:3000/convert/office \
    --header 'Content-Type: multipart/form-data' \
    --form files=@document.docx \
    --form landscape=true \
    -o result.pdf

Go

import "github.com/thecodingmachine/gotenberg-go-client/v5"

func main() {
    c := &gotenberg.Client{Hostname: "http://localhost:3000"}
    req, _ := gotenberg.NewOfficeRequest("document.docx")
    req.Landscape(true)
    dest := "result.pdf"
    c.Store(req, dest)
}

PHP

use TheCodingMachine\Gotenberg\Client;
use TheCodingMachine\Gotenberg\DocumentFactory;
use TheCodingMachine\Gotenberg\OfficeRequest;

$client = new Client('http://localhost:3000', new \Http\Adapter\Guzzle6\Client());
$files = [
    DocumentFactory::makeFromPath('document.docx', 'document.docx'),
];
$request = new OfficeRequest($files);
$request->setLandscape(true);
$dest = "result.pdf";
$client->store($request, $dest);

Merge

Gotenberg provides the endpoint /convert/merge for merging PDFs.

It accepts POST requests with a multipart/form-data Content-Type.

Basic

Nothing fancy here: you may send one or more PDF files and the API will merge them and return the resulting PDF file.

Attention: Gotenberg merges the PDF files alphabetically.

cURL

$ curl --request POST \
    --url http://localhost:3000/convert/merge \
    --header 'Content-Type: multipart/form-data' \
    --form files=@file.pdf \
    --form files=@file2.pdf \
    -o result.pdf

Go

import "github.com/thecodingmachine/gotenberg-go-client/v5"

func main() {
    c := &gotenberg.Client{Hostname: "http://localhost:3000"}
    req, _ := gotenberg.NewMergeRequest("file.pdf", "file2.pdf")
    dest := "result.pdf"
    c.Store(req, dest)
}

PHP

use TheCodingMachine\Gotenberg\Client;
use TheCodingMachine\Gotenberg\DocumentFactory;
use TheCodingMachine\Gotenberg\MergeRequest;

$client = new Client('http://localhost:3000', new \Http\Adapter\Guzzle6\Client());
$files = [
    DocumentFactory::makeFromPath('file.pdf', 'file.pdf'),
    DocumentFactory::makeFromPath('file2.pdf', 'file2.pdf'),
];
$request = new MergeRequest($files);
$dest = "result.pdf";
$client->store($request, $dest);

Timeout

All endpoints accept a form field named waitTimeout.

The API will wait the given seconds before it considers the conversion to be unsucessful.

It takes a float as value (e.g 2.5 for 2.5 seconds).

You may also define this value globally: see the environment variables section.

Examples

cURL

$ curl --request POST \
    --url http://localhost:3000/convert/html \
    --header 'Content-Type: multipart/form-data' \
    --form files=@index.html \
    --form waitTimeout=2.5

Go

import "github.com/thecodingmachine/gotenberg-go-client/v5"

func main() {
    c := &gotenberg.Client{Hostname: "http://localhost:3000"}
    req, _ := gotenberg.NewHTMLRequest("index.html")
    req.WaitTimeout(2.5)
    resp, _ := c.Post(req)
}

PHP

use TheCodingMachine\Gotenberg\Client;
use TheCodingMachine\Gotenberg\DocumentFactory;
use TheCodingMachine\Gotenberg\HTMLRequest;
use TheCodingMachine\Gotenberg\Request;

$client = new Client('http://localhost:3000', new \Http\Adapter\Guzzle6\Client());
$index = DocumentFactory::makeFromPath('index.html', 'index.html');
$request = new HTMLRequest($index);
$request->setWaitTimeout(2.5);
$dest = "result.pdf";
$client->store($request, $dest);

Webhook

All endpoints accept a form field named webhookURL.

If provided, the API will send the resulting PDF file in a POST request with the application/pdf Content-Type to given URL.

By doing so, your requests to the API will be over before the conversions are actually done!

Examples

cURL

$ curl --request POST \
    --url http://localhost:3000/convert/html \
    --header 'Content-Type: multipart/form-data' \
    --form files=@index.html \
    --form webhookURL='http://myapp.com/webhook/'

Go

import "github.com/thecodingmachine/gotenberg-go-client/v5"

func main() {
    c := &gotenberg.Client{Hostname: "http://localhost:3000"}
    req, _ := gotenberg.NewHTMLRequest("index.html")
    req.WebhookURL("http://myapp.com/webhook/")
    resp, _ := c.Post(req)
}

PHP

use TheCodingMachine\Gotenberg\Client;
use TheCodingMachine\Gotenberg\DocumentFactory;
use TheCodingMachine\Gotenberg\HTMLRequest;

$client = new Client('http://localhost:3000', new \Http\Adapter\Guzzle6\Client());
$index = DocumentFactory::makeFromPath('index.html', 'index.html');
$request = new HTMLRequest($index);
$request->setWebhookURL('http://myapp.com/webhook/');
$resp = $client->post($request);

Result filename

All endpoints accept a form field named resultFilename.

If provided, the API will return the resulting PDF file with the given filename. Otherwise a random filename is used.

Attention: this feature does not work if the form field webhookURL is given.

Examples

cURL

$ curl --request POST \
    --url http://localhost:3000/convert/html \
    --header 'Content-Type: multipart/form-data' \
    --form files=@index.html \
    --form resultFilename='foo.pdf'

Go

import "github.com/thecodingmachine/gotenberg-go-client/v5"

func main() {
    c := &gotenberg.Client{Hostname: "http://localhost:3000"}
    req, _ := gotenberg.NewHTMLRequest("index.html")
    req.ResultFilename("foo.pdf")
    resp, _ := c.Post(req)
}

PHP

use TheCodingMachine\Gotenberg\Client;
use TheCodingMachine\Gotenberg\DocumentFactory;
use TheCodingMachine\Gotenberg\HTMLRequest;
use TheCodingMachine\Gotenberg\Request;

$client = new Client('http://localhost:3000', new \Http\Adapter\Guzzle6\Client());
$index = DocumentFactory::makeFromPath('index.html', 'index.html');
$request = new HTMLRequest($index);
$request->setResultFilename('foo.pdf');
$resp = $client->post($request);

Scalability

The API being stateless, you may scale it as much as you want.

For instance, using the following Docker Compose file:

version: '3'

services:

  # your others services

  gotenberg:
    image: thecodingmachine/gotenberg:5

You may now launch your services using:

$ docker-compose up --scale gotenberg=your_number_of_instances

When requesting the Gotenberg service with your client(s), Docker will automatically redirect a request to a Gotenberg container according to the round-robin strategy.

Ping

Gotenberg provides the endpoint /ping for checking the API availability with a simple GET request.

This feature is especially useful for liveness/readiness probes in Kubernetes:

Fonts

By default, a handful of fonts are installed. Asian characters are also supported out of the box.

If you wish to use more fonts, you will have to create your own image:

FROM thecodingmachine/gotenberg:5

RUN apt-get -y install yourfonts