Compare commits

...

11 Commits

Author SHA1 Message Date
Julien Neuhart
697fa08d8b chore(godoc): add breaklines 2024-06-13 11:14:24 +02:00
garethjudson
669a35afef feat(libreoffice): add image options (#898)
* feat: libre office image options

* refactor: PR comment changes

* fix: change lossLessImageCompression default to false

---------

Co-authored-by: Gareth Judson <gareth.judson@delwp.vic.gov.au>
2024-06-13 10:28:36 +02:00
Julien Neuhart
b1d94a3845 feat(chromium): add user agent override 2024-06-12 20:51:10 +02:00
Julien Neuhart
06869cf491 chore(deps): update Golang dependencies 2024-06-12 20:51:10 +02:00
Julien Neuhart
ed67d8514c chore(deps): update golangci-lint 2024-06-04 17:02:05 +02:00
Julien Neuhart
07ec2f3d7b chore(deps): update Golang dependencies 2024-06-04 17:00:44 +02:00
Jonas Geiler
73d02e85c4 refactor(api): improved tests and test messages
Co-authored-by: Julien Neuhart <neuhart.julien@gmail.com>
2024-05-27 14:11:37 +02:00
Jonas Geiler
c1cf1eb37a chore: removed TODO.txt from .gitignore file 2024-05-27 14:11:37 +02:00
Jonas Geiler
7aecc94049 refactor(api): re-use error when starting the API server
Co-authored-by: Julien Neuhart <neuhart.julien@gmail.com>
2024-05-27 14:11:37 +02:00
Jonas Geiler
b55fbfeb8e refactor(api): updated tls validation error message
Co-authored-by: Julien Neuhart <neuhart.julien@gmail.com>
2024-05-27 14:11:37 +02:00
Jonas Geiler
20b8991fa4 feat(api): added tls/ssl support 2024-05-27 14:11:37 +02:00
20 changed files with 387 additions and 63 deletions

View File

@@ -24,7 +24,7 @@ jobs:
- name: Run linters - name: Run linters
uses: golangci/golangci-lint-action@v6 uses: golangci/golangci-lint-action@v6
with: with:
version: v1.58.2 version: v1.59.0
tests: tests:
needs: needs:

4
.gitignore vendored
View File

@@ -1,3 +1,5 @@
/coverage.html /coverage.html
/coverage.txt /coverage.txt
/TODO.txt .idea
.vscode
.DS_Store

View File

@@ -12,7 +12,7 @@ GOTENBERG_USER_GID=1001
GOTENBERG_USER_UID=1001 GOTENBERG_USER_UID=1001
NOTO_COLOR_EMOJI_VERSION=v2.042 # See https://github.com/googlefonts/noto-emoji/releases. NOTO_COLOR_EMOJI_VERSION=v2.042 # See https://github.com/googlefonts/noto-emoji/releases.
PDFTK_VERSION=v3.3.3 # See https://gitlab.com/pdftk-java/pdftk/-/releases - Binary package. PDFTK_VERSION=v3.3.3 # See https://gitlab.com/pdftk-java/pdftk/-/releases - Binary package.
GOLANGCI_LINT_VERSION=v1.58.2 # See https://github.com/golangci/golangci-lint/releases. GOLANGCI_LINT_VERSION=v1.59.0 # See https://github.com/golangci/golangci-lint/releases.
.PHONY: build .PHONY: build
build: ## Build the Gotenberg's Docker image build: ## Build the Gotenberg's Docker image

24
go.mod
View File

@@ -6,13 +6,13 @@ require (
github.com/alexliesenfeld/health v0.8.0 github.com/alexliesenfeld/health v0.8.0
github.com/andybalholm/brotli v1.1.0 // indirect github.com/andybalholm/brotli v1.1.0 // indirect
github.com/barasher/go-exiftool v1.10.0 github.com/barasher/go-exiftool v1.10.0
github.com/chromedp/cdproto v0.0.0-20240519224452-66462be74baa github.com/chromedp/cdproto v0.0.0-20240602235142-49d0e97b7881
github.com/chromedp/chromedp v0.9.5 github.com/chromedp/chromedp v0.9.5
github.com/golang/snappy v0.0.4 // indirect github.com/golang/snappy v0.0.4 // indirect
github.com/google/uuid v1.6.0 github.com/google/uuid v1.6.0
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
github.com/hashicorp/go-retryablehttp v0.7.6 github.com/hashicorp/go-retryablehttp v0.7.7
github.com/klauspost/compress v1.17.8 // indirect github.com/klauspost/compress v1.17.9 // indirect
github.com/klauspost/pgzip v1.2.6 // indirect github.com/klauspost/pgzip v1.2.6 // indirect
github.com/labstack/echo/v4 v4.12.0 github.com/labstack/echo/v4 v4.12.0
github.com/labstack/gommon v0.4.2 github.com/labstack/gommon v0.4.2
@@ -28,13 +28,13 @@ require (
github.com/ulikunitz/xz v0.5.12 // indirect github.com/ulikunitz/xz v0.5.12 // indirect
go.uber.org/multierr v1.11.0 go.uber.org/multierr v1.11.0
go.uber.org/zap v1.27.0 go.uber.org/zap v1.27.0
golang.org/x/crypto v0.23.0 // indirect golang.org/x/crypto v0.24.0 // indirect
golang.org/x/image v0.16.0 // indirect golang.org/x/image v0.17.0 // indirect
golang.org/x/net v0.25.0 golang.org/x/net v0.26.0
golang.org/x/sync v0.7.0 golang.org/x/sync v0.7.0
golang.org/x/sys v0.20.0 // indirect golang.org/x/sys v0.21.0 // indirect
golang.org/x/term v0.20.0 golang.org/x/term v0.21.0
golang.org/x/text v0.15.0 golang.org/x/text v0.16.0
) )
require github.com/dlclark/regexp2 v1.11.0 require github.com/dlclark/regexp2 v1.11.0
@@ -58,13 +58,13 @@ require (
github.com/mattn/go-runewidth v0.0.15 // indirect github.com/mattn/go-runewidth v0.0.15 // indirect
github.com/pkg/errors v0.9.1 // indirect github.com/pkg/errors v0.9.1 // indirect
github.com/prometheus/client_model v0.6.1 // indirect github.com/prometheus/client_model v0.6.1 // indirect
github.com/prometheus/common v0.53.0 // indirect github.com/prometheus/common v0.54.0 // indirect
github.com/prometheus/procfs v0.15.0 // indirect github.com/prometheus/procfs v0.15.1 // indirect
github.com/rivo/uniseg v0.4.7 // indirect github.com/rivo/uniseg v0.4.7 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasttemplate v1.2.2 // indirect github.com/valyala/fasttemplate v1.2.2 // indirect
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect
golang.org/x/time v0.5.0 // indirect golang.org/x/time v0.5.0 // indirect
google.golang.org/protobuf v1.34.1 // indirect google.golang.org/protobuf v1.34.2 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect
) )

