Compare commits

..

46 Commits
3.0.1 ... 4.2.1

Author SHA1 Message Date
Alexander Joseph Trelore
ab78559710 remove unused imports (#55)
Thanks!
2019-03-13 10:53:20 +01:00
Julien Neuhart
0d75bb1d3e improving documentation on merge 2019-03-05 11:27:25 +01:00
Julien Neuhart
6441062e7f adding Google analytics 2019-02-19 15:27:20 +01:00
Julien Neuhart
4827a45f22 adding flat open document support (fixes #47) 2019-02-18 13:46:58 +01:00
Julien Neuhart
40eef457ab adding latest tag (fixes #48) 2019-02-18 13:44:30 +01:00
Julien Neuhart
c22a4d109b minor fix in doc for web fonts 2019-02-01 11:22:06 +01:00
Julien Neuhart
571c3e04c4 adding form field web fonts timeout 2019-02-01 09:40:16 +01:00
Sergei Opletaev
f9fb97a572 Fix for #44 promise wait for fonts (#45)
`runtime.ExceptionDetails: Uncaught exception at 4:47: SyntaxError: Unexpected token %`
2019-02-01 09:21:02 +01:00
Julien Neuhart
e6484bb2d5 Trigger GitHub documentation 2019-01-28 15:56:13 +01:00
Julien Neuhart
19f51fdc1f fixing PHP documentation 2019-01-28 15:36:59 +01:00
Julien Neuhart
25f7ba3ee6 4.0.0 (#42)
* adding URL conversions

* updating gotenberg version in documentation

* pkg: input as variadic string (#39)

* pkg: input as variadic string

* pkg: fix link to github doc

* update doc about v4 client

* make doc

* fixing missing variadic inputs in doc

* adding more fonts

* updating PHP documentation
2019-01-28 15:18:12 +01:00
Julien Neuhart
a3cd1ff4e1 Merge pull request #41 from thecodingmachine/office-paperSize
removing paper size for Office conversion
2019-01-25 15:57:25 +01:00
Julien Neuhart
f62c717f79 fixing typo in docs 2019-01-25 15:50:15 +01:00
Julien Neuhart
3ed99133d4 removing paper size for Office conversion 2019-01-25 15:48:40 +01:00
Julien Neuhart
ab25cc331d fixes #35 2019-01-25 15:02:18 +01:00
Julien Neuhart
2648992a9b adding moar favicons 2019-01-03 16:02:02 +01:00
Julien Neuhart
7bcc3d081e adding liveness section in documentation 2019-01-03 15:56:53 +01:00
Julien Neuhart
affd1c45ca Merge pull request #32 from Peltoche/master
Add a /ping endpoint
2019-01-03 15:37:18 +01:00
Peltoche
8c7458812d Add a /ping endpoint
This endpoint can be used to check the service liveness. It can be used
for the kubernetes liveness and readiness probs.
2019-01-03 15:21:57 +01:00
Julien Neuhart
053908c772 Merge pull request #31 from thecodingmachine/merge-alt
adding PDFtk for merging PDF files < 1.7
2019-01-03 11:10:07 +01:00
Julien Neuhart
d623736f9f rollbacking to debian 9.5 as the 9.6 image has issue with git installation 2019-01-03 11:01:02 +01:00
Julien Neuhart
a3e416ff68 adding PDFtk for merging PDF files < 1.7 2019-01-03 10:06:56 +01:00
Julien Neuhart
8bf207b9f7 improving contributing doc 2018-12-14 16:13:52 +01:00
Julien Neuhart
e1520d5e7e Merge branch 'master' of https://github.com/thecodingmachine/gotenberg 2018-12-14 15:59:59 +01:00
Julien Neuhart
4f89c9f7c6 updating logo and documentation 2018-12-14 15:59:45 +01:00
Ben Davies
d34fb92211 docs: cURL write to a file instead of stdout (#25) 2018-12-14 09:28:18 +01:00
Julien Neuhart
3f7b006d16 fixing docs generation: was adding Dockerfile too.. 2018-12-14 09:15:51 +01:00
Julien Neuhart
a15cf33691 Dockerfiles: removing useless labels. Docs: now generating with Docker. Travis: publishing done with make 2018-12-14 09:13:04 +01:00
Julien Neuhart
4c1699df6c webhook: resulting file was deleted because of defer outside of goroutine 2018-12-13 18:15:28 +01:00
Julien Neuhart
404535c512 fixing/simplifying godoc 2018-12-13 17:06:29 +01:00
Julien Neuhart
2cedcecc38 adding concurrent testing 2018-12-13 14:46:59 +01:00
Julien Neuhart
ba7b02d8d4 fixing various typos in documentation 2018-12-12 17:44:27 +01:00
Julien Neuhart
6aa3c16646 fixing golang client README 2018-12-12 16:44:32 +01:00
Julien Neuhart
5a03e17e00 fixing introduction in documentation 2018-12-12 16:37:36 +01:00
Julien Neuhart
417d145b43 fixing documentation typos 2018-12-12 16:36:21 +01:00
Julien Neuhart
e5636a2afa adding .txt and .rtf files support (#20) 2018-12-12 16:34:34 +01:00
Julien Neuhart
5291d2d129 disabling pdfcpu logs (#22) 2018-12-12 16:24:54 +01:00
Julien Neuhart
c6f6a87499 now using latest version of unoconv, allowing to specify paper size and orientation + updating go client (#24) 2018-12-12 16:16:35 +01:00
Julien Neuhart
1ef21c6ed6 RPC error (#23) 2018-12-12 12:20:07 +01:00
Julien Neuhart
6ee1932cbf improving API tests: now verifying response content type and status code 2018-12-11 14:16:43 +01:00
Julien Neuhart
83cf43a314 fixing cURL commands in documentation 2018-12-11 14:09:04 +01:00
Julien Neuhart
52c14a79a1 fixing GoDoc + some test function names 2018-12-10 20:21:31 +01:00
Julien Neuhart
152db0551d fixing GoDoc links in badges 2018-12-10 20:11:20 +01:00
Julien Neuhart
3a2467ccfe improving documentation 2018-12-10 20:08:45 +01:00
Julien Neuhart
5a28f3597c fixing typos in documentation 2018-12-10 19:59:45 +01:00
Julien Neuhart
9ce179f91b adding PHP examples in documentation 2018-12-10 19:12:54 +01:00
90 changed files with 4022 additions and 841 deletions

View File

@@ -1,6 +1,6 @@
# Contributing
Hi! Thank your for considering contributing to Gotenberg. You'll
Hi! Thank you for considering contributing to Gotenberg. You'll
find below useful information about how to contribute to the Gotenberg project.
## Contributing code
@@ -8,8 +8,9 @@ find below useful information about how to contribute to the Gotenberg project.
### Install from sources
1. Install and run the latest version of Docker
2. Fork this repository
3. Clone it to your local Go environment (requires *Go* >= 1.11)
2. Verify your Go version (>= 1.11)
3. Fork this repository
4. Clone it outside of your `GOPATH` (we're using Go modules)
### Working with git

View File

@@ -11,13 +11,15 @@ env:
- secure: CmLh169xwt3OC7l19lDUwdZYfw+l/+XXKjTGxnuJiXHa9hAH+nc4mEUSmqHMHwjquyYE6LnSVGZ94LH8sCaGed/TMm0Lj3DZtZ0VsjYu3sKsNrVJn5WdYEah+GvNJ8i8uzvHnGk1L2fAagLpXFUB2K9UraA1CFiHs1fF5Fua7Ra0mE7lMz0MFvyvzcmc9u8nMz71K2YVsXqn+I/rv8onJCTtc/Ds0hVrBXwG5Fh0T3xHqjOhME9ZI2hRHnnQVdBojSY3USifQ//qAKowfBVc0pebEtgFpyXx/I17A6xCm0KVtnA2tNqu1+XFkjWaBzryaLE9ujhJcTRC/YK0JUZ+QxfYbD5C7jVy9Jlx1lOEjJ42B/ImNxPP+9p3wi2MWNbcbZJPxIe635fnT17lI7CLK2Qg1xjfTQ4WTEKW5IhnYMiyfRKiripFh4PCkDm8cJlTbpntsX1n0uheydbsgf/11vS7o1WGhVQ8+Jelc4DvIbBHCqienQEOLJDBwm7FiK5JbwZJ6ZAGeDM/IhGU1h9PNjVbHUyJhUxYoPYUE9QsZFT7ENCrRQpq49KOzXBRfffyJNJRh+Ep9X5YoW0dUp0MNu5B7z2+6w9X9n/C+loa11xF42bFzniCvvbSOQ37J+EngDinVxJkNPeyivR4fye2ENhIAjAEjouTJLOSXL9nAOU=
stages:
- lint
- tests
- publish
jobs:
include:
- stage: lint
- stage: tests
script: make lint
- stage: tests
script: make tests
- stage: publish
if: tag IS present
script: ./scripts/publish.sh $TRAVIS_TAG $DOCKER_USER $DOCKER_PASS
script: make publish VERSION=$TRAVIS_TAG DOCKER_USER=$DOCKER_USER DOCKER_PASSWORD=$DOCKER_PASS

View File

@@ -1,6 +1,6 @@
MIT License
Copyright (c) 2018 TheCodingMachine
Copyright (c) 2019 TheCodingMachine
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@@ -1,9 +1,12 @@
VERSION=snapshot
GOLANG_VERSION=1.11.2
VERSION=snapshot
DOCKER_USER=
DOCKER_PASSWORD=
# generate documentation.
doc:
static-docs --in build/docs --out docs --theme gotenberg --title Gotenberg --subtitle "A Docker-powered stateless API for converting HTML, Markdown and Office documents to PDF."
docker build --build-arg GOLANG_VERSION=$(GOLANG_VERSION) -t thecodingmachine/gotenberg:docs -f build/docs/Dockerfile .
docker run --rm -it -v "$(PWD):/docs" thecodingmachine/gotenberg:docs
# gofmt and goimports all go files.
fmt:
@@ -27,4 +30,8 @@ image:
# start the API using previously built Docker image.
gotenberg:
docker run -it --rm -p "3000:3000" thecodingmachine/gotenberg:$(VERSION)
docker run -it --rm -p "3000:3000" thecodingmachine/gotenberg:$(VERSION)
# publish Gotenberg images according to version.
publish:
./scripts/publish.sh $(GOLANG_VERSION) $(VERSION) $(DOCKER_USER) $(DOCKER_PASSWORD)

View File

@@ -1,8 +1,9 @@
<p align="center">
<img src="https://user-images.githubusercontent.com/8983173/49701110-4c8b9280-fbe8-11e8-895c-a4b9c7d2515b.png" alt="Gotenberg logo" width="250" height="250" />
<img src="https://user-images.githubusercontent.com/8983173/50009948-84b01e00-ffb8-11e8-850b-fc240382c626.png" alt="Gotenberg logo" width="250" height="250" />
</p>
<h3 align="center">Gotenberg</h3>
<p align="center">A Docker-powered stateless API for converting HTML, Markdown and Office documents to PDF.</p>
<p align="center"><a href="https://thecodingmachine.github.io/gotenberg">Documentation</a> &#183; <a href="/.github/CONTRIBUTING.md">Contributing</a></p>
---
@@ -11,18 +12,18 @@ At TheCodingMachine, we build a lot of web applications (intranets, extranets an
## Features
* HTML and Markdown conversions using Google Chrome headless
* Office conversions (.docx, .doc, .odt, .pptx, .ppt, .odp and so on) using [unoconv](https://github.com/dagwieers/unoconv)
* Office conversions (.txt, .rtf, .docx, .doc, .odt, .pptx, .ppt, .odp and so on) using [unoconv](https://github.com/dagwieers/unoconv)
* Performance :zap:: Google Chrome and Libreoffice (unoconv) started once in the background thanks to PM2
* Failure prevention :broken_heart:: PM2 automatically restarts previous processes if they fail
* Assets :package:: 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](/pkg) and [PHP](https://github.com/thecodingmachine/gotenberg-php-client) (**WIP**) libraries
* Easily interact with the API using our [Go](/pkg) and [PHP](https://github.com/thecodingmachine/gotenberg-php-client) libraries
## Quick start
Open a terminal and run the following command:
```bash
$ docker run --rm -p 3000:3000 thecodingmachine/gotenberg:3
$ docker run --rm -p 3000:3000 thecodingmachine/gotenberg:4
```
The API is now available on your host at `http://localhost:3000`.
@@ -32,9 +33,9 @@ to learn how to interact with it!
## Badges
[![Docker image layers](https://images.microbadger.com/badges/image/thecodingmachine/gotenberg:3.svg)](https://microbadger.com/images/thecodingmachine/gotenberg:3)
[![Docker image layers](https://images.microbadger.com/badges/image/thecodingmachine/gotenberg:4.svg)](https://microbadger.com/images/thecodingmachine/gotenberg:4)
[![Travis CI](https://travis-ci.org/thecodingmachine/gotenberg.svg?branch=master)](https://travis-ci.org/thecodingmachine/gotenberg)
[![GoDoc](https://godoc.org/github.com/thecodingmachine/gotenberg?status.svg)](https://godoc.org/github.com/thecodingmachine/gotenberg?status.svg)
[![GoDoc](https://godoc.org/github.com/thecodingmachine/gotenberg?status.svg)](https://godoc.org/github.com/thecodingmachine/gotenberg)
[![Go Report Card](https://goreportcard.com/badge/github.com/thecodingmachine/gotenberg)](https://goreportcard.com/report/thecodingmachine/gotenberg)
---

View File

@@ -1,6 +1,5 @@
FROM debian:stretch-slim
LABEL authors="Julien Neuhart <j.neuhart@thecodingmachine.com>"
FROM thecodingmachine/gotenberg:3.2.0 AS hack
FROM debian:9.5-slim
# |--------------------------------------------------------------------------
# | Common libraries
@@ -11,7 +10,7 @@ LABEL authors="Julien Neuhart <j.neuhart@thecodingmachine.com>"
RUN echo "deb http://httpredir.debian.org/debian/ stretch main contrib non-free" > /etc/apt/sources.list &&\
apt-get update &&\
apt-get install -y curl wget
apt-get install -y curl wget python3-pip ttf-mscorefonts-installer
# |--------------------------------------------------------------------------
# | PM2
@@ -21,7 +20,19 @@ RUN echo "deb http://httpredir.debian.org/debian/ stretch main contrib non-free"
# | recovering. In our case: Chrome (headless) and Office (headless).
# |
RUN curl -sL https://raw.githubusercontent.com/Unitech/pm2/master/packager/setup.deb.sh | bash -
# Yep, this is dirty. The following script does not work anymore (see https://github.com/Unitech/pm2/issues/4127).
#RUN curl -sL https://raw.githubusercontent.com/Unitech/pm2/master/packager/setup.deb.sh | bash -
# Installing PM2 with Node.js and npm breaks something which prevents Google Chrome to work as expected.
# So we just copy all PM2 related files from a previous version of Gotenberg.
RUN curl -sL https://deb.nodesource.com/setup_9.x | bash - &&\
apt-get install -y nodejs
COPY --from=hack /usr/bin/pm2 /usr/bin/pm2
COPY --from=hack /usr/share/pm2 /usr/share/pm2
COPY --from=hack /etc/default/pm2 /etc/default/pm2
COPY --from=hack /etc/systemd/system/pm2.service /etc/systemd/system/pm2.service
# |--------------------------------------------------------------------------
# | Chrome
@@ -39,7 +50,55 @@ RUN wget -q -O - https://dl.google.com/linux/linux_signing_key.pub | apt-key add
# | Unoconv
# |--------------------------------------------------------------------------
# |
# | Installs MS fonts and unoconv for Office documents conversions.
# | Installs unoconv and LibreOffice for Office documents conversions.
# |
RUN apt-get -y install ttf-mscorefonts-installer unoconv
RUN pip3 install unoconv &&\
# https://github.com/nextcloud/docker/issues/380
mkdir -p /usr/share/man/man1mkdir -p /usr/share/man/man1 &&\
apt-get -y install libreoffice
# |--------------------------------------------------------------------------
# | PDFtk
# |--------------------------------------------------------------------------
# |
# | Installs PDFtk as an alternative to pdfcpu for merging PDFs.
# | https://github.com/thecodingmachine/gotenberg/issues/29
# |
RUN apt-get -y install pdftk
# |--------------------------------------------------------------------------
# | Fonts
# |--------------------------------------------------------------------------
# |
# | Installs a handful of fonts.
# | Note: ttf-mscorefonts-installer are installed on top of this Dockerfile.
# |
# Credits:
# https://github.com/arachnys/athenapdf/blob/master/cli/Dockerfile
# https://help.accusoft.com/PrizmDoc/v12.1/HTML/Installing_Asian_Fonts_on_Ubuntu_and_Debian.html
RUN apt-get install -y \
culmus \
fonts-beng \
fonts-hosny-amiri \
fonts-lklug-sinhala \
fonts-lohit-guru \
fonts-lohit-knda \
fonts-samyak-gujr \
fonts-samyak-mlym \
fonts-samyak-taml \
fonts-sarai \
fonts-sil-abyssinica \
fonts-sil-padauk \
fonts-telu \
fonts-thai-tlwg \
ttf-liberation \
ttf-wqy-zenhei \
fonts-arphic-uming \
fonts-ipafont-mincho \
fonts-ipafont-gothic \
fonts-unfonts-core
COPY build/base/fonts.conf /etc/fonts/conf.d/100-gotenberg.conf

29
build/base/fonts.conf Normal file
View File

@@ -0,0 +1,29 @@
<?xml version='1.0'?>
<!DOCTYPE fontconfig SYSTEM 'fonts.dtd'>
<fontconfig>
<match target="font">
<edit mode="assign" name="rgba">
<const>rgb</const>
</edit>
</match>
<match target="font">
<edit mode="assign" name="hinting">
<bool>true</bool>
</edit>
</match>
<match target="font">
<edit mode="assign" name="hintstyle">
<const>hintslight</const>
</edit>
</match>
<match target="font">
<edit mode="assign" name="antialias">
<bool>true</bool>
</edit>
</match>
<match target="font">
<edit mode="assign" name="lcdfilter">
<const>lcddefault</const>
</edit>
</match>
</fontconfig>

View File

@@ -1,72 +0,0 @@
---
title: Office
---
Gotenberg provides the endpoint `/convert/office` for Office documents 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:
* `.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](#scalability) to find how to mitigate this issue.
### Guzzle
```bash
$ curl --request POST \
--url http://localhost:3000/convert/office \
--header 'Content-Type: multipart/form-data' \
--form files=@document.docx
--form files=@document2.docx
> result.pdf
```
### Go
```golang
import "github.com/thecodingmachine/gotenberg/pkg"
func main() {
c := &gotenberg.Client{Hostname: "http://localhost:3000"}
req := &gotenberg.OfficeRequest{
FilePaths: []string{
"document.docx",
"document2.docx",
},
}
dest := "result.pdf"
c.Store(req, dest)
}
```
### PHP
TODO
## Fonts
By default, only `ttf-mscorefonts` fonts are installed.
If you wish to use more fonts, you will have to create your own image:
```Dockerfile
FROM thecodingmachine/gotenberg:3
RUN apt-get -y install yourfonts
```

View File

@@ -1,45 +0,0 @@
---
title: Merge
---
Gotenberg provides the endpoint `/merge` for merging PDFs.
It accepts `POST` requests with a `multipart/form-data` Content-Type.
## Basic
Nothing special here: you may send one or more PDF files and the API
will merge them and return the resulting PDF file.
### Guzzle
```bash
$ curl --request POST \
--url http://localhost:3000/merge \
--header 'Content-Type: multipart/form-data' \
--form files=@file.pdf
--form files=@file2.pdf
> result.pdf
```
### Go
```golang
import "github.com/thecodingmachine/gotenberg/pkg"
func main() {
c := &gotenberg.Client{Hostname: "http://localhost:3000"}
req := &gotenberg.MergeRequest{
FilePaths: []string{
"file.pdf",
"file2.pdf",
},
}
dest := "result.pdf"
c.Store(req, dest)
}
```
### PHP
TODO

View File

@@ -1,45 +0,0 @@
---
title: 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 done before the conversions are actually done!
## Examples
### Guzzle
```bash
$ 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/'
> result.pdf
```
### Go
```golang
import "github.com/thecodingmachine/gotenberg/pkg"
func main() {
c := &gotenberg.Client{Hostname: "http://localhost:3000"}
req := &gotenberg.HTMLRequest{
IndexFilePath: "index.html",
Options: &gotenberg.HTMLOptions{
WebHookURL: "http://myapp.com/webhook/",
},
}
dest := "result.pdf"
resp, err := c.Post(req)
}
```
### PHP
TODO

24
build/docs/Dockerfile Normal file
View File

@@ -0,0 +1,24 @@
ARG GOLANG_VERSION
FROM golang:${GOLANG_VERSION}-stretch
# |--------------------------------------------------------------------------
# | static
# |--------------------------------------------------------------------------
# |
# | Installs the static site anti-framework general-purpose library,
# | purpose-built commands for various domains.
# |
RUN go get github.com/apex/static/cmd/static-docs
# |--------------------------------------------------------------------------
# | Final touch
# |--------------------------------------------------------------------------
# |
# | Last instructions of this build.
# |
WORKDIR /docs
CMD [ "static-docs", "--in", "build/docs/content", "--out", "docs", "--theme", "gotenberg", "--title", "Gotenberg", "--subtitle", "A Docker-powered stateless API for converting HTML, Markdown and Office documents to PDF." ]

View File

@@ -5,7 +5,7 @@ title: Introduction
[Gotenberg](https://github.com/thecodingmachine/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 (.docx, .doc, .odt, .pptx, .ppt, .odp and so on) using [unoconv](https://github.com/dagwieers/unoconv)
* Office conversions (.txt, .rtf, .docx, .doc, .odt, .pptx, .ppt, .odp and so on) using [unoconv](https://github.com/dagwieers/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!

View File

@@ -7,7 +7,7 @@ Gotenberg is shipped within a Docker image.
You may start it with:
```bash
$ docker run --rm -p 3000:3000 thecodingmachine/gotenberg:3
$ docker run --rm -p 3000:3000 thecodingmachine/gotenberg:4
```
> The API will be available at [http://localhost:3000](http://localhost:3000).
@@ -22,7 +22,7 @@ services:
# your others services
gotenberg:
image: thecodingmachine/gotenberg:3
image: thecodingmachine/gotenberg:4
```
> The API will be available under `gotenberg:3000` in your Docker Compose network.

View File

@@ -26,14 +26,14 @@ For instance:
</html>
```
### Guzzle
### cURL
```bash
$ curl --request POST \
--url http://localhost:3000/convert/html \
--header 'Content-Type: multipart/form-data' \
--form files=@index.html
> result.pdf
--form files=@index.html \
-o result.pdf
```
### Go
@@ -43,9 +43,7 @@ import "github.com/thecodingmachine/gotenberg/pkg"
func main() {
c := &gotenberg.Client{Hostname: "http://localhost:3000"}
req := &gotenberg.HTMLRequest{
IndexFilePath: "index.html",
}
req, _ := gotenberg.NewHTMLRequest("index.html")
dest := "result.pdf"
c.Store(req, dest)
}
@@ -53,7 +51,17 @@ func main() {
### PHP
TODO
```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
@@ -90,16 +98,16 @@ The following classes allow you to inject printing values:
> **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`.
### Guzzle
### cURL
```bash
$ 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
> result.pdf
--form files=@index.html \
--form files=@header.html \
--form files=@footer.html \
-o result.pdf
```
### Go
@@ -109,13 +117,9 @@ import "github.com/thecodingmachine/gotenberg/pkg"
func main() {
c := &gotenberg.Client{Hostname: "http://localhost:3000"}
req := &gotenberg.HTMLRequest{
IndexFilePath: "index.html",
Options: &gotenberg.HTMLOptions{
HeaderFilePath: "header.html",
FooterFilePath: "footer.html",
},
}
req, _ := gotenberg.NewHTMLRequest("index.html")
req.SetHeader("header.html")
req.SetFooter("footer.html")
dest := "result.pdf"
c.Store(req, dest)
}
@@ -123,11 +127,25 @@ func main() {
### PHP
TODO
```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.
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.
@@ -167,19 +185,22 @@ But this won't:
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](#office.fonts).
> see to the [fonts section](#fonts).
>
> For web fonts (Google fonts), there is a timeout of 500ms by default. You may update
> this value thanks to the form field `webFontsTimeout`.
### Guzzle
### cURL
```bash
$ 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
> result.pdf
--form files=@index.html \
--form files=@style.css \
--form files=@img.png \
--form files=@font.woff \
-o result.pdf
```
### Go
@@ -189,14 +210,8 @@ import "github.com/thecodingmachine/gotenberg/pkg"
func main() {
c := &gotenberg.Client{Hostname: "http://localhost:3000"}
req := &gotenberg.HTMLRequest{
IndexFilePath: "index.html",
AssetFilePaths: []string{
"style.css",
"img.png",
"font.woff",
},
}
req, _ := gotenberg.NewHTMLRequest("index.html")
req.SetAssets("font.woff", "img.gif", "style.css")
dest := "result.pdf"
c.Store(req, dest)
}
@@ -204,32 +219,48 @@ func main() {
### PHP
TODO
```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 paper.
You may also customize the resulting PDF format.
By default, it will be rendered in `A4` size with `1 inch` margins and `portrait` orientation.
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 both `paperWidth` and `paperHeight`. Same for margins.
### Guzzle
### cURL
```bash
$ 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
> result.pdf
--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
@@ -239,14 +270,10 @@ import "github.com/thecodingmachine/gotenberg/pkg"
func main() {
c := &gotenberg.Client{Hostname: "http://localhost:3000"}
req := &gotenberg.HTMLRequest{
IndexFilePath: "index.html",
Options: &gotenberg.HTMLOptions{
PaperSize: gotenberg.A4,
PaperMargins: gotenberg.NoMargins,
Landscape: true
},
}
req, _ := gotenberg.NewHTMLRequest("index.html")
req.SetPaperSize(gotenberg.A4)
req.SetMargins(gotenberg.NoMargins)
req.SetLandscape(true)
dest := "result.pdf"
c.Store(req, dest)
}
@@ -254,4 +281,18 @@ func main() {
### PHP
TODO
```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);
```

View File

@@ -0,0 +1,56 @@
---
title: 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
```bash
$ 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
```golang
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)
}
```
### PHP
```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);
```

View File

@@ -11,7 +11,7 @@ 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` which will convert a given markdown file to HTML.
in the file `index.html`. This function will convert a given markdown file to HTML.
For instance:
@@ -28,15 +28,15 @@ For instance:
</html>
```
### Guzzle
### cURL
```bash
$ curl --request POST \
--url http://localhost:3000/convert/markdown \
--header 'Content-Type: multipart/form-data' \
--form files=@index.html
--form files=@file.md
> result.pdf
--form files=@index.html \
--form files=@file.md \
-o result.pdf
```
### Go
@@ -46,12 +46,7 @@ import "github.com/thecodingmachine/gotenberg/pkg"
func main() {
c := &gotenberg.Client{Hostname: "http://localhost:3000"}
req := &gotenberg.MarkdownRequest{
IndexFilePath: "index.html",
MarkdownFilePaths: []string{
"file.md",
},
}
req, _ := gotenberg.NewMarkdownRequest("index.html", "file.md")
dest := "result.pdf"
c.Store(req, dest)
}
@@ -59,4 +54,17 @@ func main() {
### PHP
TODO
```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);
```

View File

@@ -0,0 +1,119 @@
---
title: 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](#scalability) to find how to mitigate this issue.
### cURL
```bash
$ 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
```golang
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)
}
```
### PHP
```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
```bash
$ 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
```golang
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)
}
```
### PHP
```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);
```

View File

@@ -0,0 +1,55 @@
---
title: Merge
---
Gotenberg provides the endpoint `/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
```bash
$ 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
```
### Go
```golang
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)
}
```
### PHP
```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);
```

View File

@@ -0,0 +1,50 @@
---
title: 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
```bash
$ 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
```golang
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)
}
```
### PHP
```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);
```

View File

@@ -14,7 +14,7 @@ services:
# your others services
gotenberg:
image: thecodingmachine/gotenberg:3
image: thecodingmachine/gotenberg:4
```
You may now launch your services using:

View File

@@ -0,0 +1,11 @@
---
title: Liveness
---
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:
* [Pod lifecycle](https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#container-probes)
* [Configure Liveness and Readiness Probes](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-probes/)

View File

@@ -0,0 +1,13 @@
---
title: 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:
```Dockerfile
FROM thecodingmachine/gotenberg:4
RUN apt-get -y install yourfonts
```

View File

@@ -2,8 +2,6 @@ ARG GOLANG_VERSION
FROM golang:${GOLANG_VERSION}-stretch
LABEL authors="Julien Neuhart <j.neuhart@thecodingmachine.com>"
# |--------------------------------------------------------------------------
# | GolangCI-Lint
# |--------------------------------------------------------------------------

View File

@@ -4,8 +4,6 @@ FROM golang:${GOLANG_VERSION}-stretch AS golang
FROM thecodingmachine/gotenberg:base
LABEL authors="Julien Neuhart <j.neuhart@thecodingmachine.com>"
# |--------------------------------------------------------------------------
# | Common libraries
# |--------------------------------------------------------------------------

View File

@@ -21,4 +21,5 @@ go test github.com/thecodingmachine/gotenberg/internal/pkg/pm2 -run TestUnoconvS
go build -o /usr/local/bin/gotenberg cmd/gotenberg/main.go
gotenberg &
sleep 10
go test -race -cover -covermode=atomic github.com/thecodingmachine/gotenberg/pkg
go test -race -cover -covermode=atomic github.com/thecodingmachine/gotenberg/pkg
sleep 5 # allows Gotenberg to remove generated files (concurrent requests).

View File

@@ -1,10 +1,30 @@
<!DOCTYPE html><html><head>
<script async="" src="https://www.googletagmanager.com/gtag/js?id=UA-10196481-9"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'UA-10196481-9');
</script>
<meta charset="utf-8"/>
<meta http-equiv="x-ua-compatible" content="ie=edge"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<title>Gotenberg · A Docker-powered stateless API for converting HTML, Markdown and Office documents to PDF.</title>
<link rel="apple-touch-icon-precomposed" sizes="57x57" href="theme/gotenberg/img/apple-touch-icon-57x57.png"/>
<link rel="apple-touch-icon-precomposed" sizes="114x114" href="theme/gotenberg/img/apple-touch-icon-114x114.png"/>
<link rel="apple-touch-icon-precomposed" sizes="72x72" href="theme/gotenberg/img/apple-touch-icon-72x72.png"/>
<link rel="apple-touch-icon-precomposed" sizes="144x144" href="theme/gotenberg/img/apple-touch-icon-144x144.png"/>
<link rel="apple-touch-icon-precomposed" sizes="60x60" href="theme/gotenberg/img/apple-touch-icon-60x60.png"/>
<link rel="apple-touch-icon-precomposed" sizes="120x120" href="theme/gotenberg/img/apple-touch-icon-120x120.png"/>
<link rel="apple-touch-icon-precomposed" sizes="76x76" href="theme/gotenberg/img/apple-touch-icon-76x76.png"/>
<link rel="apple-touch-icon-precomposed" sizes="152x152" href="theme/gotenberg/img/apple-touch-icon-152x152.png"/>
<link rel="icon" type="image/png" href="theme/gotenberg/img/favicon-196x196.png" sizes="196x196"/>
<link rel="icon" type="image/png" href="theme/gotenberg/img/favicon-96x96.png" sizes="96x96"/>
<link rel="icon" type="image/png" href="theme/gotenberg/img/favicon-32x32.png" sizes="32x32"/>
<link rel="icon" type="image/png" href="theme/gotenberg/img/favicon-16x16.png" sizes="16x16"/>
<link rel="icon" type="image/png" href="theme/gotenberg/img/favicon-16x16.png" sizes="16x16"/>
<link rel="icon" type="image/png" href="theme/gotenberg/img/favicon-128.png" sizes="128x128"/>
<link rel="stylesheet" href="theme/gotenberg/css/index.css"/>
</head>
<body>
@@ -12,7 +32,7 @@
<div class="Container">
<div class="Header">
<div class="Title center">
<img src="https://user-images.githubusercontent.com/8983173/49701110-4c8b9280-fbe8-11e8-895c-a4b9c7d2515b.png" alt="Gotenberg logo" width="250" height="250"/>
<img src="https://user-images.githubusercontent.com/8983173/50009948-84b01e00-ffb8-11e8-850b-fc240382c626.png" alt="Gotenberg logo" width="250" height="250"/>
<span class="text">Gotenberg</span>
<span class="subtext">A Docker-powered stateless API for converting HTML, Markdown and Office documents to PDF.</span>
</div>
@@ -34,6 +54,10 @@
<a href="#html">HTML</a>
</div>
<div class="item">
<a href="#url">URL</a>
</div>
<div class="item">
<a href="#markdown">Markdown</a>
</div>
@@ -54,6 +78,14 @@
<a href="#scalability">Scalability</a>
</div>
<div class="item">
<a href="#liveness">Liveness</a>
</div>
<div class="item">
<a href="#fonts">Fonts</a>
</div>
<div class="item">
<a href="#links">Links</a>
</div>
@@ -71,7 +103,7 @@
<ul>
<li>HTML and Markdown conversions using Google Chrome headless</li>
<li>Office conversions (.docx, .doc, .odt, .pptx, .ppt, .odp and so on) using <a href="https://github.com/dagwieers/unoconv">unoconv</a></li>
<li>Office conversions (.txt, .rtf, .docx, .doc, .odt, .pptx, .ppt, .odp and so on) using <a href="https://github.com/dagwieers/unoconv">unoconv</a></li>
<li>Performance: Google Chrome and Libreoffice (unoconv) started once in the background thanks to PM2</li>
<li>Failure prevention: PM2 automatically restarts previous processes if they fail</li>
<li>Assets: send your header, footer, images, fonts, stylesheets and so on for converting your HTML and Markdown to beaufitul PDFs!</li>
@@ -88,7 +120,7 @@
<p>You may start it with:</p>
<pre class="chroma">$ docker run --rm -p <span class="m">3000</span>:3000 thecodingmachine/gotenberg:3
<pre class="chroma">$ docker run --rm -p <span class="m">3000</span>:3000 thecodingmachine/gotenberg:4
</pre>
<blockquote>
@@ -104,8 +136,8 @@
</span><span class="w"> </span><span class="c"># your others services</span><span class="w">
</span><span class="w">
</span><span class="w"> </span>gotenberg<span class="p">:</span><span class="w">
</span><span class="w"> </span>image<span class="p">:</span><span class="w"> </span>thecodingmachine/gotenberg<span class="p">:</span><span class="m">3</span><span class="w">
</span><span class="w"></span></pre>
</span><span class="w"> </span>image<span class="p">:</span><span class="w"> </span>thecodingmachine/gotenberg<span class="p">:</span><span class="m">4</span><span class="w">
</span></pre>
<blockquote>
<p>The API will be available under <code>gotenberg:3000</code> in your Docker Compose network.</p>
@@ -157,26 +189,26 @@ which will be converted to PDF.</p>
<p>For instance:</p>
<pre class="chroma"><span class="cp">&lt;!doctype html&gt;</span>
<span class="p">&lt;</span><span class="nt">html</span> <span class="na">lang</span><span class="o">=</span><span class="s">&#34;en&#34;</span><span class="p"></span><span class="p">&gt;</span>
<span class="p">&lt;</span><span class="nt">head</span><span class="p"></span><span class="p">&gt;</span>
<span class="p">&lt;</span><span class="nt">meta</span> <span class="na">charset</span><span class="o">=</span><span class="s">&#34;utf-8&#34;</span><span class="p"></span><span class="p">&gt;</span>
<span class="p">&lt;</span><span class="nt">title</span><span class="p"></span><span class="p">&gt;</span>My PDF<span class="p">&lt;</span><span class="p">/</span><span class="nt">title</span><span class="p">&gt;</span>
<span class="p">&lt;</span><span class="p">/</span><span class="nt">head</span><span class="p">&gt;</span>
<span class="p">&lt;</span><span class="nt">body</span><span class="p"></span><span class="p">&gt;</span>
<span class="p">&lt;</span><span class="nt">h1</span><span class="p"></span><span class="p">&gt;</span>Hello world!<span class="p">&lt;</span><span class="p">/</span><span class="nt">h1</span><span class="p">&gt;</span>
<span class="p">&lt;</span><span class="p">/</span><span class="nt">body</span><span class="p">&gt;</span>
<span class="p">&lt;</span><span class="p">/</span><span class="nt">html</span><span class="p">&gt;</span>
<span class="p">&lt;</span><span class="nt">html</span> <span class="na">lang</span><span class="o">=</span><span class="s">&#34;en&#34;</span><span class="p">&gt;</span>
<span class="p">&lt;</span><span class="nt">head</span><span class="p">&gt;</span>
<span class="p">&lt;</span><span class="nt">meta</span> <span class="na">charset</span><span class="o">=</span><span class="s">&#34;utf-8&#34;</span><span class="p">&gt;</span>
<span class="p">&lt;</span><span class="nt">title</span><span class="p">&gt;</span>My PDF<span class="p">&lt;/</span><span class="nt">title</span><span class="p">&gt;</span>
<span class="p">&lt;/</span><span class="nt">head</span><span class="p">&gt;</span>
<span class="p">&lt;</span><span class="nt">body</span><span class="p">&gt;</span>
<span class="p">&lt;</span><span class="nt">h1</span><span class="p">&gt;</span>Hello world!<span class="p">&lt;/</span><span class="nt">h1</span><span class="p">&gt;</span>
<span class="p">&lt;/</span><span class="nt">body</span><span class="p">&gt;</span>
<span class="p">&lt;/</span><span class="nt">html</span><span class="p">&gt;</span>
</pre>
<h3 class="Heading"><a class="Anchor" aria-hidden="true" id="html.basic.guzzle" href="#html.basic.guzzle">
<h3 class="Heading"><a class="Anchor" aria-hidden="true" id="html.basic.c_url" href="#html.basic.c_url">
<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-link"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"></path><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"></path></svg>
</a>Guzzle</h3>
</a>cURL</h3>
<pre class="chroma">$ curl --request POST <span class="se">\
</span><span class="se"></span> --url http://localhost:3000/convert/html <span class="se">\
</span><span class="se"></span> --header <span class="s1">&#39;Content-Type: multipart/form-data&#39;</span> <span class="se">\
</span><span class="se"></span> --form <span class="nv">files</span><span class="o">=</span>@index.html
&gt; result.pdf
</span><span class="se"></span> --form <span class="nv">files</span><span class="o">=</span>@index.html <span class="se">\
</span><span class="se"></span> -o result.pdf
</pre>
<h3 class="Heading"><a class="Anchor" aria-hidden="true" id="html.basic.go" href="#html.basic.go">
@@ -185,13 +217,11 @@ which will be converted to PDF.</p>
<pre class="chroma"><span class="kn">import</span> <span class="s">&#34;github.com/thecodingmachine/gotenberg/pkg&#34;</span>
<span class="kd">func</span> <span class="nx">main</span><span class="p">()</span> <span class="p">{</span>
<span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
<span class="nx">c</span> <span class="o">:=</span> <span class="o">&amp;</span><span class="nx">gotenberg</span><span class="p">.</span><span class="nx">Client</span><span class="p">{</span><span class="nx">Hostname</span><span class="p">:</span> <span class="s">&#34;http://localhost:3000&#34;</span><span class="p">}</span>
<span class="nx">req</span> <span class="o">:=</span> <span class="o">&amp;</span><span class="nx">gotenberg</span><span class="p">.</span><span class="nx">HTMLRequest</span><span class="p">{</span>
<span class="nx">IndexFilePath</span><span class="p">:</span> <span class="s">&#34;index.html&#34;</span><span class="p">,</span>
<span class="p">}</span>
<span class="nx">req</span><span class="p">,</span> <span class="nx">_</span> <span class="o">:=</span> <span class="nx">gotenberg</span><span class="p">.</span><span class="nf">NewHTMLRequest</span><span class="p">(</span><span class="s">&#34;index.html&#34;</span><span class="p">)</span>
<span class="nx">dest</span> <span class="o">:=</span> <span class="s">&#34;result.pdf&#34;</span>
<span class="nx">c</span><span class="p">.</span><span class="nx">Store</span><span class="p">(</span><span class="nx">req</span><span class="p">,</span> <span class="nx">dest</span><span class="p">)</span>
<span class="nx">c</span><span class="p">.</span><span class="nf">Store</span><span class="p">(</span><span class="nx">req</span><span class="p">,</span> <span class="nx">dest</span><span class="p">)</span>
<span class="p">}</span>
</pre>
@@ -199,7 +229,16 @@ which will be converted to PDF.</p>
<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-link"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"></path><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"></path></svg>
</a>PHP</h3>
<p>TODO</p>
<pre class="chroma">use TheCodingMachine\Gotenberg\Client;
use TheCodingMachine\Gotenberg\DocumentFactory;
use TheCodingMachine\Gotenberg\HTMLRequest;
$client = new Client(&#39;http://localhost:3000&#39;, new \Http\Adapter\Guzzle6\Client());
$index = DocumentFactory::makeFromPath(&#39;index.html&#39;, &#39;index.html&#39;);
$request = new HTMLRequest($index);
$dirPath = &#34;/foo&#34;;
$filename = $client-&gt;store($request, $dirPath);
</pre>
<h2 class="Heading"><a class="Anchor" aria-hidden="true" id="html.header_and_footer" href="#html.header_and_footer">
<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-link"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"></path><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"></path></svg>
@@ -210,21 +249,21 @@ Respectively, a file named <code>header.html</code> and <code>footer.html</code>
<p>Each of them <strong>has to be a complete HTML document</strong>:</p>
<pre class="chroma"><span class="p">&lt;</span><span class="nt">html</span><span class="p"></span><span class="p">&gt;</span>
<span class="p">&lt;</span><span class="nt">head</span><span class="p"></span><span class="p">&gt;</span>
<span class="p">&lt;</span><span class="nt">style</span><span class="p"></span><span class="p">&gt;</span>
<pre class="chroma"><span class="p">&lt;</span><span class="nt">html</span><span class="p">&gt;</span>
<span class="p">&lt;</span><span class="nt">head</span><span class="p">&gt;</span>
<span class="p">&lt;</span><span class="nt">style</span><span class="p">&gt;</span>
<span class="nt">body</span> <span class="p">{</span>
<span class="k">font-size</span><span class="p">:</span> <span class="mi">8</span><span class="kt">rem</span><span class="p">;</span>
<span class="k">margin</span><span class="p">:</span> <span class="mi">4</span><span class="kt">rem</span> <span class="kc">auto</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">&lt;</span><span class="p">/</span><span class="nt">style</span><span class="p">&gt;</span>
<span class="p">&lt;</span><span class="p">/</span><span class="nt">head</span><span class="p">&gt;</span>
<span class="p">&lt;</span><span class="nt">body</span><span class="p"></span><span class="p">&gt;</span>
<span class="p">&lt;</span><span class="nt">p</span><span class="p"></span><span class="p">&gt;</span>
<span class="p">&lt;</span><span class="nt">span</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;pageNumber&#34;</span><span class="p"></span><span class="p">&gt;&lt;</span><span class="p">/</span><span class="nt">span</span><span class="p">&gt;</span> of <span class="p">&lt;</span><span class="nt">span</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;totalPages&#34;</span><span class="p"></span><span class="p">&gt;&lt;</span><span class="p">/</span><span class="nt">span</span><span class="p">&gt;</span>
<span class="p">&lt;</span><span class="p">/</span><span class="nt">p</span><span class="p">&gt;</span>
<span class="p">&lt;</span><span class="p">/</span><span class="nt">body</span><span class="p">&gt;</span>
<span class="p">&lt;</span><span class="p">/</span><span class="nt">html</span><span class="p">&gt;</span>
<span class="p">&lt;/</span><span class="nt">style</span><span class="p">&gt;</span>
<span class="p">&lt;/</span><span class="nt">head</span><span class="p">&gt;</span>
<span class="p">&lt;</span><span class="nt">body</span><span class="p">&gt;</span>
<span class="p">&lt;</span><span class="nt">p</span><span class="p">&gt;</span>
<span class="p">&lt;</span><span class="nt">span</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;pageNumber&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">span</span><span class="p">&gt;</span> of <span class="p">&lt;</span><span class="nt">span</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;totalPages&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">span</span><span class="p">&gt;</span>
<span class="p">&lt;/</span><span class="nt">p</span><span class="p">&gt;</span>
<span class="p">&lt;/</span><span class="nt">body</span><span class="p">&gt;</span>
<span class="p">&lt;/</span><span class="nt">html</span><span class="p">&gt;</span>
</pre>
<p>The following classes allow you to inject printing values:</p>
@@ -241,17 +280,17 @@ Respectively, a file named <code>header.html</code> and <code>footer.html</code>
Also, <code>footer.html</code> CSS properties override the ones from <code>header.html</code>.</p>
</blockquote>
<h3 class="Heading"><a class="Anchor" aria-hidden="true" id="html.header_and_footer.guzzle" href="#html.header_and_footer.guzzle">
<h3 class="Heading"><a class="Anchor" aria-hidden="true" id="html.header_and_footer.c_url" href="#html.header_and_footer.c_url">
<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-link"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"></path><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"></path></svg>
</a>Guzzle</h3>
</a>cURL</h3>
<pre class="chroma">$ curl --request POST <span class="se">\
</span><span class="se"></span> --url http://localhost:3000/convert/html <span class="se">\
</span><span class="se"></span> --header <span class="s1">&#39;Content-Type: multipart/form-data&#39;</span> <span class="se">\
</span><span class="se"></span> --form <span class="nv">files</span><span class="o">=</span>@index.html
--form <span class="nv">files</span><span class="o">=</span>@header.html
--form <span class="nv">files</span><span class="o">=</span>@footer.html
&gt; result.pdf
</span><span class="se"></span> --form <span class="nv">files</span><span class="o">=</span>@index.html <span class="se">\
</span><span class="se"></span> --form <span class="nv">files</span><span class="o">=</span>@header.html <span class="se">\
</span><span class="se"></span> --form <span class="nv">files</span><span class="o">=</span>@footer.html <span class="se">\
</span><span class="se"></span> -o result.pdf
</pre>
<h3 class="Heading"><a class="Anchor" aria-hidden="true" id="html.header_and_footer.go" href="#html.header_and_footer.go">
@@ -260,17 +299,13 @@ Also, <code>footer.html</code> CSS properties override the ones from <code>heade
<pre class="chroma"><span class="kn">import</span> <span class="s">&#34;github.com/thecodingmachine/gotenberg/pkg&#34;</span>
<span class="kd">func</span> <span class="nx">main</span><span class="p">()</span> <span class="p">{</span>
<span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
<span class="nx">c</span> <span class="o">:=</span> <span class="o">&amp;</span><span class="nx">gotenberg</span><span class="p">.</span><span class="nx">Client</span><span class="p">{</span><span class="nx">Hostname</span><span class="p">:</span> <span class="s">&#34;http://localhost:3000&#34;</span><span class="p">}</span>
<span class="nx">req</span> <span class="o">:=</span> <span class="o">&amp;</span><span class="nx">gotenberg</span><span class="p">.</span><span class="nx">HTMLRequest</span><span class="p">{</span>
<span class="nx">IndexFilePath</span><span class="p">:</span> <span class="s">&#34;index.html&#34;</span><span class="p">,</span>
<span class="nx">Options</span><span class="p">:</span> <span class="o">&amp;</span><span class="nx">gotenberg</span><span class="p">.</span><span class="nx">HTMLOptions</span><span class="p">{</span>
<span class="nx">HeaderFilePath</span><span class="p">:</span> <span class="s">&#34;header.html&#34;</span><span class="p">,</span>
<span class="nx">FooterFilePath</span><span class="p">:</span> <span class="s">&#34;footer.html&#34;</span><span class="p">,</span>
<span class="p">},</span>
<span class="p">}</span>
<span class="nx">req</span><span class="p">,</span> <span class="nx">_</span> <span class="o">:=</span> <span class="nx">gotenberg</span><span class="p">.</span><span class="nf">NewHTMLRequest</span><span class="p">(</span><span class="s">&#34;index.html&#34;</span><span class="p">)</span>
<span class="nx">req</span><span class="p">.</span><span class="nf">SetHeader</span><span class="p">(</span><span class="s">&#34;header.html&#34;</span><span class="p">)</span>
<span class="nx">req</span><span class="p">.</span><span class="nf">SetFooter</span><span class="p">(</span><span class="s">&#34;footer.html&#34;</span><span class="p">)</span>
<span class="nx">dest</span> <span class="o">:=</span> <span class="s">&#34;result.pdf&#34;</span>
<span class="nx">c</span><span class="p">.</span><span class="nx">Store</span><span class="p">(</span><span class="nx">req</span><span class="p">,</span> <span class="nx">dest</span><span class="p">)</span>
<span class="nx">c</span><span class="p">.</span><span class="nf">Store</span><span class="p">(</span><span class="nx">req</span><span class="p">,</span> <span class="nx">dest</span><span class="p">)</span>
<span class="p">}</span>
</pre>
@@ -278,13 +313,26 @@ Also, <code>footer.html</code> CSS properties override the ones from <code>heade
<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-link"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"></path><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"></path></svg>
</a>PHP</h3>
<p>TODO</p>
<pre class="chroma">use TheCodingMachine\Gotenberg\Client;
use TheCodingMachine\Gotenberg\DocumentFactory;
use TheCodingMachine\Gotenberg\HTMLRequest;
$client = new Client(&#39;http://localhost:3000&#39;, new \Http\Adapter\Guzzle6\Client());
$index = DocumentFactory::makeFromPath(&#39;index.html&#39;, &#39;index.html&#39;);
$header = DocumentFactory::makeFromPath(&#39;header.html&#39;, &#39;header.html&#39;);
$footer = DocumentFactory::makeFromPath(&#39;footer.html&#39;, &#39;footer.html&#39;);
$request = new HTMLRequest($index);
$request-&gt;setHeader($header);
$request-&gt;setFooter($footer);
$dirPath = &#34;/foo&#34;;
$filename = $client-&gt;store($request, $dirPath);
</pre>
<h2 class="Heading"><a class="Anchor" aria-hidden="true" id="html.assets" href="#html.assets">
<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-link"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"></path><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"></path></svg>
</a>Assets</h2>
<p>You may also send additional files. For instance, images, fonts, stylesheets and so on.</p>
<p>You may also send additional files. For instance: images, fonts, stylesheets and so on.</p>
<p>The only requirement is to make sure that their paths
are on the same level as the <code>index.html</code> file.</p>
@@ -292,52 +340,55 @@ are on the same level as the <code>index.html</code> file.</p>
<p>In others words, this will work:</p>
<pre class="chroma"><span class="cp">&lt;!doctype html&gt;</span>
<span class="p">&lt;</span><span class="nt">html</span> <span class="na">lang</span><span class="o">=</span><span class="s">&#34;en&#34;</span><span class="p"></span><span class="p">&gt;</span>
<span class="p">&lt;</span><span class="nt">head</span><span class="p"></span><span class="p">&gt;</span>
<span class="p">&lt;</span><span class="nt">meta</span> <span class="na">charset</span><span class="o">=</span><span class="s">&#34;utf-8&#34;</span><span class="p"></span><span class="p">&gt;</span>
<span class="p">&lt;</span><span class="nt">title</span><span class="p"></span><span class="p">&gt;</span>My PDF<span class="p">&lt;</span><span class="p">/</span><span class="nt">title</span><span class="p">&gt;</span>
<span class="p">&lt;</span><span class="p">/</span><span class="nt">head</span><span class="p">&gt;</span>
<span class="p">&lt;</span><span class="nt">body</span><span class="p"></span><span class="p">&gt;</span>
<span class="p">&lt;</span><span class="nt">h1</span><span class="p"></span><span class="p">&gt;</span>Hello world!<span class="p">&lt;</span><span class="p">/</span><span class="nt">h1</span><span class="p">&gt;</span>
<span class="p">&lt;</span><span class="nt">img</span> <span class="na">src</span><span class="o">=</span><span class="s">&#34;img.png&#34;</span><span class="p"></span><span class="p">&gt;</span>
<span class="p">&lt;</span><span class="p">/</span><span class="nt">body</span><span class="p">&gt;</span>
<span class="p">&lt;</span><span class="p">/</span><span class="nt">html</span><span class="p">&gt;</span>
<span class="p">&lt;</span><span class="nt">html</span> <span class="na">lang</span><span class="o">=</span><span class="s">&#34;en&#34;</span><span class="p">&gt;</span>
<span class="p">&lt;</span><span class="nt">head</span><span class="p">&gt;</span>
<span class="p">&lt;</span><span class="nt">meta</span> <span class="na">charset</span><span class="o">=</span><span class="s">&#34;utf-8&#34;</span><span class="p">&gt;</span>
<span class="p">&lt;</span><span class="nt">title</span><span class="p">&gt;</span>My PDF<span class="p">&lt;/</span><span class="nt">title</span><span class="p">&gt;</span>
<span class="p">&lt;/</span><span class="nt">head</span><span class="p">&gt;</span>
<span class="p">&lt;</span><span class="nt">body</span><span class="p">&gt;</span>
<span class="p">&lt;</span><span class="nt">h1</span><span class="p">&gt;</span>Hello world!<span class="p">&lt;/</span><span class="nt">h1</span><span class="p">&gt;</span>
<span class="p">&lt;</span><span class="nt">img</span> <span class="na">src</span><span class="o">=</span><span class="s">&#34;img.png&#34;</span><span class="p">&gt;</span>
<span class="p">&lt;/</span><span class="nt">body</span><span class="p">&gt;</span>
<span class="p">&lt;/</span><span class="nt">html</span><span class="p">&gt;</span>
</pre>
<p>But this wont:</p>
<pre class="chroma"><span class="cp">&lt;!doctype html&gt;</span>
<span class="p">&lt;</span><span class="nt">html</span> <span class="na">lang</span><span class="o">=</span><span class="s">&#34;en&#34;</span><span class="p"></span><span class="p">&gt;</span>
<span class="p">&lt;</span><span class="nt">head</span><span class="p"></span><span class="p">&gt;</span>
<span class="p">&lt;</span><span class="nt">meta</span> <span class="na">charset</span><span class="o">=</span><span class="s">&#34;utf-8&#34;</span><span class="p"></span><span class="p">&gt;</span>
<span class="p">&lt;</span><span class="nt">title</span><span class="p"></span><span class="p">&gt;</span>My PDF<span class="p">&lt;</span><span class="p">/</span><span class="nt">title</span><span class="p">&gt;</span>
<span class="p">&lt;</span><span class="p">/</span><span class="nt">head</span><span class="p">&gt;</span>
<span class="p">&lt;</span><span class="nt">body</span><span class="p"></span><span class="p">&gt;</span>
<span class="p">&lt;</span><span class="nt">h1</span><span class="p"></span><span class="p">&gt;</span>Hello world!<span class="p">&lt;</span><span class="p">/</span><span class="nt">h1</span><span class="p">&gt;</span>
<span class="p">&lt;</span><span class="nt">img</span> <span class="na">src</span><span class="o">=</span><span class="s">&#34;/foo/img.png&#34;</span><span class="p"></span><span class="p">&gt;</span>
<span class="p">&lt;</span><span class="p">/</span><span class="nt">body</span><span class="p">&gt;</span>
<span class="p">&lt;</span><span class="p">/</span><span class="nt">html</span><span class="p">&gt;</span>
<span class="p">&lt;</span><span class="nt">html</span> <span class="na">lang</span><span class="o">=</span><span class="s">&#34;en&#34;</span><span class="p">&gt;</span>
<span class="p">&lt;</span><span class="nt">head</span><span class="p">&gt;</span>
<span class="p">&lt;</span><span class="nt">meta</span> <span class="na">charset</span><span class="o">=</span><span class="s">&#34;utf-8&#34;</span><span class="p">&gt;</span>
<span class="p">&lt;</span><span class="nt">title</span><span class="p">&gt;</span>My PDF<span class="p">&lt;/</span><span class="nt">title</span><span class="p">&gt;</span>
<span class="p">&lt;/</span><span class="nt">head</span><span class="p">&gt;</span>
<span class="p">&lt;</span><span class="nt">body</span><span class="p">&gt;</span>
<span class="p">&lt;</span><span class="nt">h1</span><span class="p">&gt;</span>Hello world!<span class="p">&lt;/</span><span class="nt">h1</span><span class="p">&gt;</span>
<span class="p">&lt;</span><span class="nt">img</span> <span class="na">src</span><span class="o">=</span><span class="s">&#34;/foo/img.png&#34;</span><span class="p">&gt;</span>
<span class="p">&lt;/</span><span class="nt">body</span><span class="p">&gt;</span>
<span class="p">&lt;/</span><span class="nt">html</span><span class="p">&gt;</span>
</pre>
<p>You may also use <em>remote</em> paths for Google fonts, images and so on.</p>
<blockquote>
<p>If you want to install fonts directly in the Gotenberg Docker image,
see to the <a href="#office.fonts">fonts section</a>.</p>
see to the <a href="#fonts">fonts section</a>.</p>
<p>For web fonts (Google fonts), there is a timeout of 500ms by default. You may update
this value thanks to the form field <code>webFontsTimeout</code>.</p>
</blockquote>
<h3 class="Heading"><a class="Anchor" aria-hidden="true" id="html.assets.guzzle" href="#html.assets.guzzle">
<h3 class="Heading"><a class="Anchor" aria-hidden="true" id="html.assets.c_url" href="#html.assets.c_url">
<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-link"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"></path><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"></path></svg>
</a>Guzzle</h3>
</a>cURL</h3>
<pre class="chroma">$ curl --request POST <span class="se">\
</span><span class="se"></span> --url http://localhost:3000/convert/html <span class="se">\
</span><span class="se"></span> --header <span class="s1">&#39;Content-Type: multipart/form-data&#39;</span> <span class="se">\
</span><span class="se"></span> --form <span class="nv">files</span><span class="o">=</span>@index.html
--form <span class="nv">files</span><span class="o">=</span>@style.css
--form <span class="nv">files</span><span class="o">=</span>@img.png
--form <span class="nv">files</span><span class="o">=</span>@font.woff
&gt; result.pdf
</span><span class="se"></span> --form <span class="nv">files</span><span class="o">=</span>@index.html <span class="se">\
</span><span class="se"></span> --form <span class="nv">files</span><span class="o">=</span>@style.css <span class="se">\
</span><span class="se"></span> --form <span class="nv">files</span><span class="o">=</span>@img.png <span class="se">\
</span><span class="se"></span> --form <span class="nv">files</span><span class="o">=</span>@font.woff <span class="se">\
</span><span class="se"></span> -o result.pdf
</pre>
<h3 class="Heading"><a class="Anchor" aria-hidden="true" id="html.assets.go" href="#html.assets.go">
@@ -346,18 +397,12 @@ see to the <a href="#office.fonts">fonts section</a>.</p>
<pre class="chroma"><span class="kn">import</span> <span class="s">&#34;github.com/thecodingmachine/gotenberg/pkg&#34;</span>
<span class="kd">func</span> <span class="nx">main</span><span class="p">()</span> <span class="p">{</span>
<span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
<span class="nx">c</span> <span class="o">:=</span> <span class="o">&amp;</span><span class="nx">gotenberg</span><span class="p">.</span><span class="nx">Client</span><span class="p">{</span><span class="nx">Hostname</span><span class="p">:</span> <span class="s">&#34;http://localhost:3000&#34;</span><span class="p">}</span>
<span class="nx">req</span> <span class="o">:=</span> <span class="o">&amp;</span><span class="nx">gotenberg</span><span class="p">.</span><span class="nx">HTMLRequest</span><span class="p">{</span>
<span class="nx">IndexFilePath</span><span class="p">:</span> <span class="s">&#34;index.html&#34;</span><span class="p">,</span>
<span class="nx">AssetFilePaths</span><span class="p">:</span> <span class="p">[]</span><span class="kt">string</span><span class="p">{</span>
<span class="s">&#34;style.css&#34;</span><span class="p">,</span>
<span class="s">&#34;img.png&#34;</span><span class="p">,</span>
<span class="s">&#34;font.woff&#34;</span><span class="p">,</span>
<span class="p">},</span>
<span class="p">}</span>
<span class="nx">req</span><span class="p">,</span> <span class="nx">_</span> <span class="o">:=</span> <span class="nx">gotenberg</span><span class="p">.</span><span class="nf">NewHTMLRequest</span><span class="p">(</span><span class="s">&#34;index.html&#34;</span><span class="p">)</span>
<span class="nx">req</span><span class="p">.</span><span class="nf">SetAssets</span><span class="p">(</span><span class="s">&#34;font.woff&#34;</span><span class="p">,</span> <span class="s">&#34;img.gif&#34;</span><span class="p">,</span> <span class="s">&#34;style.css&#34;</span><span class="p">)</span>
<span class="nx">dest</span> <span class="o">:=</span> <span class="s">&#34;result.pdf&#34;</span>
<span class="nx">c</span><span class="p">.</span><span class="nx">Store</span><span class="p">(</span><span class="nx">req</span><span class="p">,</span> <span class="nx">dest</span><span class="p">)</span>
<span class="nx">c</span><span class="p">.</span><span class="nf">Store</span><span class="p">(</span><span class="nx">req</span><span class="p">,</span> <span class="nx">dest</span><span class="p">)</span>
<span class="p">}</span>
</pre>
@@ -365,37 +410,52 @@ see to the <a href="#office.fonts">fonts section</a>.</p>
<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-link"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"></path><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"></path></svg>
</a>PHP</h3>
<p>TODO</p>
<pre class="chroma">use TheCodingMachine\Gotenberg\Client;
use TheCodingMachine\Gotenberg\DocumentFactory;
use TheCodingMachine\Gotenberg\HTMLRequest;
$client = new Client(&#39;http://localhost:3000&#39;, new \Http\Adapter\Guzzle6\Client());
$index = DocumentFactory::makeFromPath(&#39;index.html&#39;, &#39;index.html&#39;);
$assets = [
DocumentFactory::makeFromPath(&#39;style.css&#39;, &#39;style.css&#39;),
DocumentFactory::makeFromPath(&#39;img.png&#39;, &#39;img.png&#39;),
DocumentFactory::makeFromPath(&#39;font.woff&#39;, &#39;font.woff&#39;),
];
$request = new HTMLRequest($index);
$request-&gt;setAssets($assets);
$dest = &#34;result.pdf&#34;;
$client-&gt;store($request, $dest);
</pre>
<h2 class="Heading"><a class="Anchor" aria-hidden="true" id="html.paper_size_margins_orientation" href="#html.paper_size_margins_orientation">
<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-link"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"></path><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"></path></svg>
</a>Paper size, margins, orientation</h2>
<p>You may also customize the resulting PDF paper.</p>
<p>You may also customize the resulting PDF format.</p>
<p>By default, it will be rendered in <code>A4</code> size with <code>1 inch</code> margins and <code>portrait</code> orientation.</p>
<p>By default, it will be rendered with <code>A4</code> size, <code>1 inch</code> margins and <code>portrait</code> orientation.</p>
<blockquote>
<p>Paper size and margins have to be provided in <code>inches</code>.
Also, you have to set both <code>paperWidth</code> and <code>paperHeight</code>. Same for margins.</p>
</blockquote>
<h3 class="Heading"><a class="Anchor" aria-hidden="true" id="html.paper_size_margins_orientation.guzzle" href="#html.paper_size_margins_orientation.guzzle">
<h3 class="Heading"><a class="Anchor" aria-hidden="true" id="html.paper_size_margins_orientation.c_url" href="#html.paper_size_margins_orientation.c_url">
<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-link"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"></path><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"></path></svg>
</a>Guzzle</h3>
</a>cURL</h3>
<pre class="chroma">$ curl --request POST <span class="se">\
</span><span class="se"></span> --url http://localhost:3000/convert/html <span class="se">\
</span><span class="se"></span> --header <span class="s1">&#39;Content-Type: multipart/form-data&#39;</span> <span class="se">\
</span><span class="se"></span> --form <span class="nv">files</span><span class="o">=</span>@index.html
--form <span class="nv">paperWidth</span><span class="o">=</span><span class="m">8</span>.27
--form <span class="nv">paperHeight</span><span class="o">=</span><span class="m">11</span>.27
--form <span class="nv">marginTop</span><span class="o">=</span><span class="m">0</span>
--form <span class="nv">marginBottom</span><span class="o">=</span><span class="m">0</span>
--form <span class="nv">marginLeft</span><span class="o">=</span><span class="m">0</span>
--form <span class="nv">marginRight</span><span class="o">=</span><span class="m">0</span>
--form <span class="nv">landscape</span><span class="o">=</span><span class="nb">true</span>
&gt; result.pdf
</span><span class="se"></span> --form <span class="nv">files</span><span class="o">=</span>@index.html <span class="se">\
</span><span class="se"></span> --form <span class="nv">paperWidth</span><span class="o">=</span><span class="m">8</span>.27 <span class="se">\
</span><span class="se"></span> --form <span class="nv">paperHeight</span><span class="o">=</span><span class="m">11</span>.27 <span class="se">\
</span><span class="se"></span> --form <span class="nv">marginTop</span><span class="o">=</span><span class="m">0</span> <span class="se">\
</span><span class="se"></span> --form <span class="nv">marginBottom</span><span class="o">=</span><span class="m">0</span> <span class="se">\
</span><span class="se"></span> --form <span class="nv">marginLeft</span><span class="o">=</span><span class="m">0</span> <span class="se">\
</span><span class="se"></span> --form <span class="nv">marginRight</span><span class="o">=</span><span class="m">0</span> <span class="se">\
</span><span class="se"></span> --form <span class="nv">landscape</span><span class="o">=</span><span class="nb">true</span> <span class="se">\
</span><span class="se"></span> -o result.pdf
</pre>
<h3 class="Heading"><a class="Anchor" aria-hidden="true" id="html.paper_size_margins_orientation.go" href="#html.paper_size_margins_orientation.go">
@@ -404,18 +464,14 @@ Also, you have to set both <code>paperWidth</code> and <code>paperHeight</code>.
<pre class="chroma"><span class="kn">import</span> <span class="s">&#34;github.com/thecodingmachine/gotenberg/pkg&#34;</span>
<span class="kd">func</span> <span class="nx">main</span><span class="p">()</span> <span class="p">{</span>
<span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
<span class="nx">c</span> <span class="o">:=</span> <span class="o">&amp;</span><span class="nx">gotenberg</span><span class="p">.</span><span class="nx">Client</span><span class="p">{</span><span class="nx">Hostname</span><span class="p">:</span> <span class="s">&#34;http://localhost:3000&#34;</span><span class="p">}</span>
<span class="nx">req</span> <span class="o">:=</span> <span class="o">&amp;</span><span class="nx">gotenberg</span><span class="p">.</span><span class="nx">HTMLRequest</span><span class="p">{</span>
<span class="nx">IndexFilePath</span><span class="p">:</span> <span class="s">&#34;index.html&#34;</span><span class="p">,</span>
<span class="nx">Options</span><span class="p">:</span> <span class="o">&amp;</span><span class="nx">gotenberg</span><span class="p">.</span><span class="nx">HTMLOptions</span><span class="p">{</span>
<span class="nx">PaperSize</span><span class="p">:</span> <span class="nx">gotenberg</span><span class="p">.</span><span class="nx">A4</span><span class="p">,</span>
<span class="nx">PaperMargins</span><span class="p">:</span> <span class="nx">gotenberg</span><span class="p">.</span><span class="nx">NoMargins</span><span class="p">,</span>
<span class="nx">Landscape</span><span class="p">:</span> <span class="kc">true</span>
<span class="p">},</span>
<span class="p">}</span>
<span class="nx">req</span><span class="p">,</span> <span class="nx">_</span> <span class="o">:=</span> <span class="nx">gotenberg</span><span class="p">.</span><span class="nf">NewHTMLRequest</span><span class="p">(</span><span class="s">&#34;index.html&#34;</span><span class="p">)</span>
<span class="nx">req</span><span class="p">.</span><span class="nf">SetPaperSize</span><span class="p">(</span><span class="nx">gotenberg</span><span class="p">.</span><span class="nx">A4</span><span class="p">)</span>
<span class="nx">req</span><span class="p">.</span><span class="nf">SetMargins</span><span class="p">(</span><span class="nx">gotenberg</span><span class="p">.</span><span class="nx">NoMargins</span><span class="p">)</span>
<span class="nx">req</span><span class="p">.</span><span class="nf">SetLandscape</span><span class="p">(</span><span class="kc">true</span><span class="p">)</span>
<span class="nx">dest</span> <span class="o">:=</span> <span class="s">&#34;result.pdf&#34;</span>
<span class="nx">c</span><span class="p">.</span><span class="nx">Store</span><span class="p">(</span><span class="nx">req</span><span class="p">,</span> <span class="nx">dest</span><span class="p">)</span>
<span class="nx">c</span><span class="p">.</span><span class="nf">Store</span><span class="p">(</span><span class="nx">req</span><span class="p">,</span> <span class="nx">dest</span><span class="p">)</span>
<span class="p">}</span>
</pre>
@@ -423,7 +479,86 @@ Also, you have to set both <code>paperWidth</code> and <code>paperHeight</code>.
<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-link"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"></path><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"></path></svg>
</a>PHP</h3>
<p>TODO</p>
<pre class="chroma">use TheCodingMachine\Gotenberg\Client;
use TheCodingMachine\Gotenberg\DocumentFactory;
use TheCodingMachine\Gotenberg\HTMLRequest;
use TheCodingMachine\Gotenberg\Request;
$client = new Client(&#39;http://localhost:3000&#39;, new \Http\Adapter\Guzzle6\Client());
$index = DocumentFactory::makeFromPath(&#39;index.html&#39;, &#39;index.html&#39;);
$request = new HTMLRequest($index);
$request-&gt;setPaperSize(Request::A4);
$request-&gt;setMargins(Request::NO_MARGINS);
$request-&gt;setLandscape(true);
$dest = &#34;result.pdf&#34;;
$client-&gt;store($request, $dest);
</pre>
</div>
<div class="Page" id="url">
<h1 class="Heading"><a class="Anchor" aria-hidden="true" id="url" href="#url">
<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-link"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"></path><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"></path></svg>
</a>URL</h1>
<p>Gotenberg provides the endpoint <code>/convert/url</code> for remote URL conversions.</p>
<p>It accepts <code>POST</code> requests with a <code>multipart/form-data</code> Content-Type.</p>
<h2 class="Heading"><a class="Anchor" aria-hidden="true" id="url.basic" href="#url.basic">
<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-link"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"></path><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"></path></svg>
</a>Basic</h2>
<p>This endpoint does not accept an <code>index.html</code> file nor assets files but a form field
named <code>remoteURL</code> instead. Otherwise, URL conversions work the same as HTML conversions.</p>
<blockquote>
<p><strong>Attention:</strong> when converting a website to PDF, you should remove all margins.
If not, some of the content of the page might be hidden.</p>
</blockquote>
<h3 class="Heading"><a class="Anchor" aria-hidden="true" id="url.basic.c_url" href="#url.basic.c_url">
<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-link"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"></path><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"></path></svg>
</a>cURL</h3>
<pre class="chroma">$ curl --request POST <span class="se">\
</span><span class="se"></span> --url http://localhost:3000/convert/url <span class="se">\
</span><span class="se"></span> --header <span class="s1">&#39;Content-Type: multipart/form-data&#39;</span> <span class="se">\
</span><span class="se"></span> --form <span class="nv">remoteURL</span><span class="o">=</span>https://google.com
--form <span class="nv">marginTop</span><span class="o">=</span><span class="m">0</span> <span class="se">\
</span><span class="se"></span> --form <span class="nv">marginBottom</span><span class="o">=</span><span class="m">0</span> <span class="se">\
</span><span class="se"></span> --form <span class="nv">marginLeft</span><span class="o">=</span><span class="m">0</span> <span class="se">\
</span><span class="se"></span> --form <span class="nv">marginRight</span><span class="o">=</span><span class="m">0</span> <span class="se">\
</span><span class="se"></span> -o result.pdf
</pre>
<h3 class="Heading"><a class="Anchor" aria-hidden="true" id="url.basic.go" href="#url.basic.go">
<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-link"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"></path><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"></path></svg>
</a>Go</h3>
<pre class="chroma"><span class="kn">import</span> <span class="s">&#34;github.com/thecodingmachine/gotenberg/pkg&#34;</span>
<span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
<span class="nx">c</span> <span class="o">:=</span> <span class="o">&amp;</span><span class="nx">gotenberg</span><span class="p">.</span><span class="nx">Client</span><span class="p">{</span><span class="nx">Hostname</span><span class="p">:</span> <span class="s">&#34;http://localhost:3000&#34;</span><span class="p">}</span>
<span class="nx">req</span> <span class="o">:=</span> <span class="nx">gotenberg</span><span class="p">.</span><span class="nf">NewURLRequest</span><span class="p">(</span><span class="s">&#34;https://google.com&#34;</span><span class="p">)</span>
<span class="nx">req</span><span class="p">.</span><span class="nf">SetMargins</span><span class="p">(</span><span class="nx">gotenberg</span><span class="p">.</span><span class="nx">NoMargins</span><span class="p">)</span>
<span class="nx">dest</span> <span class="o">:=</span> <span class="s">&#34;result.pdf&#34;</span>
<span class="nx">c</span><span class="p">.</span><span class="nf">Store</span><span class="p">(</span><span class="nx">req</span><span class="p">,</span> <span class="nx">dest</span><span class="p">)</span>
<span class="p">}</span>
</pre>
<h3 class="Heading"><a class="Anchor" aria-hidden="true" id="url.basic.php" href="#url.basic.php">
<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-link"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"></path><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"></path></svg>
</a>PHP</h3>
<pre class="chroma">use TheCodingMachine\Gotenberg\Client;
use TheCodingMachine\Gotenberg\URLRequest;
$client = new Client(&#39;http://localhost:3000&#39;, new \Http\Adapter\Guzzle6\Client());
$request = new URLRequest(&#39;https://google.com&#39;);
$request-&gt;setMargins(Request::NO_MARGINS);
$dest = &#34;result.pdf&#34;;
$client-&gt;store($request, $dest);
</pre>
</div>
@@ -442,32 +577,32 @@ Also, you have to set both <code>paperWidth</code> and <code>paperHeight</code>.
<p>Markdown conversions work the same as HTML conversions.</p>
<p>Only difference is that you have access to the Go template function <code>toHTML</code>
in the file <code>index.html</code> which will convert a given markdown file to HTML.</p>
in the file <code>index.html</code>. This function will convert a given markdown file to HTML.</p>
<p>For instance:</p>
<pre class="chroma"><span class="cp">&lt;!doctype html&gt;</span>
<span class="p">&lt;</span><span class="nt">html</span> <span class="na">lang</span><span class="o">=</span><span class="s">&#34;en&#34;</span><span class="p"></span><span class="p">&gt;</span>
<span class="p">&lt;</span><span class="nt">head</span><span class="p"></span><span class="p">&gt;</span>
<span class="p">&lt;</span><span class="nt">meta</span> <span class="na">charset</span><span class="o">=</span><span class="s">&#34;utf-8&#34;</span><span class="p"></span><span class="p">&gt;</span>
<span class="p">&lt;</span><span class="nt">title</span><span class="p"></span><span class="p">&gt;</span>My PDF<span class="p">&lt;</span><span class="p">/</span><span class="nt">title</span><span class="p">&gt;</span>
<span class="p">&lt;</span><span class="p">/</span><span class="nt">head</span><span class="p">&gt;</span>
<span class="p">&lt;</span><span class="nt">body</span><span class="p"></span><span class="p">&gt;</span>
<span class="p">&lt;</span><span class="nt">html</span> <span class="na">lang</span><span class="o">=</span><span class="s">&#34;en&#34;</span><span class="p">&gt;</span>
<span class="p">&lt;</span><span class="nt">head</span><span class="p">&gt;</span>
<span class="p">&lt;</span><span class="nt">meta</span> <span class="na">charset</span><span class="o">=</span><span class="s">&#34;utf-8&#34;</span><span class="p">&gt;</span>
<span class="p">&lt;</span><span class="nt">title</span><span class="p">&gt;</span>My PDF<span class="p">&lt;/</span><span class="nt">title</span><span class="p">&gt;</span>
<span class="p">&lt;/</span><span class="nt">head</span><span class="p">&gt;</span>
<span class="p">&lt;</span><span class="nt">body</span><span class="p">&gt;</span>
{{ toHTML .DirPath &#34;file.md&#34; }}
<span class="p">&lt;</span><span class="p">/</span><span class="nt">body</span><span class="p">&gt;</span>
<span class="p">&lt;</span><span class="p">/</span><span class="nt">html</span><span class="p">&gt;</span>
<span class="p">&lt;/</span><span class="nt">body</span><span class="p">&gt;</span>
<span class="p">&lt;/</span><span class="nt">html</span><span class="p">&gt;</span>
</pre>
<h3 class="Heading"><a class="Anchor" aria-hidden="true" id="markdown.basic.guzzle" href="#markdown.basic.guzzle">
<h3 class="Heading"><a class="Anchor" aria-hidden="true" id="markdown.basic.c_url" href="#markdown.basic.c_url">
<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-link"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"></path><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"></path></svg>
</a>Guzzle</h3>
</a>cURL</h3>
<pre class="chroma">$ curl --request POST <span class="se">\
</span><span class="se"></span> --url http://localhost:3000/convert/markdown <span class="se">\
</span><span class="se"></span> --header <span class="s1">&#39;Content-Type: multipart/form-data&#39;</span> <span class="se">\
</span><span class="se"></span> --form <span class="nv">files</span><span class="o">=</span>@index.html
--form <span class="nv">files</span><span class="o">=</span>@file.md
&gt; result.pdf
</span><span class="se"></span> --form <span class="nv">files</span><span class="o">=</span>@index.html <span class="se">\
</span><span class="se"></span> --form <span class="nv">files</span><span class="o">=</span>@file.md <span class="se">\
</span><span class="se"></span> -o result.pdf
</pre>
<h3 class="Heading"><a class="Anchor" aria-hidden="true" id="markdown.basic.go" href="#markdown.basic.go">
@@ -476,16 +611,11 @@ in the file <code>index.html</code> which will convert a given markdown file to
<pre class="chroma"><span class="kn">import</span> <span class="s">&#34;github.com/thecodingmachine/gotenberg/pkg&#34;</span>
<span class="kd">func</span> <span class="nx">main</span><span class="p">()</span> <span class="p">{</span>
<span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
<span class="nx">c</span> <span class="o">:=</span> <span class="o">&amp;</span><span class="nx">gotenberg</span><span class="p">.</span><span class="nx">Client</span><span class="p">{</span><span class="nx">Hostname</span><span class="p">:</span> <span class="s">&#34;http://localhost:3000&#34;</span><span class="p">}</span>
<span class="nx">req</span> <span class="o">:=</span> <span class="o">&amp;</span><span class="nx">gotenberg</span><span class="p">.</span><span class="nx">MarkdownRequest</span><span class="p">{</span>
<span class="nx">IndexFilePath</span><span class="p">:</span> <span class="s">&#34;index.html&#34;</span><span class="p">,</span>
<span class="nx">MarkdownFilePaths</span><span class="p">:</span> <span class="p">[]</span><span class="kt">string</span><span class="p">{</span>
<span class="s">&#34;file.md&#34;</span><span class="p">,</span>
<span class="p">},</span>
<span class="p">}</span>
<span class="nx">req</span><span class="p">,</span> <span class="nx">_</span> <span class="o">:=</span> <span class="nx">gotenberg</span><span class="p">.</span><span class="nf">NewMarkdownRequest</span><span class="p">(</span><span class="s">&#34;index.html&#34;</span><span class="p">,</span> <span class="s">&#34;file.md&#34;</span><span class="p">)</span>
<span class="nx">dest</span> <span class="o">:=</span> <span class="s">&#34;result.pdf&#34;</span>
<span class="nx">c</span><span class="p">.</span><span class="nx">Store</span><span class="p">(</span><span class="nx">req</span><span class="p">,</span> <span class="nx">dest</span><span class="p">)</span>
<span class="nx">c</span><span class="p">.</span><span class="nf">Store</span><span class="p">(</span><span class="nx">req</span><span class="p">,</span> <span class="nx">dest</span><span class="p">)</span>
<span class="p">}</span>
</pre>
@@ -493,7 +623,19 @@ in the file <code>index.html</code> which will convert a given markdown file to
<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-link"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"></path><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"></path></svg>
</a>PHP</h3>
<p>TODO</p>
<pre class="chroma">use TheCodingMachine\Gotenberg\Client;
use TheCodingMachine\Gotenberg\DocumentFactory;
use TheCodingMachine\Gotenberg\MarkdownRequest;
$client = new Client(&#39;http://localhost:3000&#39;, new \Http\Adapter\Guzzle6\Client());
$index = DocumentFactory::makeFromPath(&#39;index.html&#39;, &#39;index.html&#39;);
$markdowns = [
DocumentFactory::makeFromPath(&#39;file.md&#39;, &#39;file.md&#39;),
];
$request = new MarkdownRequest($index, $markdowns);
$dest = &#34;result.pdf&#34;;
$client-&gt;store($request, $dest);
</pre>
</div>
@@ -501,7 +643,7 @@ in the file <code>index.html</code> which will convert a given markdown file to
<h1 class="Heading"><a class="Anchor" aria-hidden="true" id="office" href="#office">
<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-link"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"></path><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"></path></svg>
</a>Office</h1>
<p>Gotenberg provides the endpoint <code>/convert/office</code> for Office documents conversions.</p>
<p>Gotenberg provides the endpoint <code>/convert/office</code> for Office document conversions.</p>
<p>It accepts <code>POST</code> requests with a <code>multipart/form-data</code> Content-Type.</p>
@@ -512,6 +654,9 @@ in the file <code>index.html</code> which will convert a given markdown file to
<p>You may send one or more Office documents. Following file extensions are accepted:</p>
<ul>
<li><code>.txt</code></li>
<li><code>.rtf</code></li>
<li><code>.fodt</code></li>
<li><code>.doc</code></li>
<li><code>.docx</code></li>
<li><code>.odt</code></li>
@@ -531,16 +676,16 @@ Thats why for Office conversions, the API does only one conversion at a time.
See the <a href="#scalability">scalability section</a> to find how to mitigate this issue.</p>
</blockquote>
<h3 class="Heading"><a class="Anchor" aria-hidden="true" id="office.basic.guzzle" href="#office.basic.guzzle">
<h3 class="Heading"><a class="Anchor" aria-hidden="true" id="office.basic.c_url" href="#office.basic.c_url">
<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-link"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"></path><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"></path></svg>
</a>Guzzle</h3>
</a>cURL</h3>
<pre class="chroma">$ curl --request POST <span class="se">\
</span><span class="se"></span> --url http://localhost:3000/convert/office <span class="se">\
</span><span class="se"></span> --header <span class="s1">&#39;Content-Type: multipart/form-data&#39;</span> <span class="se">\
</span><span class="se"></span> --form <span class="nv">files</span><span class="o">=</span>@document.docx
--form <span class="nv">files</span><span class="o">=</span>@document2.docx
&gt; result.pdf
</span><span class="se"></span> --form <span class="nv">files</span><span class="o">=</span>@document.docx <span class="se">\
</span><span class="se"></span> --form <span class="nv">files</span><span class="o">=</span>@document2.docx <span class="se">\
</span><span class="se"></span> -o result.pdf
</pre>
<h3 class="Heading"><a class="Anchor" aria-hidden="true" id="office.basic.go" href="#office.basic.go">
@@ -549,16 +694,11 @@ See the <a href="#scalability">scalability section</a> to find how to mitigate t
<pre class="chroma"><span class="kn">import</span> <span class="s">&#34;github.com/thecodingmachine/gotenberg/pkg&#34;</span>
<span class="kd">func</span> <span class="nx">main</span><span class="p">()</span> <span class="p">{</span>
<span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
<span class="nx">c</span> <span class="o">:=</span> <span class="o">&amp;</span><span class="nx">gotenberg</span><span class="p">.</span><span class="nx">Client</span><span class="p">{</span><span class="nx">Hostname</span><span class="p">:</span> <span class="s">&#34;http://localhost:3000&#34;</span><span class="p">}</span>
<span class="nx">req</span> <span class="o">:=</span> <span class="o">&amp;</span><span class="nx">gotenberg</span><span class="p">.</span><span class="nx">OfficeRequest</span><span class="p">{</span>
<span class="nx">FilePaths</span><span class="p">:</span> <span class="p">[]</span><span class="kt">string</span><span class="p">{</span>
<span class="s">&#34;document.docx&#34;</span><span class="p">,</span>
<span class="s">&#34;document2.docx&#34;</span><span class="p">,</span>
<span class="p">},</span>
<span class="p">}</span>
<span class="nx">req</span><span class="p">,</span> <span class="nx">_</span> <span class="o">:=</span> <span class="nx">gotenberg</span><span class="p">.</span><span class="nf">NewOfficeRequest</span><span class="p">(</span><span class="s">&#34;document.docx&#34;</span><span class="p">,</span> <span class="s">&#34;document2.docx&#34;</span><span class="p">)</span>
<span class="nx">dest</span> <span class="o">:=</span> <span class="s">&#34;result.pdf&#34;</span>
<span class="nx">c</span><span class="p">.</span><span class="nx">Store</span><span class="p">(</span><span class="nx">req</span><span class="p">,</span> <span class="nx">dest</span><span class="p">)</span>
<span class="nx">c</span><span class="p">.</span><span class="nf">Store</span><span class="p">(</span><span class="nx">req</span><span class="p">,</span> <span class="nx">dest</span><span class="p">)</span>
<span class="p">}</span>
</pre>
@@ -566,20 +706,72 @@ See the <a href="#scalability">scalability section</a> to find how to mitigate t
<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-link"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"></path><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"></path></svg>
</a>PHP</h3>
<p>TODO</p>
<pre class="chroma">use TheCodingMachine\Gotenberg\Client;
use TheCodingMachine\Gotenberg\DocumentFactory;
use TheCodingMachine\Gotenberg\OfficeRequest;
<h2 class="Heading"><a class="Anchor" aria-hidden="true" id="office.fonts" href="#office.fonts">
$client = new Client(&#39;http://localhost:3000&#39;, new \Http\Adapter\Guzzle6\Client());
$files = [
DocumentFactory::makeFromPath(&#39;document.docx&#39;, &#39;document.docx&#39;),
DocumentFactory::makeFromPath(&#39;document2.docx&#39;, &#39;document2.docx&#39;),
];
$request = new OfficeRequest($files);
$dest = &#34;result.pdf&#34;;
$client-&gt;store($request, $dest);
</pre>
<h2 class="Heading"><a class="Anchor" aria-hidden="true" id="office.orientation" href="#office.orientation">
<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-link"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"></path><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"></path></svg>
</a>Fonts</h2>
</a>Orientation</h2>
<p>By default, only <code>ttf-mscorefonts</code> fonts are installed.</p>
<p>You may also customize the resulting PDF format.</p>
<p>If you wish to use more fonts, you will have to create your own image:</p>
<p>By default, it will be rendered with <code>portrait</code> orientation.</p>
<pre class="chroma"><span class="k">FROM</span><span class="s"> thecodingmachine/gotenberg:3</span><span class="err">
</span><span class="err">
</span><span class="err"></span><span class="k">RUN</span> apt-get -y install yourfonts<span class="err">
</span><span class="err"></span></pre>
<h3 class="Heading"><a class="Anchor" aria-hidden="true" id="office.orientation.c_url" href="#office.orientation.c_url">
<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-link"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"></path><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"></path></svg>
</a>cURL</h3>
<pre class="chroma">$ curl --request POST <span class="se">\
</span><span class="se"></span> --url http://localhost:3000/convert/office <span class="se">\
</span><span class="se"></span> --header <span class="s1">&#39;Content-Type: multipart/form-data&#39;</span> <span class="se">\
</span><span class="se"></span> --form <span class="nv">files</span><span class="o">=</span>@document.docx <span class="se">\
</span><span class="se"></span> --form <span class="nv">landscape</span><span class="o">=</span><span class="nb">true</span> <span class="se">\
</span><span class="se"></span> -o result.pdf
</pre>
<h3 class="Heading"><a class="Anchor" aria-hidden="true" id="office.orientation.go" href="#office.orientation.go">
<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-link"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"></path><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"></path></svg>
</a>Go</h3>
<pre class="chroma"><span class="kn">import</span> <span class="s">&#34;github.com/thecodingmachine/gotenberg/pkg&#34;</span>
<span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
<span class="nx">c</span> <span class="o">:=</span> <span class="o">&amp;</span><span class="nx">gotenberg</span><span class="p">.</span><span class="nx">Client</span><span class="p">{</span><span class="nx">Hostname</span><span class="p">:</span> <span class="s">&#34;http://localhost:3000&#34;</span><span class="p">}</span>
<span class="nx">req</span><span class="p">,</span> <span class="nx">_</span> <span class="o">:=</span> <span class="nx">gotenberg</span><span class="p">.</span><span class="nf">NewOfficeRequest</span><span class="p">(</span><span class="s">&#34;document.docx&#34;</span><span class="p">)</span>
<span class="nx">req</span><span class="p">.</span><span class="nf">SetLandscape</span><span class="p">(</span><span class="kc">true</span><span class="p">)</span>
<span class="nx">dest</span> <span class="o">:=</span> <span class="s">&#34;result.pdf&#34;</span>
<span class="nx">c</span><span class="p">.</span><span class="nf">Store</span><span class="p">(</span><span class="nx">req</span><span class="p">,</span> <span class="nx">dest</span><span class="p">)</span>
<span class="p">}</span>
</pre>
<h3 class="Heading"><a class="Anchor" aria-hidden="true" id="office.orientation.php" href="#office.orientation.php">
<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-link"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"></path><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"></path></svg>
</a>PHP</h3>
<pre class="chroma">use TheCodingMachine\Gotenberg\Client;
use TheCodingMachine\Gotenberg\DocumentFactory;
use TheCodingMachine\Gotenberg\OfficeRequest;
$client = new Client(&#39;http://localhost:3000&#39;, new \Http\Adapter\Guzzle6\Client());
$files = [
DocumentFactory::makeFromPath(&#39;document.docx&#39;, &#39;document.docx&#39;),
];
$request = new OfficeRequest($files);
$request-&gt;setLandscape(true);
$dest = &#34;result.pdf&#34;;
$client-&gt;store($request, $dest);
</pre>
</div>
@@ -595,19 +787,23 @@ See the <a href="#scalability">scalability section</a> to find how to mitigate t
<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-link"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"></path><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"></path></svg>
</a>Basic</h2>
<p>Nothing special here: you may send one or more PDF files and the API
<p>Nothing fancy here: you may send one or more PDF files and the API
will merge them and return the resulting PDF file.</p>
<h3 class="Heading"><a class="Anchor" aria-hidden="true" id="merge.basic.guzzle" href="#merge.basic.guzzle">
<blockquote>
<p><strong>Attention:</strong> Gotenberg merges the PDF files alphabetically.</p>
</blockquote>
<h3 class="Heading"><a class="Anchor" aria-hidden="true" id="merge.basic.c_url" href="#merge.basic.c_url">
<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-link"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"></path><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"></path></svg>
</a>Guzzle</h3>
</a>cURL</h3>
<pre class="chroma">$ curl --request POST <span class="se">\
</span><span class="se"></span> --url http://localhost:3000/merge <span class="se">\
</span><span class="se"></span> --header <span class="s1">&#39;Content-Type: multipart/form-data&#39;</span> <span class="se">\
</span><span class="se"></span> --form <span class="nv">files</span><span class="o">=</span>@file.pdf
--form <span class="nv">files</span><span class="o">=</span>@file2.pdf
&gt; result.pdf
</span><span class="se"></span> --form <span class="nv">files</span><span class="o">=</span>@file.pdf <span class="se">\
</span><span class="se"></span> --form <span class="nv">files</span><span class="o">=</span>@file2.pdf <span class="se">\
</span><span class="se"></span> -o result.pdf
</pre>
<h3 class="Heading"><a class="Anchor" aria-hidden="true" id="merge.basic.go" href="#merge.basic.go">
@@ -616,16 +812,11 @@ will merge them and return the resulting PDF file.</p>
<pre class="chroma"><span class="kn">import</span> <span class="s">&#34;github.com/thecodingmachine/gotenberg/pkg&#34;</span>
<span class="kd">func</span> <span class="nx">main</span><span class="p">()</span> <span class="p">{</span>
<span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
<span class="nx">c</span> <span class="o">:=</span> <span class="o">&amp;</span><span class="nx">gotenberg</span><span class="p">.</span><span class="nx">Client</span><span class="p">{</span><span class="nx">Hostname</span><span class="p">:</span> <span class="s">&#34;http://localhost:3000&#34;</span><span class="p">}</span>
<span class="nx">req</span> <span class="o">:=</span> <span class="o">&amp;</span><span class="nx">gotenberg</span><span class="p">.</span><span class="nx">MergeRequest</span><span class="p">{</span>
<span class="nx">FilePaths</span><span class="p">:</span> <span class="p">[]</span><span class="kt">string</span><span class="p">{</span>
<span class="s">&#34;file.pdf&#34;</span><span class="p">,</span>
<span class="s">&#34;file2.pdf&#34;</span><span class="p">,</span>
<span class="p">},</span>
<span class="p">}</span>
<span class="nx">req</span><span class="p">,</span> <span class="nx">_</span> <span class="o">:=</span> <span class="nx">gotenberg</span><span class="p">.</span><span class="nf">NewMergeRequest</span><span class="p">(</span><span class="s">&#34;file.pdf&#34;</span><span class="p">,</span> <span class="s">&#34;file2.pdf&#34;</span><span class="p">)</span>
<span class="nx">dest</span> <span class="o">:=</span> <span class="s">&#34;result.pdf&#34;</span>
<span class="nx">c</span><span class="p">.</span><span class="nx">Store</span><span class="p">(</span><span class="nx">req</span><span class="p">,</span> <span class="nx">dest</span><span class="p">)</span>
<span class="nx">c</span><span class="p">.</span><span class="nf">Store</span><span class="p">(</span><span class="nx">req</span><span class="p">,</span> <span class="nx">dest</span><span class="p">)</span>
<span class="p">}</span>
</pre>
@@ -633,7 +824,19 @@ will merge them and return the resulting PDF file.</p>
<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-link"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"></path><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"></path></svg>
</a>PHP</h3>
<p>TODO</p>
<pre class="chroma">use TheCodingMachine\Gotenberg\Client;
use TheCodingMachine\Gotenberg\DocumentFactory;
use TheCodingMachine\Gotenberg\MergeRequest;
$client = new Client(&#39;http://localhost:3000&#39;, new \Http\Adapter\Guzzle6\Client());
$files = [
DocumentFactory::makeFromPath(&#39;file.pdf&#39;, &#39;file.pdf&#39;),
DocumentFactory::makeFromPath(&#39;file2.pdf&#39;, &#39;file2.pdf&#39;),
];
$request = new MergeRequest($files);
$dest = &#34;result.pdf&#34;;
$client-&gt;store($request, $dest);
</pre>
</div>
@@ -646,22 +849,21 @@ will merge them and return the resulting PDF file.</p>
<p>If provided, the API will send the resulting PDF file in a <code>POST</code> request with the <code>application/pdf</code> Content-Type
to given URL.</p>
<p>By doing so, your requests to the API will be done before the conversions are actually done!</p>
<p>By doing so, your requests to the API will be over before the conversions are actually done!</p>
<h2 class="Heading"><a class="Anchor" aria-hidden="true" id="webhook.examples" href="#webhook.examples">
<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-link"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"></path><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"></path></svg>
</a>Examples</h2>
<h3 class="Heading"><a class="Anchor" aria-hidden="true" id="webhook.examples.guzzle" href="#webhook.examples.guzzle">
<h3 class="Heading"><a class="Anchor" aria-hidden="true" id="webhook.examples.c_url" href="#webhook.examples.c_url">
<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-link"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"></path><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"></path></svg>
</a>Guzzle</h3>
</a>cURL</h3>
<pre class="chroma">$ curl --request POST <span class="se">\
</span><span class="se"></span> --url http://localhost:3000/convert/html <span class="se">\
</span><span class="se"></span> --header <span class="s1">&#39;Content-Type: multipart/form-data&#39;</span> <span class="se">\
</span><span class="se"></span> --form <span class="nv">files</span><span class="o">=</span>@index.html
--form <span class="nv">webhookURL</span><span class="o">=</span><span class="s1">&#39;http://myapp.com/webhook/&#39;</span>
&gt; result.pdf
</span><span class="se"></span> --form <span class="nv">files</span><span class="o">=</span>@index.html <span class="se">\
</span><span class="se"></span> --form <span class="nv">webhookURL</span><span class="o">=</span><span class="s1">&#39;http://myapp.com/webhook/&#39;</span>
</pre>
<h3 class="Heading"><a class="Anchor" aria-hidden="true" id="webhook.examples.go" href="#webhook.examples.go">
@@ -670,16 +872,12 @@ to given URL.</p>
<pre class="chroma"><span class="kn">import</span> <span class="s">&#34;github.com/thecodingmachine/gotenberg/pkg&#34;</span>
<span class="kd">func</span> <span class="nx">main</span><span class="p">()</span> <span class="p">{</span>
<span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
<span class="nx">c</span> <span class="o">:=</span> <span class="o">&amp;</span><span class="nx">gotenberg</span><span class="p">.</span><span class="nx">Client</span><span class="p">{</span><span class="nx">Hostname</span><span class="p">:</span> <span class="s">&#34;http://localhost:3000&#34;</span><span class="p">}</span>
<span class="nx">req</span> <span class="o">:=</span> <span class="o">&amp;</span><span class="nx">gotenberg</span><span class="p">.</span><span class="nx">HTMLRequest</span><span class="p">{</span>
<span class="nx">IndexFilePath</span><span class="p">:</span> <span class="s">&#34;index.html&#34;</span><span class="p">,</span>
<span class="nx">Options</span><span class="p">:</span> <span class="o">&amp;</span><span class="nx">gotenberg</span><span class="p">.</span><span class="nx">HTMLOptions</span><span class="p">{</span>
<span class="nx">WebHookURL</span><span class="p">:</span> <span class="s">&#34;http://myapp.com/webhook/&#34;</span><span class="p">,</span>
<span class="p">},</span>
<span class="p">}</span>
<span class="nx">req</span><span class="p">,</span> <span class="nx">_</span> <span class="o">:=</span> <span class="nx">gotenberg</span><span class="p">.</span><span class="nf">NewHTMLRequest</span><span class="p">(</span><span class="s">&#34;index.html&#34;</span><span class="p">)</span>
<span class="nx">req</span><span class="p">.</span><span class="nf">SetWebhookURL</span><span class="p">(</span><span class="s">&#34;http://myapp.com/webhook/&#34;</span><span class="p">)</span>
<span class="nx">dest</span> <span class="o">:=</span> <span class="s">&#34;result.pdf&#34;</span>
<span class="nx">resp</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">c</span><span class="p">.</span><span class="nx">Post</span><span class="p">(</span><span class="nx">req</span><span class="p">)</span>
<span class="nx">resp</span><span class="p">,</span> <span class="nx">_</span> <span class="o">:=</span> <span class="nx">c</span><span class="p">.</span><span class="nf">Post</span><span class="p">(</span><span class="nx">req</span><span class="p">)</span>
<span class="p">}</span>
</pre>
@@ -687,7 +885,16 @@ to given URL.</p>
<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-link"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"></path><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"></path></svg>
</a>PHP</h3>
<p>TODO</p>
<pre class="chroma">use TheCodingMachine\Gotenberg\Client;
use TheCodingMachine\Gotenberg\DocumentFactory;
use TheCodingMachine\Gotenberg\HTMLRequest;
$client = new Client(&#39;http://localhost:3000&#39;, new \Http\Adapter\Guzzle6\Client());
$index = DocumentFactory::makeFromPath(&#39;index.html&#39;, &#39;index.html&#39;);
$request = new HTMLRequest($index);
$request-&gt;setWebhookURL(&#39;http://myapp.com/webhook/&#39;);
$resp = $client-&gt;post($request);
</pre>
</div>
@@ -706,8 +913,8 @@ to given URL.</p>
</span><span class="w"> </span><span class="c"># your others services</span><span class="w">
</span><span class="w">
</span><span class="w"> </span>gotenberg<span class="p">:</span><span class="w">
</span><span class="w"> </span>image<span class="p">:</span><span class="w"> </span>thecodingmachine/gotenberg<span class="p">:</span><span class="m">3</span><span class="w">
</span><span class="w"></span></pre>
</span><span class="w"> </span>image<span class="p">:</span><span class="w"> </span>thecodingmachine/gotenberg<span class="p">:</span><span class="m">4</span><span class="w">
</span></pre>
<p>You may now launch your services using:</p>
@@ -719,6 +926,37 @@ redirect a request to a Gotenberg container according to the round-robin strateg
</div>
<div class="Page" id="liveness">
<h1 class="Heading"><a class="Anchor" aria-hidden="true" id="liveness" href="#liveness">
<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-link"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"></path><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"></path></svg>
</a>Liveness</h1>
<p>Gotenberg provides the endpoint <code>/ping</code> for checking the API availability with
a simple <code>GET</code> request.</p>
<p>This feature is especially useful for liveness/readiness probes in Kubernetes:</p>
<ul>
<li><a href="https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#container-probes">Pod lifecycle</a></li>
<li><a href="https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-probes/">Configure Liveness and Readiness Probes</a></li>
</ul>
</div>
<div class="Page" id="fonts">
<h1 class="Heading"><a class="Anchor" aria-hidden="true" id="fonts" href="#fonts">
<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-link"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"></path><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"></path></svg>
</a>Fonts</h1>
<p>By default, a handful of fonts are installed. Asian characters are also supported out of the box.</p>
<p>If you wish to use more fonts, you will have to create your own image:</p>
<pre class="chroma"><span class="k">FROM</span><span class="s"> thecodingmachine/gotenberg:4</span><span class="err">
</span><span class="err">
</span><span class="err"></span><span class="k">RUN</span> apt-get -y install yourfonts<span class="err">
</span></pre>
</div>
<div class="Page" id="links">
<h1 class="Heading"><a class="Anchor" aria-hidden="true" id="links" href="#links">
<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-link"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"></path><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"></path></svg>

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

BIN
docs/theme/gotenberg/img/favicon-128.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 923 B

After

Width:  |  Height:  |  Size: 917 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.3 KiB

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 95 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 182 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 372 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

View File

@@ -1,12 +1,32 @@
<!DOCTYPE html>
<html>
<head>
<!-- Global site tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-10196481-9"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'UA-10196481-9');
</script>
<meta charset="utf-8">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{{.Title}} &#183; {{.Subtitle}}</title>
<link rel="apple-touch-icon-precomposed" sizes="57x57" href="theme/gotenberg/img/apple-touch-icon-57x57.png" />
<link rel="apple-touch-icon-precomposed" sizes="114x114" href="theme/gotenberg/img/apple-touch-icon-114x114.png" />
<link rel="apple-touch-icon-precomposed" sizes="72x72" href="theme/gotenberg/img/apple-touch-icon-72x72.png" />
<link rel="apple-touch-icon-precomposed" sizes="144x144" href="theme/gotenberg/img/apple-touch-icon-144x144.png" />
<link rel="apple-touch-icon-precomposed" sizes="60x60" href="theme/gotenberg/img/apple-touch-icon-60x60.png" />
<link rel="apple-touch-icon-precomposed" sizes="120x120" href="theme/gotenberg/img/apple-touch-icon-120x120.png" />
<link rel="apple-touch-icon-precomposed" sizes="76x76" href="theme/gotenberg/img/apple-touch-icon-76x76.png" />
<link rel="apple-touch-icon-precomposed" sizes="152x152" href="theme/gotenberg/img/apple-touch-icon-152x152.png" />
<link rel="icon" type="image/png" href="theme/gotenberg/img/favicon-196x196.png" sizes="196x196" />
<link rel="icon" type="image/png" href="theme/gotenberg/img/favicon-96x96.png" sizes="96x96" />
<link rel="icon" type="image/png" href="theme/gotenberg/img/favicon-32x32.png" sizes="32x32" />
<link rel="icon" type="image/png" href="theme/gotenberg/img/favicon-16x16.png" sizes="16x16" />
<link rel="icon" type="image/png" href="theme/gotenberg/img/favicon-16x16.png" sizes="16x16" />
<link rel="icon" type="image/png" href="theme/gotenberg/img/favicon-128.png" sizes="128x128" />
<link rel="stylesheet" href="theme/gotenberg/css/index.css">
</head>
<body>
@@ -14,7 +34,7 @@
<div class="Container">
<div class="Header">
<div class="Title center">
<img src="https://user-images.githubusercontent.com/8983173/49701110-4c8b9280-fbe8-11e8-895c-a4b9c7d2515b.png" alt="Gotenberg logo" width="250" height="250" />
<img src="https://user-images.githubusercontent.com/8983173/50009948-84b01e00-ffb8-11e8-850b-fc240382c626.png" alt="Gotenberg logo" width="250" height="250" />
<span class="text">{{.Title}}</span>
<span class="subtext">{{.Subtitle}}</span>
</div>

8
go.mod
View File

@@ -1,21 +1,19 @@
module github.com/thecodingmachine/gotenberg
require (
github.com/derekparker/delve v1.1.0
github.com/google/go-cmp v0.2.0 // indirect
github.com/gorilla/websocket v1.4.0 // indirect
github.com/hhrutter/pdfcpu v0.1.18
github.com/labstack/echo v0.0.0-20181123063703-c7eb8da9ec73
github.com/hhrutter/pdfcpu v0.1.19
github.com/labstack/echo v0.0.0-20190126223945-739700bf4ef7
github.com/labstack/gommon v0.2.8
github.com/mafredri/cdp v0.20.0
github.com/microcosm-cc/bluemonday v1.0.1
github.com/pkg/errors v0.8.0 // indirect
github.com/russross/blackfriday/v2 v2.0.1
github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95 // indirect
github.com/stretchr/objx v0.1.1 // indirect
github.com/stretchr/testify v1.2.2
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9 // indirect
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc // indirect
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f // indirect
golang.org/x/sys v0.0.0-20181128092732-4ed8d59d0b35 // indirect
)

14
go.sum
View File

@@ -1,19 +1,15 @@
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/derekparker/delve v1.1.0 h1:icd65nMp7s2HiLz6y/6RCVXBdoED3xxYLwX09EMaRCc=
github.com/derekparker/delve v1.1.0/go.mod h1:pMSZMfp0Nhbm8qdZJkuE/yPGOkLpGXLS1I4poXQpuJU=
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q=
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/hhrutter/pdfcpu v0.1.18 h1:GX3d6tZxmQlmB5X9VUyOtx7+HZZpR+8ISLqBineP6dc=
github.com/hhrutter/pdfcpu v0.1.18/go.mod h1:iaCmXGnOXPIvoGLELZZc4m/GpIopVsvLQ00thrbu8LU=
github.com/labstack/echo v0.0.0-20181123063703-c7eb8da9ec73 h1:oQvA3Xy8274W5Ldgo6Nogy4lX8XO5AaIBsjF5Ggk0IU=
github.com/labstack/echo v0.0.0-20181123063703-c7eb8da9ec73/go.mod h1:rS0D1UPvC8/3sXjhSwEq+K1olh7ipbDhjDWATN2KSgA=
github.com/labstack/echo v3.3.5+incompatible h1:9PfxPUmasKzeJor9uQTaXLT6WUG/r+vSTmvXxvv3JO4=
github.com/labstack/echo v3.3.5+incompatible/go.mod h1:0INS7j/VjnFxD4E2wkz67b8cVwCLbBmJyDaka6Cmk1s=
github.com/hhrutter/pdfcpu v0.1.19 h1:J6GjNFDPjurQWallPW9gmz3CcNqBfvgB7Dad9T3e8Hc=
github.com/hhrutter/pdfcpu v0.1.19/go.mod h1:iaCmXGnOXPIvoGLELZZc4m/GpIopVsvLQ00thrbu8LU=
github.com/labstack/echo v0.0.0-20190126223945-739700bf4ef7 h1:kFheQLunLP9dCBrmg3D7y9FD44tlk/nvZQ7JCNvvWEA=
github.com/labstack/echo v0.0.0-20190126223945-739700bf4ef7/go.mod h1:rS0D1UPvC8/3sXjhSwEq+K1olh7ipbDhjDWATN2KSgA=
github.com/labstack/gommon v0.2.7/go.mod h1:/tj9csK2iPSBvn+3NLM9e52usepMtrd5ilFYA+wQNJ4=
github.com/labstack/gommon v0.2.8 h1:JvRqmeZcfrHC5u6uVleB4NxxNbzx6gpbJiQknDbKQu0=
github.com/labstack/gommon v0.2.8/go.mod h1:/tj9csK2iPSBvn+3NLM9e52usepMtrd5ilFYA+wQNJ4=
@@ -33,8 +29,6 @@ github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95 h1:/vdW8Cb7EXrkqWGufVMES1OH2sU9gKVb2n9/1y5NMBY=
github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=

View File

@@ -67,9 +67,11 @@ func setup() *echo.Echo {
return nil
}
})
e.GET("/ping", func(c echo.Context) error { return nil })
e.POST("/merge", merge)
g := e.Group("/convert")
g.POST("/html", convertHTML)
g.POST("/url", convertURL)
g.POST("/markdown", convertMarkdown)
g.POST("/office", convertOffice)
return e
@@ -87,14 +89,15 @@ func newContext(r *resource) (context.Context, context.CancelFunc) {
func print(c echo.Context, p printer.Printer, r *resource) error {
baseFilename, err := rand.Get()
if err != nil {
return fmt.Errorf("getting result file name: %v", err)
return hijackErr(fmt.Errorf("getting result file name: %v", err), r)
}
filename := fmt.Sprintf("%s.pdf", baseFilename)
fpath := fmt.Sprintf("%s/%s", r.dirPath, filename)
if r.webhookURL() == "" {
defer r.removeAll()
// if no webhook URL given, run conversion
// and directly return the resulting PDF file
// or and error.
// or an error.
if err := p.Print(fpath); err != nil {
return err
}
@@ -104,6 +107,7 @@ func print(c echo.Context, p printer.Printer, r *resource) error {
// run the following lines in a goroutine so that
// it doesn't block.
go func() {
defer r.removeAll()
if err := p.Print(fpath); err != nil {
c.Logger().Errorf("%v", err)
return
@@ -123,3 +127,10 @@ func print(c echo.Context, p printer.Printer, r *resource) error {
}()
return nil
}
func hijackErr(err error, r *resource) error {
if r != nil {
defer r.removeAll()
}
return err
}

View File

@@ -0,0 +1,18 @@
package api
import (
"net/http"
"net/http/httptest"
"testing"
"github.com/labstack/echo"
"github.com/stretchr/testify/assert"
)
func TestPing(t *testing.T) {
req := httptest.NewRequest(http.MethodGet, "/ping", nil)
rec := httptest.NewRecorder()
e := echo.New()
_ = e.NewContext(req, rec)
assert.Equal(t, http.StatusOK, rec.Code)
}

View File

@@ -8,9 +8,8 @@ import (
func convertHTML(c echo.Context) error {
r, err := newResource(c)
if err != nil {
return err
return hijackErr(err, r)
}
defer r.removeAll()
ctx, cancel := newContext(r)
if cancel != nil {
defer cancel()
@@ -18,26 +17,26 @@ func convertHTML(c echo.Context) error {
p := &printer.HTML{Context: ctx}
indexPath, err := r.filePath("index.html")
if err != nil {
return err
return hijackErr(err, r)
}
p.WithLocalURL(indexPath)
headerPath, _ := r.filePath("header.html")
if err := p.WithHeaderFile(headerPath); err != nil {
return err
return hijackErr(err, r)
}
footerPath, _ := r.filePath("footer.html")
if err := p.WithFooterFile(footerPath); err != nil {
return err
return hijackErr(err, r)
}
paperSize, err := r.paperSize()
if err != nil {
return err
return hijackErr(err, r)
}
p.PaperWidth = paperSize[0]
p.PaperHeight = paperSize[1]
paperMargins, err := r.paperMargins()
if err != nil {
return err
return hijackErr(err, r)
}
p.MarginTop = paperMargins[0]
p.MarginBottom = paperMargins[1]
@@ -45,8 +44,13 @@ func convertHTML(c echo.Context) error {
p.MarginRight = paperMargins[3]
landscape, err := r.landscape()
if err != nil {
return err
return hijackErr(err, r)
}
p.Landscape = landscape
chromeWebFontsTimeout, err := r.webFontsTimeout()
if err != nil {
return hijackErr(err, r)
}
p.WebFontsTimeout = chromeWebFontsTimeout
return print(c, p, r)
}

View File

@@ -11,11 +11,14 @@ import (
)
func TestHTML(t *testing.T) {
body, contentType := test.HTMLMultipartForm(t)
body, contentType := test.HTMLTestMultipartForm(t)
req := httptest.NewRequest(http.MethodPost, "/convert/html", body)
req.Header.Set(echo.HeaderContentType, contentType)
rec := httptest.NewRecorder()
e := echo.New()
c := e.NewContext(req, rec)
assert.NoError(t, convertHTML(c))
if assert.NoError(t, convertHTML(c)) {
assert.Equal(t, http.StatusOK, rec.Code)
assert.Equal(t, "application/pdf", rec.Header().Get(echo.HeaderContentType))
}
}

View File

@@ -8,35 +8,34 @@ import (
func convertMarkdown(c echo.Context) error {
r, err := newResource(c)
if err != nil {
return err
return hijackErr(err, r)
}
defer r.removeAll()
ctx, cancel := newContext(r)
if cancel != nil {
defer cancel()
}
indexPath, err := r.filePath("index.html")
if err != nil {
return err
return hijackErr(err, r)
}
p := &printer.Markdown{Context: ctx, TemplatePath: indexPath}
headerPath, _ := r.filePath("header.html")
if err := p.WithHeaderFile(headerPath); err != nil {
return err
return hijackErr(err, r)
}
footerPath, _ := r.filePath("footer.html")
if err := p.WithFooterFile(footerPath); err != nil {
return err
return hijackErr(err, r)
}
paperSize, err := r.paperSize()
if err != nil {
return err
return hijackErr(err, r)
}
p.PaperWidth = paperSize[0]
p.PaperHeight = paperSize[1]
paperMargins, err := r.paperMargins()
if err != nil {
return err
return hijackErr(err, r)
}
p.MarginTop = paperMargins[0]
p.MarginBottom = paperMargins[1]
@@ -44,8 +43,13 @@ func convertMarkdown(c echo.Context) error {
p.MarginRight = paperMargins[3]
landscape, err := r.landscape()
if err != nil {
return err
return hijackErr(err, r)
}
p.Landscape = landscape
chromeWebFontsTimeout, err := r.webFontsTimeout()
if err != nil {
return hijackErr(err, r)
}
p.WebFontsTimeout = chromeWebFontsTimeout
return print(c, p, r)
}

View File

@@ -11,11 +11,14 @@ import (
)
func TestMarkdown(t *testing.T) {
body, contentType := test.MarkdownMultipartForm(t)
body, contentType := test.MarkdownTestMultipartForm(t)
req := httptest.NewRequest(http.MethodPost, "/convert/markdown", body)
req.Header.Set(echo.HeaderContentType, contentType)
rec := httptest.NewRecorder()
e := echo.New()
c := e.NewContext(req, rec)
assert.NoError(t, convertMarkdown(c))
if assert.NoError(t, convertMarkdown(c)) {
assert.Equal(t, http.StatusOK, rec.Code)
assert.Equal(t, "application/pdf", rec.Header().Get(echo.HeaderContentType))
}
}

View File

@@ -1,6 +1,7 @@
package api
import (
"errors"
"fmt"
"net/http"
"os"
@@ -13,23 +14,26 @@ import (
func merge(c echo.Context) error {
r, err := newResource(c)
if err != nil {
return err
return hijackErr(err, r)
}
defer r.removeAll()
fpaths, err := r.filePaths([]string{".pdf"})
if err != nil {
return err
return hijackErr(err, r)
}
if len(fpaths) == 0 {
return hijackErr(errors.New("no suitable PDF files to merge"), r)
}
baseFilename, err := rand.Get()
if err != nil {
return fmt.Errorf("getting result file name: %v", err)
return hijackErr(fmt.Errorf("getting result file name: %v", err), r)
}
filename := fmt.Sprintf("%s.pdf", baseFilename)
fpath := fmt.Sprintf("%s/%s", r.dirPath, filename)
if r.webhookURL() == "" {
defer r.removeAll()
// if no webhook URL given, run merge
// and directly return the resulting PDF file
// or and error.
// or an error.
if err := printer.Merge(fpaths, fpath); err != nil {
return err
}
@@ -39,6 +43,7 @@ func merge(c echo.Context) error {
// run the following lines in a goroutine so that
// it doesn't block.
go func() {
defer r.removeAll()
if err := printer.Merge(fpaths, fpath); err != nil {
c.Logger().Errorf("%v", err)
return

View File

@@ -11,11 +11,14 @@ import (
)
func TestMerge(t *testing.T) {
body, contentType := test.PDFMultipartForm(t)
body, contentType := test.PDFTestMultipartForm(t)
req := httptest.NewRequest(http.MethodPost, "/merge", body)
req.Header.Set(echo.HeaderContentType, contentType)
rec := httptest.NewRecorder()
e := echo.New()
c := e.NewContext(req, rec)
assert.NoError(t, merge(c))
if assert.NoError(t, merge(c)) {
assert.Equal(t, http.StatusOK, rec.Code)
assert.Equal(t, "application/pdf", rec.Header().Get(echo.HeaderContentType))
}
}

View File

@@ -8,6 +8,9 @@ import (
)
var officeExts = []string{
".txt",
".rtf",
".fodt",
".doc",
".docx",
".odt",
@@ -22,20 +25,24 @@ var officeExts = []string{
func convertOffice(c echo.Context) error {
r, err := newResource(c)
if err != nil {
return err
return hijackErr(err, r)
}
defer r.removeAll()
ctx, cancel := newContext(r)
if cancel != nil {
defer cancel()
}
fpaths, err := r.filePaths(officeExts)
if err != nil {
return err
return hijackErr(err, r)
}
if len(fpaths) == 0 {
return errors.New("no suitable office documents to convert")
return hijackErr(errors.New("no suitable office documents to convert"), r)
}
p := &printer.Office{Context: ctx, FilePaths: fpaths}
landscape, err := r.landscape()
if err != nil {
return hijackErr(err, r)
}
p.Landscape = landscape
return print(c, p, r)
}

View File

@@ -11,11 +11,14 @@ import (
)
func TestOffice(t *testing.T) {
body, contentType := test.OfficeMultipartForm(t)
body, contentType := test.OfficeTestMultipartForm(t)
req := httptest.NewRequest(http.MethodPost, "/convert/office", body)
req.Header.Set(echo.HeaderContentType, contentType)
rec := httptest.NewRecorder()
e := echo.New()
c := e.NewContext(req, rec)
assert.NoError(t, convertOffice(c))
if assert.NoError(t, convertOffice(c)) {
assert.Equal(t, http.StatusOK, rec.Code)
assert.Equal(t, "application/pdf", rec.Header().Get(echo.HeaderContentType))
}
}

View File

@@ -1,6 +1,7 @@
package api
import (
"errors"
"fmt"
"io"
"os"
@@ -12,14 +13,16 @@ import (
)
const (
webhookURL string = "webhookURL"
paperWidth string = "paperWidth"
paperHeight string = "paperHeight"
marginTop string = "marginTop"
marginBottom string = "marginBottom"
marginLeft string = "marginLeft"
marginRight string = "marginRight"
landscape string = "landscape"
remoteURL string = "remoteURL"
webhookURL string = "webhookURL"
paperWidth string = "paperWidth"
paperHeight string = "paperHeight"
marginTop string = "marginTop"
marginBottom string = "marginBottom"
marginLeft string = "marginLeft"
marginRight string = "marginRight"
landscape string = "landscape"
webFontsTimeout string = "webFontsTimeout"
)
// resource facilitates storing and accessing
@@ -31,6 +34,7 @@ type resource struct {
func newResource(c echo.Context) (*resource, error) {
v := make(map[string]string)
v[remoteURL] = c.FormValue(remoteURL)
v[webhookURL] = c.FormValue(webhookURL)
v[paperWidth] = c.FormValue(paperWidth)
v[paperHeight] = c.FormValue(paperHeight)
@@ -39,6 +43,7 @@ func newResource(c echo.Context) (*resource, error) {
v[marginLeft] = c.FormValue(marginLeft)
v[marginRight] = c.FormValue(marginRight)
v[landscape] = c.FormValue(landscape)
v[webFontsTimeout] = c.FormValue(webFontsTimeout)
dirPath, err := rand.Get()
if err != nil {
return nil, err
@@ -49,17 +54,17 @@ func newResource(c echo.Context) (*resource, error) {
r := &resource{values: v, dirPath: dirPath}
form, err := c.MultipartForm()
if err != nil {
return nil, fmt.Errorf("getting multipart form: %v", err)
return r, fmt.Errorf("getting multipart form: %v", err)
}
for _, files := range form.File {
for _, fh := range files {
in, err := fh.Open()
if err != nil {
return nil, fmt.Errorf("%s: opening file: %v", fh.Filename, err)
return r, fmt.Errorf("%s: opening file: %v", fh.Filename, err)
}
defer in.Close()
if err := r.writeFile(fh.Filename, in); err != nil {
return nil, err
return r, err
}
}
}
@@ -180,5 +185,24 @@ func (r *resource) landscape() (bool, error) {
return landscape, nil
}
func (r *resource) remoteURL() (string, error) {
if r.values[remoteURL] == "" {
return "", errors.New("no remote URL")
}
return r.values[remoteURL], nil
}
func (r *resource) webFontsTimeout() (int64, error) {
webFontsTimeoutStr := r.values[webFontsTimeout]
if webFontsTimeoutStr == "" {
return 500, nil
}
timeout, err := strconv.ParseInt(webFontsTimeoutStr, 10, 64)
if err != nil {
return 500, fmt.Errorf("web fonts timeout: %v", err)
}
return timeout, nil
}
func (r *resource) webhookURL() string { return r.values[webhookURL] }
func (r *resource) removeAll() error { return os.RemoveAll(r.dirPath) }

56
internal/app/api/url.go Normal file
View File

@@ -0,0 +1,56 @@
package api
import (
"github.com/labstack/echo"
"github.com/thecodingmachine/gotenberg/internal/pkg/printer"
)
func convertURL(c echo.Context) error {
r, err := newResource(c)
if err != nil {
return hijackErr(err, r)
}
ctx, cancel := newContext(r)
if cancel != nil {
defer cancel()
}
p := &printer.HTML{Context: ctx}
URL, err := r.remoteURL()
if err != nil {
return hijackErr(err, r)
}
p.URL = URL
headerPath, _ := r.filePath("header.html")
if err := p.WithHeaderFile(headerPath); err != nil {
return hijackErr(err, r)
}
footerPath, _ := r.filePath("footer.html")
if err := p.WithFooterFile(footerPath); err != nil {
return hijackErr(err, r)
}
paperSize, err := r.paperSize()
if err != nil {
return hijackErr(err, r)
}
p.PaperWidth = paperSize[0]
p.PaperHeight = paperSize[1]
paperMargins, err := r.paperMargins()
if err != nil {
return hijackErr(err, r)
}
p.MarginTop = paperMargins[0]
p.MarginBottom = paperMargins[1]
p.MarginLeft = paperMargins[2]
p.MarginRight = paperMargins[3]
landscape, err := r.landscape()
if err != nil {
return hijackErr(err, r)
}
p.Landscape = landscape
chromeWebFontsTimeout, err := r.webFontsTimeout()
if err != nil {
return hijackErr(err, r)
}
p.WebFontsTimeout = chromeWebFontsTimeout
return print(c, p, r)
}

View File

@@ -0,0 +1,24 @@
package api
import (
"net/http"
"net/http/httptest"
"testing"
"github.com/labstack/echo"
"github.com/stretchr/testify/assert"
"github.com/thecodingmachine/gotenberg/test"
)
func TestURL(t *testing.T) {
body, contentType := test.URLTestMultipartForm(t)
req := httptest.NewRequest(http.MethodPost, "/convert/url", body)
req.Header.Set(echo.HeaderContentType, contentType)
rec := httptest.NewRecorder()
e := echo.New()
c := e.NewContext(req, rec)
if assert.NoError(t, convertURL(c)) {
assert.Equal(t, http.StatusOK, rec.Code)
assert.Equal(t, "application/pdf", rec.Header().Get(echo.HeaderContentType))
}
}

View File

@@ -54,8 +54,9 @@ func (c *Chrome) getFullname() string {
func (c *Chrome) isViable() bool {
// check if Chrome is correctly running.
devt := devtool.New("http://127.0.0.1:9222")
_, err := devt.Create(context.TODO())
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
_, err := devtool.New("http://localhost:9222").Version(ctx)
return err == nil
}

View File

@@ -1,52 +1,7 @@
/*
Package printer contains structs which convert
a specific file type to PDF:
a specific file type to PDF.
// converting HTML to PDF.
p := &printer.HTML{
Context: context.Background(),
PaperWidth: 8.27,
PaperHeight: 11.7,
Landscape: false,
}
p.WithLocalURL("index.html")
if err := p.Print("result.pdf"); err != nil {
return err
}
// converting Markdown to PDF:
// it assumes here that our template "index.html"
// will call toHTML method to convert
// markdown files to HTML.
p := &printer.Markdown{
Context: context.Background(),
TemplatePath: "index.html",
PaperWidth: 8.27,
PaperHeight: 11.7,
Landscape: false,
}
if err := p.Print("result.pdf"); err != nil {
return err
}
// converting Office documents to PDF:
// it converts each files independently and
// then merge them.
//
// Also, as unoconv cannot perform
// concurrent conversions, a lock is applied.
p := &printer.Office{
Context: ctx,
FilePaths: []string{"document.docx", "presentation.pptx"}
}
if err := p.Print("result.pdf"); err != nil {
return err
}
It is also able to merge a list of PDF files:
if err := printer.Merge([]string{"foo.pdf", "bar.pdf"}, "result.pdf"); err != nil {
return err
}
It is also able to merge a list of PDF files.
*/
package printer

View File

@@ -18,17 +18,18 @@ import (
// HTML facilitates HTML to PDF conversion.
type HTML struct {
Context context.Context
URL string
HeaderHTML string
FooterHTML string
PaperWidth float64
PaperHeight float64
MarginTop float64
MarginBottom float64
MarginLeft float64
MarginRight float64
Landscape bool
Context context.Context
URL string
HeaderHTML string
FooterHTML string
PaperWidth float64
PaperHeight float64
MarginTop float64
MarginBottom float64
MarginLeft float64
MarginRight float64
Landscape bool
WebFontsTimeout int64
}
const defaultHeaderFooterHTML string = "<html><head></head><body></body></html>"
@@ -36,14 +37,15 @@ const defaultHeaderFooterHTML string = "<html><head></head><body></body></html>"
// Print converts HTML to PDF.
// Credits: https://medium.com/compass-true-north/go-service-to-convert-web-pages-to-pdf-using-headless-chrome-5fd9ffbae1af
func (html *HTML) Print(destination string) error {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
// use the DevTools HTTP/JSON API to manage targets (e.g. pages, webworkers).
devt := devtool.New("http://127.0.0.1:9222")
pt, err := devt.Create(html.Context)
devt, err := devtool.New("http://localhost:9222").Version(ctx)
if err != nil {
return fmt.Errorf("creating DevTools target: %v", err)
}
// open a new RPC connection to the Chrome Debugging Protocol target.
conn, err := rpcc.DialContext(html.Context, pt.WebSocketDebuggerURL)
conn, err := rpcc.DialContext(html.Context, devt.WebSocketDebuggerURL)
if err != nil {
return fmt.Errorf("creating RPC connection: %v", err)
}
@@ -102,14 +104,17 @@ func (html *HTML) Print(destination string) error {
return fmt.Errorf("waiting for page loading: %v", err)
}
// inject a script to make sure web fonts are loaded.
script := `new Promise((resolve, reject) => {
script := fmt.Sprintf(`new Promise((resolve, reject) => {
document.fonts.ready.then(function () {
resolve('fonts loaded');
});
setTimeout(resolve.bind(resolve, 'timeout'), %.0f);
});`
setTimeout(resolve.bind(resolve, 'timeout'), %d);
});`, html.WebFontsTimeout)
scriptArg := runtime.NewEvaluateArgs(script).SetAwaitPromise(true)
returnObj, _ := c.Runtime.Evaluate(html.Context, scriptArg)
if returnObj.ExceptionDetails != nil {
return fmt.Errorf("script evaluated with exception: %+v", returnObj.ExceptionDetails)
}
loadFontsResult := string(returnObj.Result.Value)
if strings.Contains(loadFontsResult, "timeout") {
return errors.New("timed out loading fonts")

View File

@@ -15,17 +15,18 @@ import (
// Markdown facilitates Markdown to PDF conversion.
type Markdown struct {
Context context.Context
TemplatePath string
HeaderHTML string
FooterHTML string
PaperWidth float64
PaperHeight float64
MarginTop float64
MarginBottom float64
MarginLeft float64
MarginRight float64
Landscape bool
Context context.Context
TemplatePath string
HeaderHTML string
FooterHTML string
PaperWidth float64
PaperHeight float64
MarginTop float64
MarginBottom float64
MarginLeft float64
MarginRight float64
Landscape bool
WebFontsTimeout int64
html *HTML
}
@@ -52,6 +53,7 @@ func (md *Markdown) Print(destination string) error {
md.html.MarginLeft = md.MarginLeft
md.html.MarginRight = md.MarginRight
md.html.Landscape = md.Landscape
md.html.WebFontsTimeout = md.WebFontsTimeout
tmpl, err := template.
New(filepath.Base(md.TemplatePath)).
Funcs(template.FuncMap{"toHTML": toHTML}).

View File

@@ -18,6 +18,7 @@ var mu sync.Mutex
type Office struct {
Context context.Context
FilePaths []string
Landscape bool
}
// Print converts Office documents to PDF.
@@ -32,14 +33,18 @@ func (o *Office) Print(destination string) error {
return err
}
tmpDest := fmt.Sprintf("%s/%s.pdf", dirPath, baseFilename)
cmdArgs := []string{
"--format",
"pdf",
}
if o.Landscape {
cmdArgs = append(cmdArgs, "--printer", "PaperOrientation=landscape")
}
cmdArgs = append(cmdArgs, "--output", tmpDest, fpath)
cmd := exec.CommandContext(
o.Context,
"unoconv",
"--format",
"pdf",
"--output",
tmpDest,
fpath,
cmdArgs...,
)
_, err = cmd.Output()
if o.Context.Err() == context.DeadlineExceeded {

View File

@@ -20,6 +20,8 @@ func TestOffice(t *testing.T) {
Context: ctx,
FilePaths: []string{
fmt.Sprintf("%s/%s", dirPath, "document.docx"),
fmt.Sprintf("%s/%s", dirPath, "document.txt"),
fmt.Sprintf("%s/%s", dirPath, "document.rtf"),
},
}
dst := fmt.Sprintf("%s/%s", dirPath, "foo.pdf")

View File

@@ -3,11 +3,19 @@ package printer
import (
"fmt"
"io/ioutil"
"os/exec"
pdfcpuAPI "github.com/hhrutter/pdfcpu/pkg/api"
pdfcpuLog "github.com/hhrutter/pdfcpu/pkg/log"
pdfcpuConfig "github.com/hhrutter/pdfcpu/pkg/pdfcpu"
)
func init() {
// disable loggers when merging
// PDFs.
pdfcpuLog.DisableLoggers()
}
// Printer is a type that can create a PDF file from a source.
// The source is defined in the underlying implementation.
type Printer interface {
@@ -16,9 +24,18 @@ type Printer interface {
// Merge merges PDF files.
func Merge(fpaths []string, destination string) error {
cmd := pdfcpuAPI.MergeCommand(fpaths, destination, pdfcpuConfig.NewDefaultConfiguration())
_, err := pdfcpuAPI.Merge(cmd)
return err
cmdcpu := pdfcpuAPI.MergeCommand(fpaths, destination, pdfcpuConfig.NewDefaultConfiguration())
_, err := pdfcpuAPI.Merge(cmdcpu)
if err == nil {
return nil
}
// if pdfcpu failed to merge PDF files...
// https://github.com/thecodingmachine/gotenberg/issues/29
var cmdArgs []string
cmdArgs = append(cmdArgs, fpaths...)
cmdArgs = append(cmdArgs, "cat", "output", destination)
cmd := exec.Command("pdftk", cmdArgs...)
return cmd.Run()
}
func writeBytesToFile(dst string, b []byte) error {

View File

@@ -16,7 +16,7 @@ func TestMerge(t *testing.T) {
err := Merge(
[]string{
fmt.Sprintf("%s/%s", dirPath, "gotenberg.pdf"),
fmt.Sprintf("%s/%s", dirPath, "gotenberg.pdf"),
fmt.Sprintf("%s/%s", dirPath, "gotenberg_bis.pdf"),
},
dst,
)

View File

@@ -16,19 +16,17 @@ import "github.com/thecodingmachine/gotenberg/pkg"
func main() {
// HTML conversion example.
c := &gotenberg.Client{Hostname: "http://localhost:3000"}
req := &gotenberg.HTMLRequest{
IndexFilePath: "index.html",
AssetFilePaths: []string{
"style.css",
"img.png",
},
Options: &gotenberg.HTMLOptions{
HeaderFilePath: "header.html",
FooterFilePath: "footer.html",
PaperSize: gotenberg.A4,
PaperMargins: gotenberg.NormalMargins,
},
}
req, _ := gotenberg.NewHTMLRequest("index.html")
req.SetHeader("header.html")
req.SetFooter("footer.html")
req.SetAssets(
"font.woff",
"img.gif",
"style.css",
)
req.SetPaperSize(gotenberg.A4)
req.SetMargins(gotenberg.NormalMargins)
req.SetLandscape(false)
dest := "foo.pdf"
c.Store(req, dest)
}

View File

@@ -13,14 +13,16 @@ import (
)
const (
webhookURL string = "webhookURL"
paperWidth string = "paperWidth"
paperHeight string = "paperHeight"
marginTop string = "marginTop"
marginBottom string = "marginBottom"
marginLeft string = "marginLeft"
marginRight string = "marginRight"
landscape string = "landscape"
remoteURL string = "remoteURL"
webhookURL string = "webhookURL"
paperWidth string = "paperWidth"
paperHeight string = "paperHeight"
marginTop string = "marginTop"
marginBottom string = "marginBottom"
marginLeft string = "marginLeft"
marginRight string = "marginRight"
landscape string = "landscape"
webFontsTimeout string = "webFontsTimeout"
)
var (
@@ -59,18 +61,34 @@ type Client struct {
// form values and form files to
// the Gotenberg API.
type Request interface {
validate() error
SetWebhookURL(webhookURL string)
getPostURL() string
getFormValues() map[string]string
getFormFiles() map[string]string
}
// ChromeRequest is a type for sending
// conversion requests which will be
// handle by Google Chrome.
type ChromeRequest interface {
SetHeader(fpath string) error
SetFooter(fpath string) error
SetPaperSize(size [2]float64)
SetMargins(margins [4]float64)
SetLandscape(isLandscape bool)
SetWebFontsTimeout(timeout int64)
}
// UnoconvRequest is a type for sending
// conversion requests which will be
// handle by unoconv.
type UnoconvRequest interface {
SetLandscape(landscape bool)
}
// Post sends a request to the Gotenberg API
// and returns the response.
func (c *Client) Post(req Request) (*http.Response, error) {
if err := req.validate(); err != nil {
return nil, err
}
body, contentType, err := multipartForm(req)
if err != nil {
return nil, err

View File

@@ -2,30 +2,7 @@
Package gotenberg is a Go client for
interacting with a Gotenberg API.
For instance, if you want to convert HTML:
import "github.com/thecodingmachine/gotenberg/pkg"
func main() {
// HTML conversion example.
c := &gotenberg.Client{Hostname: "http://localhost:3000"}
req := &gotenberg.HTMLRequest{
IndexFilePath: "index.html",
AssetFilePaths: []string{
"style.css",
"img.png",
},
Options: &gotenberg.HTMLOptions{
HeaderFilePath: "header.html",
FooterFilePath: "footer.html",
PaperSize: gotenberg.A4,
PaperMargins: gotenberg.NormalMargins,
},
}
dest := "foo.pdf"
c.Store(req, dest)
}
For more complete usages, head to the https://thecodingmachine.gotenberg.github.io.
For more complete usages, head to the documentation:
https://thecodingmachine.github.io/gotenberg/
*/
package gotenberg

View File

@@ -9,64 +9,94 @@ import (
// HTMLRequest facilitates HTML conversion
// with the Gotenberg API.
type HTMLRequest struct {
IndexFilePath string
AssetFilePaths []string
Options *HTMLOptions
indexFilePath string
headerFilePath string
footerFilePath string
assetFilePaths []string
values map[string]string
}
// HTMLOptions gathers all options
// for HTML conversion with the Gotenberg API.
type HTMLOptions struct {
WebHookURL string
HeaderFilePath string
FooterFilePath string
PaperSize [2]float64
PaperMargins [4]float64
Landscape bool
// NewHTMLRequest create HTMLRequest.
func NewHTMLRequest(indexFilePath string) (*HTMLRequest, error) {
if !fileExists(indexFilePath) {
return nil, fmt.Errorf("%s: index file does not exist", indexFilePath)
}
return &HTMLRequest{indexFilePath: indexFilePath, values: make(map[string]string)}, nil
}
func (html *HTMLRequest) validate() error {
if !fileExists(html.IndexFilePath) {
return fmt.Errorf("%s: index file does not exist", html.IndexFilePath)
}
if html.Options.HeaderFilePath != "" && !fileExists(html.Options.HeaderFilePath) {
return fmt.Errorf("%s: header file does not exist", html.Options.HeaderFilePath)
}
if html.Options.FooterFilePath != "" && !fileExists(html.Options.FooterFilePath) {
return fmt.Errorf("%s: footer file does not exist", html.Options.FooterFilePath)
// SetWebhookURL sets webhookURL form field.
func (html *HTMLRequest) SetWebhookURL(webhookURL string) {
html.values[webhookURL] = webhookURL
}
// SetHeader sets header form file.
func (html *HTMLRequest) SetHeader(fpath string) error {
if !fileExists(fpath) {
return fmt.Errorf("%s: header file does not exist", fpath)
}
html.headerFilePath = fpath
return nil
}
// SetFooter sets footer form file.
func (html *HTMLRequest) SetFooter(fpath string) error {
if !fileExists(fpath) {
return fmt.Errorf("%s: footer file does not exist", fpath)
}
html.footerFilePath = fpath
return nil
}
// SetAssets sets assets form files.
func (html *HTMLRequest) SetAssets(fpaths ...string) error {
for _, fpath := range fpaths {
if !fileExists(fpath) {
return fmt.Errorf("%s: file does not exist", fpath)
}
}
html.assetFilePaths = fpaths
return nil
}
// SetPaperSize sets paperWidth and paperHeight form fields.
func (html *HTMLRequest) SetPaperSize(size [2]float64) {
html.values[paperWidth] = fmt.Sprintf("%f", size[0])
html.values[paperHeight] = fmt.Sprintf("%f", size[1])
}
// SetMargins sets marginTop, marginBottom,
// marginLeft and marginRight form fields.
func (html *HTMLRequest) SetMargins(margins [4]float64) {
html.values[marginTop] = fmt.Sprintf("%f", margins[0])
html.values[marginBottom] = fmt.Sprintf("%f", margins[1])
html.values[marginLeft] = fmt.Sprintf("%f", margins[2])
html.values[marginRight] = fmt.Sprintf("%f", margins[3])
}
// SetLandscape sets landscape form field.
func (html *HTMLRequest) SetLandscape(isLandscape bool) {
html.values[landscape] = strconv.FormatBool(isLandscape)
}
// SetWebFontsTimeout sets webFontsTimeout form field.
func (html *HTMLRequest) SetWebFontsTimeout(timeout int64) {
html.values[webFontsTimeout] = strconv.FormatInt(timeout, 10)
}
func (html *HTMLRequest) getPostURL() string {
return "/convert/html"
}
func (html *HTMLRequest) getFormValues() map[string]string {
if html.Options == nil {
html.Options = &HTMLOptions{}
}
values := make(map[string]string)
values[webhookURL] = html.Options.WebHookURL
values[paperWidth] = fmt.Sprintf("%f", html.Options.PaperSize[0])
values[paperHeight] = fmt.Sprintf("%f", html.Options.PaperSize[1])
values[marginTop] = fmt.Sprintf("%f", html.Options.PaperMargins[0])
values[marginBottom] = fmt.Sprintf("%f", html.Options.PaperMargins[1])
values[marginLeft] = fmt.Sprintf("%f", html.Options.PaperMargins[2])
values[marginRight] = fmt.Sprintf("%f", html.Options.PaperMargins[3])
values[landscape] = strconv.FormatBool(html.Options.Landscape)
return values
return html.values
}
func (html *HTMLRequest) getFormFiles() map[string]string {
if html.Options == nil {
html.Options = &HTMLOptions{}
}
files := make(map[string]string)
files["index.html"] = html.IndexFilePath
files["header.html"] = html.Options.HeaderFilePath
files["footer.html"] = html.Options.FooterFilePath
for _, fpath := range html.AssetFilePaths {
files["index.html"] = html.indexFilePath
files["header.html"] = html.headerFilePath
files["footer.html"] = html.footerFilePath
for _, fpath := range html.assetFilePaths {
files[filepath.Base(fpath)] = fpath
}
return files
@@ -75,4 +105,5 @@ func (html *HTMLRequest) getFormFiles() map[string]string {
// Compile-time checks to ensure type implements desired interfaces.
var (
_ = Request(new(HTMLRequest))
_ = ChromeRequest(new(HTMLRequest))
)

View File

@@ -13,20 +13,21 @@ import (
func TestHTML(t *testing.T) {
c := &Client{Hostname: "http://localhost:3000"}
req := &HTMLRequest{
IndexFilePath: test.HTMLTestFilePath(t, "index.html"),
AssetFilePaths: []string{
test.HTMLTestFilePath(t, "font.woff"),
test.HTMLTestFilePath(t, "img.gif"),
test.HTMLTestFilePath(t, "style.css"),
},
Options: &HTMLOptions{
HeaderFilePath: test.HTMLTestFilePath(t, "header.html"),
FooterFilePath: test.HTMLTestFilePath(t, "footer.html"),
PaperSize: A4,
PaperMargins: NormalMargins,
},
}
req, err := NewHTMLRequest(test.HTMLTestFilePath(t, "index.html"))
require.Nil(t, err)
err = req.SetHeader(test.HTMLTestFilePath(t, "header.html"))
require.Nil(t, err)
err = req.SetFooter(test.HTMLTestFilePath(t, "footer.html"))
require.Nil(t, err)
err = req.SetAssets(
test.HTMLTestFilePath(t, "font.woff"),
test.HTMLTestFilePath(t, "img.gif"),
test.HTMLTestFilePath(t, "style.css"),
)
require.Nil(t, err)
req.SetPaperSize(A4)
req.SetMargins(NormalMargins)
req.SetLandscape(false)
dirPath, err := rand.Get()
require.Nil(t, err)
dest := fmt.Sprintf("%s/foo.pdf", dirPath)
@@ -36,3 +37,11 @@ func TestHTML(t *testing.T) {
err = os.RemoveAll(dirPath)
assert.Nil(t, err)
}
func TestConcurrentHTML(t *testing.T) {
for i := 0; i < 10; i++ {
go func() {
TestHTML(t)
}()
}
}

View File

@@ -9,73 +9,107 @@ import (
// MarkdownRequest facilitates Markdown conversion
// with the Gotenberg API.
type MarkdownRequest struct {
IndexFilePath string
MarkdownFilePaths []string
AssetFilePaths []string
Options *MarkdownOptions
indexFilePath string
markdownFilePaths []string
headerFilePath string
footerFilePath string
assetFilePaths []string
values map[string]string
}
// MarkdownOptions gathers all options
// for Markdown conversion with the Gotenberg API.
type MarkdownOptions struct {
WebHookURL string
HeaderFilePath string
FooterFilePath string
PaperSize [2]float64
PaperMargins [4]float64
Landscape bool
}
func (markdown *MarkdownRequest) validate() error {
if !fileExists(markdown.IndexFilePath) {
return fmt.Errorf("%s: index file does not exist", markdown.IndexFilePath)
// NewMarkdownRequest create MarkdownRequest.
func NewMarkdownRequest(indexFilePath string, markdownFilePaths ...string) (*MarkdownRequest, error) {
if !fileExists(indexFilePath) {
return nil, fmt.Errorf("%s: index file does not exist", indexFilePath)
}
if markdown.Options.HeaderFilePath != "" && !fileExists(markdown.Options.HeaderFilePath) {
return fmt.Errorf("%s: header file does not exist", markdown.Options.HeaderFilePath)
}
if markdown.Options.FooterFilePath != "" && !fileExists(markdown.Options.FooterFilePath) {
return fmt.Errorf("%s: footer file does not exist", markdown.Options.FooterFilePath)
}
for _, fpath := range markdown.MarkdownFilePaths {
for _, fpath := range markdownFilePaths {
if !fileExists(fpath) {
return fmt.Errorf("%s: markdown file does not exist", fpath)
return nil, fmt.Errorf("%s: markdown file does not exist", fpath)
}
}
return &MarkdownRequest{
indexFilePath: indexFilePath,
markdownFilePaths: markdownFilePaths,
values: make(map[string]string),
}, nil
}
// SetWebhookURL sets webhookURL form field.
func (markdown *MarkdownRequest) SetWebhookURL(webhookURL string) {
markdown.values[webhookURL] = webhookURL
}
// SetHeader sets header form file.
func (markdown *MarkdownRequest) SetHeader(fpath string) error {
if !fileExists(fpath) {
return fmt.Errorf("%s: header file does not exist", fpath)
}
markdown.headerFilePath = fpath
return nil
}
// SetFooter sets footer form file.
func (markdown *MarkdownRequest) SetFooter(fpath string) error {
if !fileExists(fpath) {
return fmt.Errorf("%s: footer file does not exist", fpath)
}
markdown.footerFilePath = fpath
return nil
}
// SetAssets sets assets form files.
func (markdown *MarkdownRequest) SetAssets(fpaths ...string) error {
for _, fpath := range fpaths {
if !fileExists(fpath) {
return fmt.Errorf("%s: file does not exist", fpath)
}
}
markdown.assetFilePaths = fpaths
return nil
}
// SetPaperSize sets paperWidth and paperHeight form fields.
func (markdown *MarkdownRequest) SetPaperSize(size [2]float64) {
markdown.values[paperWidth] = fmt.Sprintf("%f", size[0])
markdown.values[paperHeight] = fmt.Sprintf("%f", size[1])
}
// SetMargins sets marginTop, marginBottom,
// marginLeft and marginRight form fields.
func (markdown *MarkdownRequest) SetMargins(margins [4]float64) {
markdown.values[marginTop] = fmt.Sprintf("%f", margins[0])
markdown.values[marginBottom] = fmt.Sprintf("%f", margins[1])
markdown.values[marginLeft] = fmt.Sprintf("%f", margins[2])
markdown.values[marginRight] = fmt.Sprintf("%f", margins[3])
}
// SetLandscape sets landscape form field.
func (markdown *MarkdownRequest) SetLandscape(isLandscape bool) {
markdown.values[landscape] = strconv.FormatBool(isLandscape)
}
// SetWebFontsTimeout sets webFontsTimeout form field.
func (markdown *MarkdownRequest) SetWebFontsTimeout(timeout int64) {
markdown.values[webFontsTimeout] = strconv.FormatInt(timeout, 10)
}
func (markdown *MarkdownRequest) getPostURL() string {
return "/convert/markdown"
}
func (markdown *MarkdownRequest) getFormValues() map[string]string {
if markdown.Options == nil {
markdown.Options = &MarkdownOptions{}
}
values := make(map[string]string)
values[webhookURL] = markdown.Options.WebHookURL
values[paperWidth] = fmt.Sprintf("%f", markdown.Options.PaperSize[0])
values[paperHeight] = fmt.Sprintf("%f", markdown.Options.PaperSize[1])
values[marginTop] = fmt.Sprintf("%f", markdown.Options.PaperMargins[0])
values[marginBottom] = fmt.Sprintf("%f", markdown.Options.PaperMargins[1])
values[marginLeft] = fmt.Sprintf("%f", markdown.Options.PaperMargins[2])
values[marginRight] = fmt.Sprintf("%f", markdown.Options.PaperMargins[3])
values[landscape] = strconv.FormatBool(markdown.Options.Landscape)
return values
return markdown.values
}
func (markdown *MarkdownRequest) getFormFiles() map[string]string {
if markdown.Options == nil {
markdown.Options = &MarkdownOptions{}
}
files := make(map[string]string)
files["index.html"] = markdown.IndexFilePath
files["header.html"] = markdown.Options.HeaderFilePath
files["footer.html"] = markdown.Options.FooterFilePath
for _, fpath := range markdown.MarkdownFilePaths {
files["index.html"] = markdown.indexFilePath
files["header.html"] = markdown.headerFilePath
files["footer.html"] = markdown.footerFilePath
for _, fpath := range markdown.markdownFilePaths {
files[filepath.Base(fpath)] = fpath
}
for _, fpath := range markdown.AssetFilePaths {
for _, fpath := range markdown.assetFilePaths {
files[filepath.Base(fpath)] = fpath
}
return files
@@ -84,4 +118,5 @@ func (markdown *MarkdownRequest) getFormFiles() map[string]string {
// Compile-time checks to ensure type implements desired interfaces.
var (
_ = Request(new(MarkdownRequest))
_ = ChromeRequest(new(MarkdownRequest))
)

View File

@@ -13,25 +13,26 @@ import (
func TestMarkdown(t *testing.T) {
c := &Client{Hostname: "http://localhost:3000"}
req := &MarkdownRequest{
IndexFilePath: test.MarkdownTestFilePath(t, "index.html"),
MarkdownFilePaths: []string{
test.MarkdownTestFilePath(t, "paragraph1.md"),
test.MarkdownTestFilePath(t, "paragraph2.md"),
test.MarkdownTestFilePath(t, "paragraph3.md"),
},
AssetFilePaths: []string{
test.HTMLTestFilePath(t, "font.woff"),
test.HTMLTestFilePath(t, "img.gif"),
test.HTMLTestFilePath(t, "style.css"),
},
Options: &MarkdownOptions{
HeaderFilePath: test.MarkdownTestFilePath(t, "header.html"),
FooterFilePath: test.MarkdownTestFilePath(t, "footer.html"),
PaperSize: A4,
PaperMargins: NormalMargins,
},
}
req, err := NewMarkdownRequest(
test.MarkdownTestFilePath(t, "index.html"),
test.MarkdownTestFilePath(t, "paragraph1.md"),
test.MarkdownTestFilePath(t, "paragraph2.md"),
test.MarkdownTestFilePath(t, "paragraph3.md"),
)
require.Nil(t, err)
err = req.SetHeader(test.MarkdownTestFilePath(t, "header.html"))
require.Nil(t, err)
err = req.SetFooter(test.MarkdownTestFilePath(t, "footer.html"))
require.Nil(t, err)
err = req.SetAssets(
test.MarkdownTestFilePath(t, "font.woff"),
test.MarkdownTestFilePath(t, "img.gif"),
test.MarkdownTestFilePath(t, "style.css"),
)
require.Nil(t, err)
req.SetPaperSize(A4)
req.SetMargins(NormalMargins)
req.SetLandscape(false)
dirPath, err := rand.Get()
require.Nil(t, err)
dest := fmt.Sprintf("%s/foo.pdf", dirPath)
@@ -41,3 +42,11 @@ func TestMarkdown(t *testing.T) {
err = os.RemoveAll(dirPath)
assert.Nil(t, err)
}
func TestConcurrentMarkdown(t *testing.T) {
for i := 0; i < 10; i++ {
go func() {
TestMarkdown(t)
}()
}
}

View File

@@ -8,24 +8,23 @@ import (
// MergeRequest facilitates merging PDF
// with the Gotenberg API.
type MergeRequest struct {
FilePaths []string
Options *MergeOptions
filePaths []string
values map[string]string
}
// MergeOptions gathers all options
// for merging PDF
// with the Gotenberg API.
type MergeOptions struct {
WebHookURL string
}
func (merge *MergeRequest) validate() error {
for _, fpath := range merge.FilePaths {
// NewMergeRequest create MergeRequest.
func NewMergeRequest(fpaths ...string) (*MergeRequest, error) {
for _, fpath := range fpaths {
if !fileExists(fpath) {
return fmt.Errorf("%s: PDF file does not exist", fpath)
return nil, fmt.Errorf("%s: file does not exist", fpath)
}
}
return nil
return &MergeRequest{filePaths: fpaths, values: make(map[string]string)}, nil
}
// SetWebhookURL sets webhookURL form field.
func (merge *MergeRequest) SetWebhookURL(webhookURL string) {
merge.values[webhookURL] = webhookURL
}
func (merge *MergeRequest) getPostURL() string {
@@ -33,17 +32,12 @@ func (merge *MergeRequest) getPostURL() string {
}
func (merge *MergeRequest) getFormValues() map[string]string {
if merge.Options == nil {
merge.Options = &MergeOptions{}
}
values := make(map[string]string)
values[webhookURL] = merge.Options.WebHookURL
return values
return merge.values
}
func (merge *MergeRequest) getFormFiles() map[string]string {
files := make(map[string]string)
for _, fpath := range merge.FilePaths {
for _, fpath := range merge.filePaths {
files[filepath.Base(fpath)] = fpath
}
return files

View File

@@ -13,12 +13,11 @@ import (
func TestMerge(t *testing.T) {
c := &Client{Hostname: "http://localhost:3000"}
req := &MergeRequest{
FilePaths: []string{
test.PDFTestFilePath(t, "gotenberg.pdf"),
test.PDFTestFilePath(t, "gotenberg.pdf"),
},
}
req, err := NewMergeRequest(
test.PDFTestFilePath(t, "gotenberg.pdf"),
test.PDFTestFilePath(t, "gotenberg.pdf"),
)
require.Nil(t, err)
dirPath, err := rand.Get()
require.Nil(t, err)
dest := fmt.Sprintf("%s/foo.pdf", dirPath)
@@ -28,3 +27,11 @@ func TestMerge(t *testing.T) {
err = os.RemoveAll(dirPath)
assert.Nil(t, err)
}
func TestConcurrentMerge(t *testing.T) {
for i := 0; i < 10; i++ {
go func() {
TestMerge(t)
}()
}
}

View File

@@ -3,29 +3,34 @@ package gotenberg
import (
"fmt"
"path/filepath"
"strconv"
)
// OfficeRequest facilitates Office documents
// conversion with the Gotenberg API.
type OfficeRequest struct {
FilePaths []string
Options *OfficeOptions
filePaths []string
values map[string]string
}
// OfficeOptions gathers all options
// for Office documents conversion
// with the Gotenberg API.
type OfficeOptions struct {
WebHookURL string
}
func (office *OfficeRequest) validate() error {
for _, fpath := range office.FilePaths {
// NewOfficeRequest create OfficeRequest.
func NewOfficeRequest(fpaths ...string) (*OfficeRequest, error) {
for _, fpath := range fpaths {
if !fileExists(fpath) {
return fmt.Errorf("%s: office file does not exist", fpath)
return nil, fmt.Errorf("%s: file does not exist", fpath)
}
}
return nil
return &OfficeRequest{filePaths: fpaths, values: make(map[string]string)}, nil
}
// SetWebhookURL sets webhookURL form field.
func (office *OfficeRequest) SetWebhookURL(webhookURL string) {
office.values[webhookURL] = webhookURL
}
// SetLandscape sets landscape form field.
func (office *OfficeRequest) SetLandscape(isLandscape bool) {
office.values[landscape] = strconv.FormatBool(isLandscape)
}
func (office *OfficeRequest) getPostURL() string {
@@ -33,17 +38,12 @@ func (office *OfficeRequest) getPostURL() string {
}
func (office *OfficeRequest) getFormValues() map[string]string {
if office.Options == nil {
office.Options = &OfficeOptions{}
}
values := make(map[string]string)
values[webhookURL] = office.Options.WebHookURL
return values
return office.values
}
func (office *OfficeRequest) getFormFiles() map[string]string {
files := make(map[string]string)
for _, fpath := range office.FilePaths {
for _, fpath := range office.filePaths {
files[filepath.Base(fpath)] = fpath
}
return files
@@ -52,4 +52,5 @@ func (office *OfficeRequest) getFormFiles() map[string]string {
// Compile-time checks to ensure type implements desired interfaces.
var (
_ = Request(new(OfficeRequest))
_ = UnoconvRequest(new(OfficeRequest))
)

View File

@@ -13,11 +13,9 @@ import (
func TestOffice(t *testing.T) {
c := &Client{Hostname: "http://localhost:3000"}
req := &OfficeRequest{
FilePaths: []string{
test.OfficeTestFilePath(t, "document.docx"),
},
}
req, err := NewOfficeRequest(test.OfficeTestFilePath(t, "document.docx"))
require.Nil(t, err)
req.SetLandscape(false)
dirPath, err := rand.Get()
require.Nil(t, err)
dest := fmt.Sprintf("%s/foo.pdf", dirPath)
@@ -27,3 +25,11 @@ func TestOffice(t *testing.T) {
err = os.RemoveAll(dirPath)
assert.Nil(t, err)
}
func TestConcurrentOffice(t *testing.T) {
for i := 0; i < 10; i++ {
go func() {
TestOffice(t)
}()
}
}

90
pkg/url.go Normal file
View File

@@ -0,0 +1,90 @@
package gotenberg
import (
"fmt"
"strconv"
)
// URLRequest facilitates remote URL conversion
// with the Gotenberg API.
type URLRequest struct {
headerFilePath string
footerFilePath string
values map[string]string
}
// NewURLRequest create URLRequest.
func NewURLRequest(URL string) *URLRequest {
req := &URLRequest{values: make(map[string]string)}
req.values[remoteURL] = URL
return req
}
// SetWebhookURL sets webhookURL form field.
func (url *URLRequest) SetWebhookURL(webhookURL string) {
url.values[webhookURL] = webhookURL
}
// SetHeader sets header form file.
func (url *URLRequest) SetHeader(fpath string) error {
if !fileExists(fpath) {
return fmt.Errorf("%s: header file does not exist", fpath)
}
url.headerFilePath = fpath
return nil
}
// SetFooter sets footer form file.
func (url *URLRequest) SetFooter(fpath string) error {
if !fileExists(fpath) {
return fmt.Errorf("%s: footer file does not exist", fpath)
}
url.footerFilePath = fpath
return nil
}
// SetPaperSize sets paperWidth and paperHeight form fields.
func (url *URLRequest) SetPaperSize(size [2]float64) {
url.values[paperWidth] = fmt.Sprintf("%f", size[0])
url.values[paperHeight] = fmt.Sprintf("%f", size[1])
}
// SetMargins sets marginTop, marginBottom,
// marginLeft and marginRight form fields.
func (url *URLRequest) SetMargins(margins [4]float64) {
url.values[marginTop] = fmt.Sprintf("%f", margins[0])
url.values[marginBottom] = fmt.Sprintf("%f", margins[1])
url.values[marginLeft] = fmt.Sprintf("%f", margins[2])
url.values[marginRight] = fmt.Sprintf("%f", margins[3])
}
// SetLandscape sets landscape form field.
func (url *URLRequest) SetLandscape(isLandscape bool) {
url.values[landscape] = strconv.FormatBool(isLandscape)
}
// SetWebFontsTimeout sets webFontsTimeout form field.
func (url *URLRequest) SetWebFontsTimeout(timeout int64) {
url.values[webFontsTimeout] = strconv.FormatInt(timeout, 10)
}
func (url *URLRequest) getPostURL() string {
return "/convert/url"
}
func (url *URLRequest) getFormValues() map[string]string {
return url.values
}
func (url *URLRequest) getFormFiles() map[string]string {
files := make(map[string]string)
files["header.html"] = url.headerFilePath
files["footer.html"] = url.footerFilePath
return files
}
// Compile-time checks to ensure type implements desired interfaces.
var (
_ = Request(new(URLRequest))
_ = ChromeRequest(new(URLRequest))
)

41
pkg/url_test.go Normal file
View File

@@ -0,0 +1,41 @@
package gotenberg
import (
"fmt"
"os"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/thecodingmachine/gotenberg/internal/pkg/rand"
"github.com/thecodingmachine/gotenberg/test"
)
func TestURL(t *testing.T) {
c := &Client{Hostname: "http://localhost:3000"}
req := NewURLRequest("http://google.com")
err := req.SetHeader(test.URLTestFilePath(t, "header.html"))
require.Nil(t, err)
err = req.SetFooter(test.URLTestFilePath(t, "footer.html"))
require.Nil(t, err)
require.Nil(t, err)
req.SetPaperSize(A4)
req.SetMargins(NormalMargins)
req.SetLandscape(false)
dirPath, err := rand.Get()
require.Nil(t, err)
dest := fmt.Sprintf("%s/foo.pdf", dirPath)
err = c.Store(req, dest)
assert.Nil(t, err)
assert.FileExists(t, dest)
err = os.RemoveAll(dirPath)
assert.Nil(t, err)
}
func TestConcurrentURL(t *testing.T) {
for i := 0; i < 10; i++ {
go func() {
TestURL(t)
}()
}
}

View File

@@ -2,10 +2,10 @@
set -e
GOLANG_VERSION=1.11.2
VERSION="$1"
DOCKER_USER="$2"
DOCKER_PASSWORD="$3"
GOLANG_VERSION="$1"
VERSION="$2"
DOCKER_USER="$3"
DOCKER_PASSWORD="$4"
docker login -u "$DOCKER_USER" -p "$DOCKER_PASSWORD"
@@ -21,11 +21,13 @@ docker build -t thecodingmachine/gotenberg:base -f build/base/Dockerfile .
docker build \
--build-arg GOLANG_VERSION=${GOLANG_VERSION} \
--build-arg VERSION=${VERSION} \
-t thecodingmachine/gotenberg:latest \
-t thecodingmachine/gotenberg:${SEMVER[0]} \
-t thecodingmachine/gotenberg:${SEMVER[0]}.${SEMVER[1]} \
-t thecodingmachine/gotenberg:${SEMVER[0]}.${SEMVER[1]}.${SEMVER[2]} \
-f build/package/Dockerfile .
docker push "thecodingmachine/gotenberg:latest"
docker push "thecodingmachine/gotenberg:${SEMVER[0]}"
docker push "thecodingmachine/gotenberg:${SEMVER[0]}.${SEMVER[1]}"
docker push "thecodingmachine/gotenberg:${SEMVER[0]}.${SEMVER[1]}.${SEMVER[2]}"

2196
test/testdata/office/document.rtf vendored Normal file

File diff suppressed because it is too large Load Diff

3
test/testdata/office/document.txt vendored Normal file
View File

@@ -0,0 +1,3 @@
Gutenberg
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

BIN
test/testdata/pdf/gotenberg_bis.pdf vendored Normal file

Binary file not shown.

15
test/testdata/url/footer.html vendored Normal file
View File

@@ -0,0 +1,15 @@
<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>

13
test/testdata/url/header.html vendored Normal file
View File

@@ -0,0 +1,13 @@
<html>
<head>
<style>
body {
font-size: 8rem;
margin: 4rem auto;
}
</style>
</head>
<body>
<span class="title"></span>
</body>
</html>

View File

@@ -16,31 +16,38 @@ import (
"github.com/thecodingmachine/gotenberg/internal/pkg/rand"
)
// HTMLMultipartForm returns the body
// HTMLTestMultipartForm returns the body
// for a multipate/form-data request with all
// files under "html" folder.
func HTMLMultipartForm(t *testing.T) (*bytes.Buffer, string) {
func HTMLTestMultipartForm(t *testing.T) (*bytes.Buffer, string) {
return multipartForm(t, "html")
}
// MarkdownMultipartForm returns the body
// URLTestMultipartForm returns the body
// for a multipate/form-data request with all
// files under "url" folder.
func URLTestMultipartForm(t *testing.T) (*bytes.Buffer, string) {
return multipartForm(t, "url")
}
// MarkdownTestMultipartForm returns the body
// for a multipate/form-data request with all
// files under "markdown" folder.
func MarkdownMultipartForm(t *testing.T) (*bytes.Buffer, string) {
func MarkdownTestMultipartForm(t *testing.T) (*bytes.Buffer, string) {
return multipartForm(t, "markdown")
}
// OfficeMultipartForm returns the body
// OfficeTestMultipartForm returns the body
// for a multipate/form-data request with all
// files under "office" folder.
func OfficeMultipartForm(t *testing.T) (*bytes.Buffer, string) {
func OfficeTestMultipartForm(t *testing.T) (*bytes.Buffer, string) {
return multipartForm(t, "office")
}
// PDFMultipartForm returns the body
// PDFTestMultipartForm returns the body
// for a multipate/form-data request with all
// files under "pdf" folder.
func PDFMultipartForm(t *testing.T) (*bytes.Buffer, string) {
func PDFTestMultipartForm(t *testing.T) (*bytes.Buffer, string) {
return multipartForm(t, "pdf")
}
@@ -65,6 +72,10 @@ func multipartForm(t *testing.T, kind string) (*bytes.Buffer, string) {
_, err = io.Copy(part, file)
require.Nil(t, err)
}
if kind == "url" {
err := writer.WriteField("remoteURL", "http://google.com")
require.Nil(t, err)
}
return body, writer.FormDataContentType()
}
@@ -74,6 +85,12 @@ func HTMLTestDirPath(t *testing.T) string {
return copyDir(t, "html")
}
// URLTestDirPath creates a copy
// of "url" folder in test/testdata.
func URLTestDirPath(t *testing.T) string {
return copyDir(t, "url")
}
// MarkdownTestDirPath creates a copy
// of "markdown" folder in test/testdata.
func MarkdownTestDirPath(t *testing.T) string {
@@ -127,6 +144,12 @@ func HTMLTestFilePath(t *testing.T, filename string) string {
return abs(t, "html", filename)
}
// URLTestFilePath returns the absolute file path
// of a file in "url" folder in test/testdata.
func URLTestFilePath(t *testing.T, filename string) string {
return abs(t, "url", filename)
}
// MarkdownTestFilePath returns the absolute file path
// of a file in "markdown" folder in test/testdata.
func MarkdownTestFilePath(t *testing.T, filename string) string {