Gotenberg
A Docker-powered stateless API for converting HTML, Markdown and Office documents to PDF.
Gotenberg is a Docker-powered stateless API for converting HTML, Markdown and Office documents to PDF.
Gotenberg is shipped within a Docker image.
You may start it with:
$ docker run --rm -p 3000:3000 thecodingmachine/gotenberg:4
The API will be available at http://localhost:3000.
Or add it in your Docker Compose stack:
version: '3' services: # your others services gotenberg: image: thecodingmachine/gotenberg:4
The API will be available under
gotenberg:3000in your Docker Compose network.
It may also be deployed with Kubernetes.
In the following examples, we will assume your Gotenberg API is available at http://localhost:3000.
$ go get -u github.com/thecodingmachine/gotenberg
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
Gotenberg provides the endpoint /convert/html for HTML conversions.
It accepts POST requests with a multipart/form-data Content-Type.
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 --request POST \ --url http://localhost:3000/convert/html \ --header 'Content-Type: multipart/form-data' \ --form files=@index.html \ -o result.pdf
import "github.com/thecodingmachine/gotenberg/pkg" func main() { c := &gotenberg.Client{Hostname: "http://localhost:3000"} req, _ := gotenberg.NewHTMLRequest("index.html") dest := "result.pdf" c.Store(req, dest) }
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);
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 datetitle: document titlepageNumber: current page numbertotalPage: total pages in the documentAttention: the CSS properties are independant of the ones used in the
index.htmlfile. Also,footer.htmlCSS properties override the ones fromheader.html.
$ 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
import "github.com/thecodingmachine/gotenberg/pkg" func main() { c := &gotenberg.Client{Hostname: "http://localhost:3000"} req, _ := gotenberg.NewHTMLRequest("index.html") req.SetHeader("header.html") req.SetFooter("footer.html") dest := "result.pdf" c.Store(req, dest) }
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);
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 --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
import "github.com/thecodingmachine/gotenberg/pkg" func main() { c := &gotenberg.Client{Hostname: "http://localhost:3000"} req, _ := gotenberg.NewHTMLRequest("index.html") req.SetAssets("font.woff", "img.gif", "style.css") dest := "result.pdf" c.Store(req, dest) }
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);
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. Also, you have to set bothpaperWidthandpaperHeight. Same for margins.
$ 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
import "github.com/thecodingmachine/gotenberg/pkg" func main() { c := &gotenberg.Client{Hostname: "http://localhost:3000"} req, _ := gotenberg.NewHTMLRequest("index.html") req.SetPaperSize(gotenberg.A4) req.SetMargins(gotenberg.NoMargins) req.SetLandscape(true) dest := "result.pdf" c.Store(req, dest) }
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);
Gotenberg provides the endpoint /convert/url for remote URL conversions.
It accepts POST requests with a multipart/form-data Content-Type.
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 --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
import "github.com/thecodingmachine/gotenberg/pkg" func main() { c := &gotenberg.Client{Hostname: "http://localhost:3000"} req := gotenberg.NewURLRequest("https://google.com") req.SetMargins(gotenberg.NoMargins) dest := "result.pdf" c.Store(req, dest) }
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";
$filename = $client->store($request, $dest);
Gotenberg provides the endpoint /convert/markdown for Markdown conversions.
It accepts POST requests with a multipart/form-data Content-Type.
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 --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
import "github.com/thecodingmachine/gotenberg/pkg" func main() { c := &gotenberg.Client{Hostname: "http://localhost:3000"} req, _ := gotenberg.NewMarkdownRequest("index.html", "file.md") dest := "result.pdf" c.Store(req, dest) }
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";
$filename = $client->store($request, $dest);
Gotenberg provides the endpoint /convert/office for Office document conversions.
It accepts POST requests with a multipart/form-data Content-Type.
You may send one or more Office documents. Following file extensions are accepted:
.txt.rtf.doc.docx.odt.xls.xlsx.ods.ppt.pptx.odpAll files will be merged into a single resulting PDF.
Attention: currently,
unoconvcannot 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 --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
import "github.com/thecodingmachine/gotenberg/pkg" func main() { c := &gotenberg.Client{Hostname: "http://localhost:3000"} req, _ := gotenberg.NewOfficeRequest("document.docx", "document2.docx") dest := "result.pdf" c.Store(req, dest) }
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";
$filename = $client->store($request, $dest);
You may also customize the resulting PDF format.
By default, it will be rendered with portrait orientation.
$ 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
import "github.com/thecodingmachine/gotenberg/pkg" func main() { c := &gotenberg.Client{Hostname: "http://localhost:3000"} req, _ := gotenberg.NewOfficeRequest("document.docx") req.SetLandscape(true) dest := "result.pdf" c.Store(req, dest) }
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";
$filename = $client->store($request, $dest);
Gotenberg provides the endpoint /merge for merging PDFs.
It accepts POST requests with a multipart/form-data Content-Type.
Nothing special here: you may send one or more PDF files and the API will merge them and return the resulting PDF file.
$ curl --request POST \ --url http://localhost:3000/merge \ --header 'Content-Type: multipart/form-data' \ --form files=@file.pdf \ --form files=@file2.pdf \ -o result.pdf
import "github.com/thecodingmachine/gotenberg/pkg" func main() { c := &gotenberg.Client{Hostname: "http://localhost:3000"} req, _ := gotenberg.NewMergeRequest("file.pdf", "file2.pdf") dest := "result.pdf" c.Store(req, dest) }
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";
$filename = $client->store($request, $dest);
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!
$ 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/'
import "github.com/thecodingmachine/gotenberg/pkg" func main() { c := &gotenberg.Client{Hostname: "http://localhost:3000"} req, _ := gotenberg.NewHTMLRequest("index.html") req.SetWebhookURL("http://myapp.com/webhook/") dest := "result.pdf" resp, _ := c.Post(req) }
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);
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:4
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.
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:
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:4 RUN apt-get -y install yourfonts
Psst: TheCodingMachine is always looking for talented coders.