48
go.sum
View File

@@ -12,8 +12,8 @@ github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6r
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/chromedp/cdproto v0.0.0-20240202021202-6d0b6a386732/go.mod h1:GKljq0VrfU4D5yc+2qA6OVr8pmO/MBbPEWqWQ/oqGEs= github.com/chromedp/cdproto v0.0.0-20240202021202-6d0b6a386732/go.mod h1:GKljq0VrfU4D5yc+2qA6OVr8pmO/MBbPEWqWQ/oqGEs=
github.com/chromedp/cdproto v0.0.0-20240519224452-66462be74baa h1:T3Ho4BWIkoEoMPCj90W2HIPF/k56qk4JWzTs6JUBxVw= github.com/chromedp/cdproto v0.0.0-20240602235142-49d0e97b7881 h1:RAUqkPvbEDGPgCYVc4GefBqAorWJAjKpVHgsRZyJmGE=
github.com/chromedp/cdproto v0.0.0-20240519224452-66462be74baa/go.mod h1:GKljq0VrfU4D5yc+2qA6OVr8pmO/MBbPEWqWQ/oqGEs= github.com/chromedp/cdproto v0.0.0-20240602235142-49d0e97b7881/go.mod h1:GKljq0VrfU4D5yc+2qA6OVr8pmO/MBbPEWqWQ/oqGEs=
github.com/chromedp/chromedp v0.9.5 h1:viASzruPJOiThk7c5bueOUY91jGLJVximoEMGoH93rg= github.com/chromedp/chromedp v0.9.5 h1:viASzruPJOiThk7c5bueOUY91jGLJVximoEMGoH93rg=
github.com/chromedp/chromedp v0.9.5/go.mod h1:D4I2qONslauw/C7INoCir1BJkSwBYMyZgx8X276z3+Y= github.com/chromedp/chromedp v0.9.5/go.mod h1:D4I2qONslauw/C7INoCir1BJkSwBYMyZgx8X276z3+Y=
github.com/chromedp/sysutil v1.0.0 h1:+ZxhTpfpZlmchB58ih/LBHX52ky7w2VhQVKQMucy3Ic= github.com/chromedp/sysutil v1.0.0 h1:+ZxhTpfpZlmchB58ih/LBHX52ky7w2VhQVKQMucy3Ic=
@@ -51,8 +51,8 @@ github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9n
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k= github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k=
github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
github.com/hashicorp/go-retryablehttp v0.7.6 h1:TwRYfx2z2C4cLbXmT8I5PgP/xmuqASDyiVuGYfs9GZM= github.com/hashicorp/go-retryablehttp v0.7.7 h1:C8hUCYzor8PIfXHa4UrZkU4VvK8o9ISHxT2Q8+VepXU=
github.com/hashicorp/go-retryablehttp v0.7.6/go.mod h1:pkQpWZeYWskR+D1tR2O5OcBFOxfA7DoAO6xtkuQnHTk= github.com/hashicorp/go-retryablehttp v0.7.7/go.mod h1:pkQpWZeYWskR+D1tR2O5OcBFOxfA7DoAO6xtkuQnHTk=
github.com/hhrutter/lzw v1.0.0 h1:laL89Llp86W3rRs83LvKbwYRx6INE8gDn0XNb1oXtm0= github.com/hhrutter/lzw v1.0.0 h1:laL89Llp86W3rRs83LvKbwYRx6INE8gDn0XNb1oXtm0=
github.com/hhrutter/lzw v1.0.0/go.mod h1:2HC6DJSn/n6iAZfgM3Pg+cP1KxeWc3ezG8bBqW5+WEo= github.com/hhrutter/lzw v1.0.0/go.mod h1:2HC6DJSn/n6iAZfgM3Pg+cP1KxeWc3ezG8bBqW5+WEo=
github.com/hhrutter/tiff v1.0.1 h1:MIus8caHU5U6823gx7C6jrfoEvfSTGtEFRiM8/LOzC0= github.com/hhrutter/tiff v1.0.1 h1:MIus8caHU5U6823gx7C6jrfoEvfSTGtEFRiM8/LOzC0=
@@ -61,8 +61,8 @@ github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8Hm
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
github.com/klauspost/compress v1.11.4/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/compress v1.11.4/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
github.com/klauspost/compress v1.17.8 h1:YcnTYrq7MikUT7k0Yb5eceMmALQPYBW/Xltxn0NAMnU= github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA=
github.com/klauspost/compress v1.17.8/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
github.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= github.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
github.com/klauspost/pgzip v1.2.6 h1:8RXeL5crjEUFnR2/Sn6GJNWtSQ3Dk8pq4CL3jvdDyjU= github.com/klauspost/pgzip v1.2.6 h1:8RXeL5crjEUFnR2/Sn6GJNWtSQ3Dk8pq4CL3jvdDyjU=
@@ -108,10 +108,10 @@ github.com/prometheus/client_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQ
github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho= github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho=
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
github.com/prometheus/common v0.53.0 h1:U2pL9w9nmJwJDa4qqLQ3ZaePJ6ZTwt7cMD3AG3+aLCE= github.com/prometheus/common v0.54.0 h1:ZlZy0BgJhTwVZUn7dLOkwCZHUkrAqd3WYtcFCWnM1D8=
github.com/prometheus/common v0.53.0/go.mod h1:BrxBKv3FWBIGXw89Mg1AeBq7FSyRzXWI3l3e7W3RN5U= github.com/prometheus/common v0.54.0/go.mod h1:/TQgMJP5CuVYveyT7n/0Ix8yLNNXy9yRSkhnLTHPDIQ=
github.com/prometheus/procfs v0.15.0 h1:A82kmvXJq2jTu5YUhSGNlYoxh85zLnKgPz4bMZgI5Ek= github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
github.com/prometheus/procfs v0.15.0/go.mod h1:Y0RJ/Y5g5wJpkTisOtqwDSo4HwhGmLB4VQSw2sQJLHk= github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
@@ -143,28 +143,28 @@ go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI=
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM=
golang.org/x/image v0.16.0 h1:9kloLAKhUufZhA12l5fwnx2NZW39/we1UhBesW433jw= golang.org/x/image v0.17.0 h1:nTRVVdajgB8zCMZVsViyzhnMKPwYeroEERRC64JuLco=
golang.org/x/image v0.16.0/go.mod h1:ugSZItdV4nOxyqp56HmXwH0Ry0nBCpjnZdpDaIHdoPs= golang.org/x/image v0.17.0/go.mod h1:4yyo5vMFQjVjUcVk4jEQcU9MGy/rulF5WvUILseCM2E=
golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ=
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE=
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw= golang.org/x/term v0.21.0 h1:WVXCp+/EBEHOj53Rvu+7KiT/iElMrO8ACK16SMZ3jaA=
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0=
golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=

