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:3
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:3
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([]string{ "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);
$dirPath = "/foo";
$filename = $client->store($request, $dirPath);
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);
$dirPath = "/foo";
$filename = $client->store($request, $dirPath);
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", []string{"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);
$dirPath = "/foo";
$filename = $client->store($request, $dirPath);
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([]string{"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);
$dirPath = "/foo";
$filename = $client->store($request, $dirPath);
You may also customize the resulting PDF format.
By default, it will be rendered with A4 size and portrait orientation.
Paper size has to be provided in
inches. Also, you have to set bothpaperWidthandpaperHeight.
$ curl --request POST \ --url http://localhost:3000/convert/office \ --header 'Content-Type: multipart/form-data' \ --form files=@document.docx \ --form paperWidth=8.27 \ --form paperHeight=11.27 \ --form landscape=true \ -o result.pdf
import "github.com/thecodingmachine/gotenberg/pkg" func main() { c := &gotenberg.Client{Hostname: "http://localhost:3000"} req, _ := gotenberg.NewOfficeRequest([]string{"document.docx"}) req.SetPaperSize(gotenberg.A4) 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->setPaperSize(Request::A4);
$request->setLandscape(true);
$dirPath = "/foo";
$filename = $client->store($request, $dirPath);
By default, only ttf-mscorefonts fonts are installed.
If you wish to use more fonts, you will have to create your own image:
FROM thecodingmachine/gotenberg:3 RUN apt-get -y install yourfonts
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([]string{"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);
$dirPath = "/foo";
$filename = $client->store($request, $dirPath);
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:3
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:
Psst: TheCodingMachine is always looking for talented coders.