View File

@@ -28,6 +28,8 @@ func init() {
// middlewares or health checks. // middlewares or health checks.
type Api struct { type Api struct {
port int port int
tlsCertFile string
tlsKeyFile string
startTimeout time.Duration startTimeout time.Duration
timeout time.Duration timeout time.Duration
rootPath string rootPath string
@@ -159,6 +161,8 @@ func (a *Api) Descriptor() gotenberg.ModuleDescriptor {
fs := flag.NewFlagSet("api", flag.ExitOnError) fs := flag.NewFlagSet("api", flag.ExitOnError)
fs.Int("api-port", 3000, "Set the port on which the API should listen") fs.Int("api-port", 3000, "Set the port on which the API should listen")
fs.String("api-port-from-env", "", "Set the environment variable with the port on which the API should listen - override the default port") fs.String("api-port-from-env", "", "Set the environment variable with the port on which the API should listen - override the default port")
fs.String("api-tls-cert-file", "", "Path to the TLS/SSL certificate file - for HTTPS support")
fs.String("api-tls-key-file", "", "Path to the TLS/SSL key file - for HTTPS support")
fs.Duration("api-start-timeout", time.Duration(30)*time.Second, "Set the time limit for the API to start") fs.Duration("api-start-timeout", time.Duration(30)*time.Second, "Set the time limit for the API to start")
fs.Duration("api-timeout", time.Duration(30)*time.Second, "Set the time limit for requests") fs.Duration("api-timeout", time.Duration(30)*time.Second, "Set the time limit for requests")
fs.String("api-root-path", "/", "Set the root path of the API - for service discovery via URL paths") fs.String("api-root-path", "/", "Set the root path of the API - for service discovery via URL paths")
@@ -175,6 +179,8 @@ func (a *Api) Descriptor() gotenberg.ModuleDescriptor {
func (a *Api) Provision(ctx *gotenberg.Context) error { func (a *Api) Provision(ctx *gotenberg.Context) error {
flags := ctx.ParsedFlags() flags := ctx.ParsedFlags()
a.port = flags.MustInt("api-port") a.port = flags.MustInt("api-port")
a.tlsCertFile = flags.MustString("api-tls-cert-file")
a.tlsKeyFile = flags.MustString("api-tls-key-file")
a.startTimeout = flags.MustDuration("api-start-timeout") a.startTimeout = flags.MustDuration("api-start-timeout")
a.timeout = flags.MustDuration("api-timeout") a.timeout = flags.MustDuration("api-timeout")
a.rootPath = flags.MustString("api-root-path") a.rootPath = flags.MustString("api-root-path")
@@ -301,6 +307,12 @@ func (a *Api) Validate() error {
) )
} }
if (a.tlsCertFile != "" && a.tlsKeyFile == "") || (a.tlsCertFile == "" && a.tlsKeyFile != "") {
err = multierr.Append(err,
errors.New("both TLS certificate and key files must be set"),
)
}
if !strings.HasPrefix(a.rootPath, "/") { if !strings.HasPrefix(a.rootPath, "/") {
err = multierr.Append(err, err = multierr.Append(err,
errors.New("root path must start with /"), errors.New("root path must start with /"),
@@ -478,8 +490,15 @@ func (a *Api) Start() error {
// As the following code is blocking, run it in a goroutine. // As the following code is blocking, run it in a goroutine.
go func() { go func() {
server := &http2.Server{} var err error
err := a.srv.StartH2CServer(fmt.Sprintf(":%d", a.port), server) if a.tlsCertFile != "" && a.tlsKeyFile != "" {
// Start an HTTPS server (supports HTTP/2).
err = a.srv.StartTLS(fmt.Sprintf(":%d", a.port), a.tlsCertFile, a.tlsKeyFile)
} else {
// Start an HTTP/2 Cleartext (non-HTTPS) server.
server := &http2.Server{}
err = a.srv.StartH2CServer(fmt.Sprintf(":%d", a.port), server)
}
if !errors.Is(err, http.ErrServerClosed) { if !errors.Is(err, http.ErrServerClosed) {
a.logger.Fatal(err.Error()) a.logger.Fatal(err.Error())
} }

View File

@@ -462,6 +462,8 @@ func TestApi_Validate(t *testing.T) {
for _, tc := range []struct { for _, tc := range []struct {
scenario string scenario string
port int port int
tlsCertFile string
tlsKeyFile string
rootPath string rootPath string
traceHeader string traceHeader string
routes []Route routes []Route
@@ -486,6 +488,26 @@ func TestApi_Validate(t *testing.T) {
middlewares: nil, middlewares: nil,
expectError: true, expectError: true,
}, },
{
scenario: "invalid TLS files: only cert file provided",
port: 10,
tlsCertFile: "cert.pem",
rootPath: "/foo/",
traceHeader: "foo",
routes: nil,
middlewares: nil,
expectError: true,
},
{
scenario: "invalid TLS files: only key file provided",
port: 10,
tlsKeyFile: "key.pem",
rootPath: "/foo/",
traceHeader: "foo",
routes: nil,
middlewares: nil,
expectError: true,
},
{ {
scenario: "invalid root path: missing / prefix", scenario: "invalid root path: missing / prefix",
port: 10, port: 10,
@@ -647,10 +669,33 @@ func TestApi_Validate(t *testing.T) {
}, },
}, },
}, },
{
scenario: "success with TLS",
port: 10,
tlsCertFile: "cert.pem",
tlsKeyFile: "key.pem",
rootPath: "/foo/",
traceHeader: "foo",
routes: []Route{
{
Method: http.MethodGet,
Path: "/foo",
Handler: func(_ echo.Context) error { return nil },
},
{
Method: http.MethodGet,
Path: "/forms/foo",
Handler: func(_ echo.Context) error { return nil },
IsMultipart: true,
},
},
},
} { } {
t.Run(tc.scenario, func(t *testing.T) { t.Run(tc.scenario, func(t *testing.T) {
mod := Api{ mod := Api{
port: tc.port, port: tc.port,
tlsCertFile: tc.tlsCertFile,
tlsKeyFile: tc.tlsKeyFile,
rootPath: tc.rootPath, rootPath: tc.rootPath,
traceHeader: tc.traceHeader, traceHeader: tc.traceHeader,
routes: tc.routes, routes: tc.routes,
@@ -673,6 +718,8 @@ func TestApi_Start(t *testing.T) {
for _, tc := range []struct { for _, tc := range []struct {
scenario string scenario string
readyFn []func() error readyFn []func() error
tlsCertFile string
tlsKeyFile string
expectError bool expectError bool
}{ }{
{ {
@@ -691,10 +738,22 @@ func TestApi_Start(t *testing.T) {
}, },
expectError: false, expectError: false,
}, },
{
scenario: "success with TLS",
readyFn: []func() error{
func() error { return nil },
func() error { return nil },
},
tlsCertFile: "/tests/test/testdata/api/cert.pem",
tlsKeyFile: "/tests/test/testdata/api/key.pem",
expectError: false,
},
} { } {
t.Run(tc.scenario, func(t *testing.T) { t.Run(tc.scenario, func(t *testing.T) {
mod := new(Api) mod := new(Api)
mod.port = 3000 mod.port = 3000
mod.tlsCertFile = tc.tlsCertFile
mod.tlsKeyFile = tc.tlsKeyFile
mod.startTimeout = time.Duration(30) * time.Second mod.startTimeout = time.Duration(30) * time.Second
mod.rootPath = "/" mod.rootPath = "/"
mod.basicAuthUsername = "foo" mod.basicAuthUsername = "foo"

View File

@@ -227,6 +227,7 @@ func (b *chromiumBrowser) pdf(ctx context.Context, logger *zap.Logger, url, outp
clearCookiesActionFunc(logger, b.arguments.clearCookies), clearCookiesActionFunc(logger, b.arguments.clearCookies),
disableJavaScriptActionFunc(logger, b.arguments.disableJavaScript), disableJavaScriptActionFunc(logger, b.arguments.disableJavaScript),
setCookiesActionFunc(logger, options.Cookies), setCookiesActionFunc(logger, options.Cookies),
userAgentOverride(logger, options.UserAgent),
extraHttpHeadersActionFunc(logger, options.ExtraHttpHeaders), extraHttpHeadersActionFunc(logger, options.ExtraHttpHeaders),
navigateActionFunc(logger, url, options.SkipNetworkIdleEvent), navigateActionFunc(logger, url, options.SkipNetworkIdleEvent),
hideDefaultWhiteBackgroundActionFunc(logger, options.OmitBackground, options.PrintBackground), hideDefaultWhiteBackgroundActionFunc(logger, options.OmitBackground, options.PrintBackground),
@@ -250,6 +251,7 @@ func (b *chromiumBrowser) screenshot(ctx context.Context, logger *zap.Logger, ur
clearCookiesActionFunc(logger, b.arguments.clearCookies), clearCookiesActionFunc(logger, b.arguments.clearCookies),
disableJavaScriptActionFunc(logger, b.arguments.disableJavaScript), disableJavaScriptActionFunc(logger, b.arguments.disableJavaScript),
setCookiesActionFunc(logger, options.Cookies), setCookiesActionFunc(logger, options.Cookies),
userAgentOverride(logger, options.UserAgent),
extraHttpHeadersActionFunc(logger, options.ExtraHttpHeaders), extraHttpHeadersActionFunc(logger, options.ExtraHttpHeaders),
navigateActionFunc(logger, url, options.SkipNetworkIdleEvent), navigateActionFunc(logger, url, options.SkipNetworkIdleEvent),
hideDefaultWhiteBackgroundActionFunc(logger, options.OmitBackground, true), hideDefaultWhiteBackgroundActionFunc(logger, options.OmitBackground, true),

View File

@@ -614,6 +614,41 @@ func TestChromiumBrowser_pdf(t *testing.T) {
"set cookie", "set cookie",
}, },
}, },
{
scenario: "user agent override",
browser: newChromiumBrowser(
browserArguments{
binPath: os.Getenv("CHROMIUM_BIN_PATH"),
wsUrlReadTimeout: 5 * time.Second,
allowList: regexp2.MustCompile("", 0),
denyList: regexp2.MustCompile("", 0),
},
),
fs: func() *gotenberg.FileSystem {
fs := gotenberg.NewFileSystem()
err := os.MkdirAll(fs.WorkingDirPath(), 0o755)
if err != nil {
t.Fatalf(fmt.Sprintf("expected no error but got: %v", err))
}
err = os.WriteFile(fmt.Sprintf("%s/index.html", fs.WorkingDirPath()), []byte("<h1>User-Agent override</h1>"), 0o755)
if err != nil {
t.Fatalf("expected no error but got: %v", err)
}
return fs
}(),
options: PdfOptions{
Options: Options{UserAgent: "foo"},
},
noDeadline: false,
start: true,
expectError: false,
expectedLogEntries: []string{
fmt.Sprintf("user agent override: foo"),
},
},
{ {
scenario: "extra HTTP headers", scenario: "extra HTTP headers",
browser: newChromiumBrowser( browser: newChromiumBrowser(
@@ -1990,6 +2025,7 @@ func TestChromiumBrowser_screenshot(t *testing.T) {
"cache not cleared", "cache not cleared",
"cookies not cleared", "cookies not cleared",
"JavaScript not disabled", "JavaScript not disabled",
"no user agent override",
"no extra HTTP headers", "no extra HTTP headers",
"navigate to", "navigate to",
"default white background not hidden", "default white background not hidden",
@@ -2037,6 +2073,7 @@ func TestChromiumBrowser_screenshot(t *testing.T) {
"cookies not cleared", "cookies not cleared",
"JavaScript not disabled", "JavaScript not disabled",
"no cookies to set", "no cookies to set",
"no user agent override",
"no extra HTTP headers", "no extra HTTP headers",
"navigate to", "navigate to",
"default white background not hidden", "default white background not hidden",
@@ -2084,6 +2121,7 @@ func TestChromiumBrowser_screenshot(t *testing.T) {
"cookies not cleared", "cookies not cleared",
"JavaScript not disabled", "JavaScript not disabled",
"no cookies to set", "no cookies to set",
"no user agent override",
"no extra HTTP headers", "no extra HTTP headers",
"navigate to", "navigate to",
"default white background not hidden", "default white background not hidden",

View File

@@ -108,8 +108,12 @@ type Options struct {
// Optional // Optional
Cookies []Cookie Cookies []Cookie
// ExtraHttpHeaders are the HTTP headers to send by Chromium while loading // UserAgent overrides the default 'User-Agent' HTTP header.
// the HTML document. // Optional.
UserAgent string
// ExtraHttpHeaders are extra HTTP headers to send by Chromium while
// loading he HTML document.
// Optional. // Optional.
ExtraHttpHeaders map[string]string ExtraHttpHeaders map[string]string
@@ -134,6 +138,7 @@ func DefaultOptions() Options {
WaitWindowStatus: "", WaitWindowStatus: "",
WaitForExpression: "", WaitForExpression: "",
Cookies: nil, Cookies: nil,
UserAgent: "",
ExtraHttpHeaders: nil, ExtraHttpHeaders: nil,
EmulatedMediaType: "", EmulatedMediaType: "",
OmitBackground: false, OmitBackground: false,

View File

@@ -35,6 +35,7 @@ func FormDataChromiumOptions(ctx *api.Context) (*api.FormData, Options) {
waitWindowStatus string waitWindowStatus string
waitForExpression string waitForExpression string
cookies []Cookie cookies []Cookie
userAgent string
extraHttpHeaders map[string]string extraHttpHeaders map[string]string
emulatedMediaType string emulatedMediaType string
omitBackground bool omitBackground bool
@@ -78,6 +79,7 @@ func FormDataChromiumOptions(ctx *api.Context) (*api.FormData, Options) {
return err return err
}). }).
String("userAgent", &userAgent, defaultOptions.UserAgent).
Custom("extraHttpHeaders", func(value string) error { Custom("extraHttpHeaders", func(value string) error {
if value == "" { if value == "" {
extraHttpHeaders = defaultOptions.ExtraHttpHeaders extraHttpHeaders = defaultOptions.ExtraHttpHeaders
@@ -115,6 +117,7 @@ func FormDataChromiumOptions(ctx *api.Context) (*api.FormData, Options) {
WaitWindowStatus: waitWindowStatus, WaitWindowStatus: waitWindowStatus,
WaitForExpression: waitForExpression, WaitForExpression: waitForExpression,
Cookies: cookies, Cookies: cookies,
UserAgent: userAgent,
ExtraHttpHeaders: extraHttpHeaders, ExtraHttpHeaders: extraHttpHeaders,
EmulatedMediaType: emulatedMediaType, EmulatedMediaType: emulatedMediaType,
OmitBackground: omitBackground, OmitBackground: omitBackground,

View File

@@ -281,6 +281,23 @@ func setCookiesActionFunc(logger *zap.Logger, cookies []Cookie) chromedp.ActionF
} }
} }
func userAgentOverride(logger *zap.Logger, userAgent string) chromedp.ActionFunc {
return func(ctx context.Context) error {
if len(userAgent) == 0 {
logger.Debug("no user agent override")
return nil
}
logger.Debug(fmt.Sprintf("user agent override: %s", userAgent))
err := emulation.SetUserAgentOverride(userAgent).Do(ctx)
if err == nil {
return nil
}
return fmt.Errorf("set user agent override: %w", err)
}
}
func extraHttpHeadersActionFunc(logger *zap.Logger, extraHttpHeaders map[string]string) chromedp.ActionFunc { func extraHttpHeadersActionFunc(logger *zap.Logger, extraHttpHeaders map[string]string) chromedp.ActionFunc {
return func(ctx context.Context) error { return func(ctx context.Context) error {
if len(extraHttpHeaders) == 0 { if len(extraHttpHeaders) == 0 {

View File

@@ -54,10 +54,21 @@ type Options struct {
// Optional. // Optional.
ExportFormFields bool ExportFormFields bool
// SinglePageSheets allows to output each sheet as a single page in the resulting PDF. // SinglePageSheets allows to output each sheet as a single page in the
// resulting PDF.
// Optional // Optional
SinglePageSheets bool SinglePageSheets bool
// LosslessImageCompression allows turning lossless compression on or off
// to tweak image conversion performance.
// Optional
LosslessImageCompression bool
// ReduceImageResolution allows turning on or off image resolution
// reduction to tweak image conversion performance.
// Optional
ReduceImageResolution bool
// PdfFormats allows to convert the resulting PDF to PDF/A-1b, PDF/A-2b, // PdfFormats allows to convert the resulting PDF to PDF/A-1b, PDF/A-2b,
// PDF/A-3b and PDF/UA. // PDF/A-3b and PDF/UA.
// Optional. // Optional.

View File

@@ -281,6 +281,14 @@ func (p *libreOfficeProcess) pdf(ctx context.Context, logger *zap.Logger, inputP
args = append(args, "--export", "SinglePageSheets=true") args = append(args, "--export", "SinglePageSheets=true")
} }
if options.LosslessImageCompression {
args = append(args, "--export", "UseLosslessCompression=true")
}
if !options.ReduceImageResolution {
args = append(args, "--export", "ReduceImageResolution=false")
}
switch options.PdfFormats.PdfA { switch options.PdfFormats.PdfA {
case "": case "":
case gotenberg.PdfA1b: case gotenberg.PdfA1b:

View File

@@ -451,6 +451,64 @@ func TestLibreOfficeProcess_pdf(t *testing.T) {
start: true, start: true,
expectError: false, expectError: false,
}, },
{
scenario: "success LosslessImageCompression",
libreOffice: newLibreOfficeProcess(
libreOfficeArguments{
binPath: os.Getenv("LIBREOFFICE_BIN_PATH"),
unoBinPath: os.Getenv("UNOCONVERTER_BIN_PATH"),
startTimeout: 5 * time.Second,
},
),
fs: func() *gotenberg.FileSystem {
fs := gotenberg.NewFileSystem()
err := os.MkdirAll(fs.WorkingDirPath(), 0o755)
if err != nil {
t.Fatalf(fmt.Sprintf("expected no error but got: %v", err))
}
err = os.WriteFile(fmt.Sprintf("%s/document.txt", fs.WorkingDirPath()), []byte("LosslessImageCompression"), 0o755)
if err != nil {
t.Fatalf("expected no error but got: %v", err)
}
return fs
}(),
options: Options{LosslessImageCompression: true},
cancelledCtx: false,
start: true,
expectError: false,
},
{
scenario: "success ReduceImageResolution",
libreOffice: newLibreOfficeProcess(
libreOfficeArguments{
binPath: os.Getenv("LIBREOFFICE_BIN_PATH"),
unoBinPath: os.Getenv("UNOCONVERTER_BIN_PATH"),
startTimeout: 5 * time.Second,
},
),
fs: func() *gotenberg.FileSystem {
fs := gotenberg.NewFileSystem()
err := os.MkdirAll(fs.WorkingDirPath(), 0o755)
if err != nil {
t.Fatalf(fmt.Sprintf("expected no error but got: %v", err))
}
err = os.WriteFile(fmt.Sprintf("%s/document.txt", fs.WorkingDirPath()), []byte("ReduceImageResolution"), 0o755)
if err != nil {
t.Fatalf("expected no error but got: %v", err)
}
return fs
}(),
options: Options{ReduceImageResolution: false},
cancelledCtx: false,
start: true,
expectError: false,
},
{ {
scenario: "success (PDF/A-1b)", scenario: "success (PDF/A-1b)",
libreOffice: newLibreOfficeProcess( libreOffice: newLibreOfficeProcess(

View File

@@ -25,16 +25,18 @@ func convertRoute(libreOffice libreofficeapi.Uno, engine gotenberg.PdfEngine) ap
// Let's get the data from the form and validate them. // Let's get the data from the form and validate them.
var ( var (
inputPaths []string inputPaths []string
landscape bool landscape bool
nativePageRanges string nativePageRanges string
exportFormFields bool exportFormFields bool
singlePageSheets bool singlePageSheets bool
pdfa string losslessImageCompression bool
pdfua bool reduceImageResolution bool
nativePdfFormats bool pdfa string
merge bool pdfua bool
metadata map[string]interface{} nativePdfFormats bool
merge bool
metadata map[string]interface{}
) )
err := ctx.FormData(). err := ctx.FormData().
@@ -43,6 +45,8 @@ func convertRoute(libreOffice libreofficeapi.Uno, engine gotenberg.PdfEngine) ap
String("nativePageRanges", &nativePageRanges, ""). String("nativePageRanges", &nativePageRanges, "").
Bool("exportFormFields", &exportFormFields, true). Bool("exportFormFields", &exportFormFields, true).
Bool("singlePageSheets", &singlePageSheets, false). Bool("singlePageSheets", &singlePageSheets, false).
Bool("losslessImageCompression", &losslessImageCompression, false).
Bool("reduceImageResolution", &reduceImageResolution, true).
String("pdfa", &pdfa, ""). String("pdfa", &pdfa, "").
Bool("pdfua", &pdfua, false). Bool("pdfua", &pdfua, false).
Bool("nativePdfFormats", &nativePdfFormats, true). Bool("nativePdfFormats", &nativePdfFormats, true).
@@ -71,10 +75,12 @@ func convertRoute(libreOffice libreofficeapi.Uno, engine gotenberg.PdfEngine) ap
for i, inputPath := range inputPaths { for i, inputPath := range inputPaths {
outputPaths[i] = ctx.GeneratePath(".pdf") outputPaths[i] = ctx.GeneratePath(".pdf")
options := libreofficeapi.Options{ options := libreofficeapi.Options{
Landscape: landscape, Landscape: landscape,
PageRanges: nativePageRanges, PageRanges: nativePageRanges,
ExportFormFields: exportFormFields, ExportFormFields: exportFormFields,
SinglePageSheets: singlePageSheets, SinglePageSheets: singlePageSheets,
LosslessImageCompression: losslessImageCompression,
ReduceImageResolution: reduceImageResolution,
} }
if nativePdfFormats { if nativePdfFormats {

View File

@@ -5,19 +5,19 @@
# Credits: https://github.com/thecodingmachine/docker-images-php. # Credits: https://github.com/thecodingmachine/docker-images-php.
set +e set +e
mkdir testing_file_system_rights.foo mkdir -p testing_file_system_rights.foo
chmod 700 testing_file_system_rights.foo chmod 700 testing_file_system_rights.foo
su gotenberg -c "touch testing_file_system_rights.foo/foo > /dev/null 2>&1" su gotenberg -c "touch testing_file_system_rights.foo/foo > /dev/null 2>&1"
HAS_CONSISTENT_RIGHTS=$? HAS_CONSISTENT_RIGHTS=$?
if [[ "$HAS_CONSISTENT_RIGHTS" != "0" ]]; then if [[ "$HAS_CONSISTENT_RIGHTS" != "0" ]]; then
# If not specified, the DOCKER_USER is the owner of the current working directory (heuristic!). # If not specified, the DOCKER_USER is the owner of the current working directory (heuristic!).
DOCKER_USER=$(stat -c '%U' "$(pwd)") DOCKER_USER=$(stat -c '%u' "$(pwd)")
else else
# macOs or Windows. # macOs or Windows.
# Note: in most cases, we don't care about the rights (they are not respected). # Note: in most cases, we don't care about the rights (they are not respected).
FILE_OWNER=$(stat -c '%U' "testing_file_system_rights.foo/foo") FILE_OWNER=$(stat -c '%u' "testing_file_system_rights.foo/foo")
if [[ "$FILE_OWNER" == "root" ]]; then if [[ "$FILE_OWNER" == "0" ]]; then
# If root, we are likely on a Windows host. # If root, we are likely on a Windows host.
# All files will belong to root, but it does not matter as everybody can write/delete # All files will belong to root, but it does not matter as everybody can write/delete
# those (0777 access rights). # those (0777 access rights).
@@ -56,4 +56,4 @@ go mod tidy
set +x set +x
# Run the command with the correct user. # Run the command with the correct user.
exec "sudo" "-E" "-H" "-u" "#$DOCKER_USER_ID" "$@" exec "sudo" "-E" "-H" "-u" "#$DOCKER_USER_ID" "$@"

14
test/testdata/api/README.md vendored Normal file
View File

@@ -0,0 +1,14 @@
To generate a valid certificate and private key use the following command:
```bash
# In OpenSSL ≥ 1.1.1
openssl req -x509 -newkey rsa:4096 -sha256 -days 9999 -nodes \
-keyout key.pem -out cert.pem -subj "/CN=localhost" \
-addext "subjectAltName=DNS:localhost,IP:127.0.0.1,IP:::1"
```
To check a certificate use the following command:
```bash
openssl x509 -in cert.pem -text
```

30
test/testdata/api/cert.pem vendored Normal file
View File

@@ -0,0 +1,30 @@
-----BEGIN CERTIFICATE-----
MIIFOjCCAyKgAwIBAgIUZaht2QoZwWDK8wEuwm+wmip9sqwwDQYJKoZIhvcNAQEL
BQAwFDESMBAGA1UEAwwJbG9jYWxob3N0MCAXDTI0MDUyNjA1MDExNFoYDzIwNTEx
MDExMDUwMTE0WjAUMRIwEAYDVQQDDAlsb2NhbGhvc3QwggIiMA0GCSqGSIb3DQEB
AQUAA4ICDwAwggIKAoICAQCm3TNckD55ae6pxXS03iV2P3X/edZT4vRa8D15elsi
CSRf6mdU2TVAQyg22GEcMXgsbba7sotJVg7559LkWAgb3Dj9rbPputdS29ebncat
x1nzo1QHMCS/v7nWoPe9fQVlDEf3ebM198f+5vhccSYFxFa+YvsUmSw9yMF8z1nA
6POZTpm6ErByAY9EGvC8EcNOQd9xLOfBZc0e+mY/rMRxmL+0N+sSRE/ACU+2zxHN
KcCiFX5WQdLDYW1ZjX5xAVatCPNlxSQpj97/ktGsnqwGnJWSTeMUOPv7L0LrQasf
k1VXBvfTW2/ZqhI9WA8qfdC5XOuzVwfU14gv+6vv9AGZnUFmOjeo9XG4xWBxlIKj
AQAzeNwAGFltPkp5a3yPczxMjKY4xLsMGcDfpaNtv1B24+7fV77Nyty/gLLT9pUa
FtXLuRfIPSaUK3kFhAuYzGUL/sO9O9jJkNlxxygxQ+VyIgmdw0zWrUi3q+Lfmdya
FFjHdM7LnLrLCjSv3dEoMINngR3+IejsY+98Ehd25O3oW+ZnGIQX0VwykV8sOQWq
rpnnZ+2iHMtF6Vsj40z/Y9y7yVE18AIJFC+Yr/dD/nQyg6SE70wdNcOx0Ay1xHbP
WuV3fqKOfhONR7NEBSO2UbHi6GZAiYMMwwul+4yKAQ4+lHOEXG4JQcKvtZasxq9v
GwIDAQABo4GBMH8wHQYDVR0OBBYEFPPMaH3C0rAkD/E0gtBZwM0x8N9MMB8GA1Ud
IwQYMBaAFPPMaH3C0rAkD/E0gtBZwM0x8N9MMA8GA1UdEwEB/wQFMAMBAf8wLAYD
VR0RBCUwI4IJbG9jYWxob3N0hwR/AAABhxAAAAAAAAAAAAAAAAAAAAABMA0GCSqG
SIb3DQEBCwUAA4ICAQB0DXz59JfQgfJvRJfraa+Cv/3nG4NNmHYtNGhrid5sagH3
LM8jkbA+JKRAn34SmsqUZb1f2XFI64TwrScgMfAvfi+5ibRqBjM/YL06bTlG61Ua
t2IQchV2kcX4zQnaMCIYG0dUj6JoXIspiFULMHyGDs7G9BKVcZxlaLrEvPXBqQjd
nwF80KHS/rCConmICU9uWHPgHxS1VmhQiwYGbrakETilau1ppRS0bpH2k/stVyjc
plttIr1DUD6xaRV4mZ0NZVlIXl2nDf78iQvreJeMrGl6AcNHzV+/rGHpY/gOrziP
PQkB1VtSiHtBe/VMKXcVB69SEzsoGEknqs4GPXuVYm47POo3/U5HmGU+FadSATNP
qtorRECUMbVn0IfDzJX2jBLUjZlhPI9gqO9DhiAT8FWHRV2yPsZKuiBBg5/hUvyb
Qsswtmi/YV5e6AnXEuvylwE3wYzlqDzxwlZM08bWFyZYnSf38d3hAjVwUHAugiaX
4sgE6GPAiONW5FkTZ4Z2oXYiN213QDIIcQeFAl04qSEFkGBV8x2hM0uXgFH1S++j
JePdKBbV/CjxegyyYsmnln+eO1kC8pCalB6k6pEtYMqNYOnmimC8UKrJATrH9EVq
eAehGBiIe514Oz06Ws2Vm4PTiY2x0KVbqphwkuz85ohSF9UXOHUcMryusr1zlg==
-----END CERTIFICATE-----

52
test/testdata/api/key.pem vendored Normal file
View File

@@ -0,0 +1,52 @@
-----BEGIN PRIVATE KEY-----
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
lrSOZMPTLX1tN/cWa+Ii+/WALnkI
-----END PRIVATE KEY-----