mirror of
https://github.com/gotenberg/gotenberg.git
synced 2026-08-12 18:32:14 +01:00
Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a780373cfc | ||
|
|
5ad90ccdb8 | ||
|
|
ef97d81ca8 | ||
|
|
c9f7b642c5 | ||
|
|
770962be3f | ||
|
|
d6c857f4a8 | ||
|
|
7b7ffb427d | ||
|
|
b80318d1b6 | ||
|
|
3997d69e49 |
4
.github/workflows/continuous_integration.yml
vendored
4
.github/workflows/continuous_integration.yml
vendored
@@ -17,14 +17,14 @@ jobs:
|
|||||||
- name: Setup Go
|
- name: Setup Go
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: '1.21'
|
go-version: '1.22'
|
||||||
cache: false
|
cache: false
|
||||||
- name: Checkout source code
|
- name: Checkout source code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
- name: Run linters
|
- name: Run linters
|
||||||
uses: golangci/golangci-lint-action@v4
|
uses: golangci/golangci-lint-action@v4
|
||||||
with:
|
with:
|
||||||
version: v1.55.2
|
version: v1.56.2
|
||||||
|
|
||||||
tests:
|
tests:
|
||||||
needs:
|
needs:
|
||||||
|
|||||||
4
Makefile
4
Makefile
@@ -5,14 +5,14 @@ help: ## Show the help
|
|||||||
.PHONY: it
|
.PHONY: it
|
||||||
it: build build-tests ## Initialize the development environment
|
it: build build-tests ## Initialize the development environment
|
||||||
|
|
||||||
GOLANG_VERSION=1.21
|
GOLANG_VERSION=1.22
|
||||||
DOCKER_REPOSITORY=gotenberg
|
DOCKER_REPOSITORY=gotenberg
|
||||||
GOTENBERG_VERSION=snapshot
|
GOTENBERG_VERSION=snapshot
|
||||||
GOTENBERG_USER_GID=1001
|
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.55.2 # See https://github.com/golangci/golangci-lint/releases.
|
GOLANGCI_LINT_VERSION=v1.56.2 # 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
|
||||||
|
|||||||
20
go.mod
20
go.mod
@@ -1,11 +1,11 @@
|
|||||||
module github.com/gotenberg/gotenberg/v8
|
module github.com/gotenberg/gotenberg/v8
|
||||||
|
|
||||||
go 1.21
|
go 1.22
|
||||||
|
|
||||||
require (
|
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/chromedp/cdproto v0.0.0-20240226204813-532e667d868f
|
github.com/chromedp/cdproto v0.0.0-20240312231614-1e5096e63154
|
||||||
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
|
||||||
@@ -19,7 +19,7 @@ require (
|
|||||||
github.com/mholt/archiver/v3 v3.5.1
|
github.com/mholt/archiver/v3 v3.5.1
|
||||||
github.com/microcosm-cc/bluemonday v1.0.26
|
github.com/microcosm-cc/bluemonday v1.0.26
|
||||||
github.com/nwaples/rardecode v1.1.3 // indirect
|
github.com/nwaples/rardecode v1.1.3 // indirect
|
||||||
github.com/pdfcpu/pdfcpu v0.6.0
|
github.com/pdfcpu/pdfcpu v0.7.0
|
||||||
github.com/pierrec/lz4/v4 v4.1.21 // indirect
|
github.com/pierrec/lz4/v4 v4.1.21 // indirect
|
||||||
github.com/prometheus/client_golang v1.19.0
|
github.com/prometheus/client_golang v1.19.0
|
||||||
github.com/russross/blackfriday/v2 v2.1.0
|
github.com/russross/blackfriday/v2 v2.1.0
|
||||||
@@ -27,12 +27,12 @@ require (
|
|||||||
github.com/ulikunitz/xz v0.5.11 // indirect
|
github.com/ulikunitz/xz v0.5.11 // 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.20.0 // indirect
|
golang.org/x/crypto v0.21.0 // indirect
|
||||||
golang.org/x/image v0.15.0 // indirect
|
golang.org/x/image v0.15.0 // indirect
|
||||||
golang.org/x/net v0.21.0
|
golang.org/x/net v0.22.0
|
||||||
golang.org/x/sync v0.6.0
|
golang.org/x/sync v0.6.0
|
||||||
golang.org/x/sys v0.17.0 // indirect
|
golang.org/x/sys v0.18.0 // indirect
|
||||||
golang.org/x/term v0.17.0
|
golang.org/x/term v0.18.0
|
||||||
golang.org/x/text v0.14.0
|
golang.org/x/text v0.14.0
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -56,12 +56,12 @@ 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.0 // indirect
|
github.com/prometheus/client_model v0.6.0 // indirect
|
||||||
github.com/prometheus/common v0.49.0 // indirect
|
github.com/prometheus/common v0.50.0 // indirect
|
||||||
github.com/prometheus/procfs v0.12.0 // indirect
|
github.com/prometheus/procfs v0.13.0 // 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
|
||||||
google.golang.org/protobuf v1.32.0 // indirect
|
google.golang.org/protobuf v1.33.0 // indirect
|
||||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
36
go.sum
36
go.sum
@@ -10,8 +10,8 @@ github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6r
|
|||||||
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
|
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
|
||||||
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
github.com/cespare/xxhash/v2 v2.2.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-20240226204813-532e667d868f h1:jODunjTDQHm0Srs2IsfcS3hOmNLUN7Spag3NJZQra2g=
|
github.com/chromedp/cdproto v0.0.0-20240312231614-1e5096e63154 h1:jeAmkzyOAQBPRmZMhX+i/CJv0VViLkHk1nF0qx8s0Mk=
|
||||||
github.com/chromedp/cdproto v0.0.0-20240226204813-532e667d868f/go.mod h1:GKljq0VrfU4D5yc+2qA6OVr8pmO/MBbPEWqWQ/oqGEs=
|
github.com/chromedp/cdproto v0.0.0-20240312231614-1e5096e63154/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=
|
||||||
@@ -87,8 +87,8 @@ github.com/nwaples/rardecode v1.1.3 h1:cWCaZwfM5H7nAD6PyEdcVnczzV8i/JtotnyW/dD9l
|
|||||||
github.com/nwaples/rardecode v1.1.3/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0=
|
github.com/nwaples/rardecode v1.1.3/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0=
|
||||||
github.com/orisano/pixelmatch v0.0.0-20220722002657-fb0b55479cde h1:x0TT0RDC7UhAVbbWWBzr41ElhJx5tXPWkIHA2HWPRuw=
|
github.com/orisano/pixelmatch v0.0.0-20220722002657-fb0b55479cde h1:x0TT0RDC7UhAVbbWWBzr41ElhJx5tXPWkIHA2HWPRuw=
|
||||||
github.com/orisano/pixelmatch v0.0.0-20220722002657-fb0b55479cde/go.mod h1:nZgzbfBr3hhjoZnS66nKrHmduYNpc34ny7RK4z5/HM0=
|
github.com/orisano/pixelmatch v0.0.0-20220722002657-fb0b55479cde/go.mod h1:nZgzbfBr3hhjoZnS66nKrHmduYNpc34ny7RK4z5/HM0=
|
||||||
github.com/pdfcpu/pdfcpu v0.6.0 h1:z4kARP5bcWa39TTYMcN/kjBnm7MvhTWjXgeYmkdAGMI=
|
github.com/pdfcpu/pdfcpu v0.7.0 h1:cd7/z7hAyyDuzdciKfNZyQ3TYreJza2DsuPdIHYURcA=
|
||||||
github.com/pdfcpu/pdfcpu v0.6.0/go.mod h1:kmpD0rk8YnZj0l3qSeGBlAB+XszHUgNv//ORH/E7EYo=
|
github.com/pdfcpu/pdfcpu v0.7.0/go.mod h1:kmpD0rk8YnZj0l3qSeGBlAB+XszHUgNv//ORH/E7EYo=
|
||||||
github.com/pierrec/lz4/v4 v4.1.2/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
|
github.com/pierrec/lz4/v4 v4.1.2/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
|
||||||
github.com/pierrec/lz4/v4 v4.1.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ=
|
github.com/pierrec/lz4/v4 v4.1.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ=
|
||||||
github.com/pierrec/lz4/v4 v4.1.21/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
|
github.com/pierrec/lz4/v4 v4.1.21/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
|
||||||
@@ -100,10 +100,10 @@ github.com/prometheus/client_golang v1.19.0 h1:ygXvpU1AoN1MhdzckN+PyD9QJOSD4x7km
|
|||||||
github.com/prometheus/client_golang v1.19.0/go.mod h1:ZRM9uEAypZakd+q/x7+gmsvXdURP+DABIEIjnmDdp+k=
|
github.com/prometheus/client_golang v1.19.0/go.mod h1:ZRM9uEAypZakd+q/x7+gmsvXdURP+DABIEIjnmDdp+k=
|
||||||
github.com/prometheus/client_model v0.6.0 h1:k1v3CzpSRUTrKMppY35TLwPvxHqBu0bYgxZzqGIgaos=
|
github.com/prometheus/client_model v0.6.0 h1:k1v3CzpSRUTrKMppY35TLwPvxHqBu0bYgxZzqGIgaos=
|
||||||
github.com/prometheus/client_model v0.6.0/go.mod h1:NTQHnmxFpouOD0DpvP4XujX3CdOAGQPoaGhyTchlyt8=
|
github.com/prometheus/client_model v0.6.0/go.mod h1:NTQHnmxFpouOD0DpvP4XujX3CdOAGQPoaGhyTchlyt8=
|
||||||
github.com/prometheus/common v0.49.0 h1:ToNTdK4zSnPVJmh698mGFkDor9wBI/iGaJy5dbH1EgI=
|
github.com/prometheus/common v0.50.0 h1:YSZE6aa9+luNa2da6/Tik0q0A5AbR+U003TItK57CPQ=
|
||||||
github.com/prometheus/common v0.49.0/go.mod h1:Kxm+EULxRbUkjGU6WFsQqo3ORzB4tyKvlWFOE9mB2sE=
|
github.com/prometheus/common v0.50.0/go.mod h1:wHFBCEVWVmHMUpg7pYcOm2QUR/ocQdYSJVQJKnHc3xQ=
|
||||||
github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo=
|
github.com/prometheus/procfs v0.13.0 h1:GqzLlQyfsPbaEHaQkO7tbDlriv/4o5Hudv6OXHGKX7o=
|
||||||
github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo=
|
github.com/prometheus/procfs v0.13.0/go.mod h1:cd4PFCR54QLnGKPaKGA6l+cfuNXtht43ZKY6tow0Y1g=
|
||||||
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=
|
||||||
@@ -134,26 +134,26 @@ 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.20.0 h1:jmAMJJZXr5KiCw05dfYK9QnqaqKLYXijU23lsEdcQqg=
|
golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA=
|
||||||
golang.org/x/crypto v0.20.0/go.mod h1:Xwo95rrVNIoSMx9wa1JroENMToLWn3RNVrTBpLHgZPQ=
|
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
|
||||||
golang.org/x/image v0.15.0 h1:kOELfmgrmJlw4Cdb7g/QGuB3CvDrXbqEIww/pNtNBm8=
|
golang.org/x/image v0.15.0 h1:kOELfmgrmJlw4Cdb7g/QGuB3CvDrXbqEIww/pNtNBm8=
|
||||||
golang.org/x/image v0.15.0/go.mod h1:HUYqC05R2ZcZ3ejNQsIHQDQiwWM4JBqmm6MKANTp4LE=
|
golang.org/x/image v0.15.0/go.mod h1:HUYqC05R2ZcZ3ejNQsIHQDQiwWM4JBqmm6MKANTp4LE=
|
||||||
golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4=
|
golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc=
|
||||||
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
|
||||||
golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
|
golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
|
||||||
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
golang.org/x/sync v0.6.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.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y=
|
golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
|
||||||
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/term v0.17.0 h1:mkTF7LCd6WGJNL3K1Ad7kwxNfYAW6a8a8QqtMblp/4U=
|
golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8=
|
||||||
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
|
golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58=
|
||||||
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||||
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.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I=
|
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
|
||||||
google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||||
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=
|
||||||
|
|||||||
@@ -51,3 +51,11 @@ func (fs *FileSystem) MkdirAll() (string, error) {
|
|||||||
|
|
||||||
return path, nil
|
return path, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PathRename defines the method signature for renaming files. Implement this
|
||||||
|
// interface if you don't want to rely on [os.Rename], notably for testing
|
||||||
|
// purpose.
|
||||||
|
type PathRename interface {
|
||||||
|
// Rename uses the same signature as [os.Rename].
|
||||||
|
Rename(oldpath, newpath string) error
|
||||||
|
}
|
||||||
|
|||||||
@@ -127,6 +127,15 @@ func (provider *MetricsProviderMock) Metrics() ([]Metric, error) {
|
|||||||
return provider.MetricsMock()
|
return provider.MetricsMock()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PathRenameMock is a mock for the [PathRename] interface.
|
||||||
|
type PathRenameMock struct {
|
||||||
|
RenameMock func(oldpath, newpath string) error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rename *PathRenameMock) Rename(oldpath, newpath string) error {
|
||||||
|
return rename.RenameMock(oldpath, newpath)
|
||||||
|
}
|
||||||
|
|
||||||
// Interface guards.
|
// Interface guards.
|
||||||
var (
|
var (
|
||||||
_ Module = (*ModuleMock)(nil)
|
_ Module = (*ModuleMock)(nil)
|
||||||
|
|||||||
@@ -188,3 +188,16 @@ func TestMetricsProviderMock(t *testing.T) {
|
|||||||
t.Errorf("expected no error from MetricsProviderMock.Metrics, but got: %v", err)
|
t.Errorf("expected no error from MetricsProviderMock.Metrics, but got: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestPathRenameMock(t *testing.T) {
|
||||||
|
mock := &PathRenameMock{
|
||||||
|
RenameMock: func(oldpath, newpath string) error {
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
err := mock.Rename("", "")
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("expected no error from PathRenameMock.Rename, but got: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -34,18 +34,24 @@ var (
|
|||||||
|
|
||||||
// Context is the request context for a "multipart/form-data" requests.
|
// Context is the request context for a "multipart/form-data" requests.
|
||||||
type Context struct {
|
type Context struct {
|
||||||
dirPath string
|
dirPath string
|
||||||
values map[string][]string
|
values map[string][]string
|
||||||
files map[string]string
|
files map[string]string
|
||||||
|
|
||||||
outputPaths []string
|
outputPaths []string
|
||||||
|
cancelled bool
|
||||||
|
|
||||||
cancelled bool
|
logger *zap.Logger
|
||||||
logger *zap.Logger
|
echoCtx echo.Context
|
||||||
echoCtx echo.Context
|
pathRename gotenberg.PathRename
|
||||||
context.Context
|
context.Context
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type osPathRename struct{}
|
||||||
|
|
||||||
|
func (o *osPathRename) Rename(oldpath, newpath string) error {
|
||||||
|
return os.Rename(oldpath, newpath)
|
||||||
|
}
|
||||||
|
|
||||||
// newContext returns a [Context] by parsing a "multipart/form-data" request.
|
// newContext returns a [Context] by parsing a "multipart/form-data" request.
|
||||||
func newContext(echoCtx echo.Context, logger *zap.Logger, fs *gotenberg.FileSystem, timeout time.Duration) (*Context, context.CancelFunc, error) {
|
func newContext(echoCtx echo.Context, logger *zap.Logger, fs *gotenberg.FileSystem, timeout time.Duration) (*Context, context.CancelFunc, error) {
|
||||||
processCtx, processCancel := context.WithTimeout(context.Background(), timeout)
|
processCtx, processCancel := context.WithTimeout(context.Background(), timeout)
|
||||||
@@ -55,6 +61,7 @@ func newContext(echoCtx echo.Context, logger *zap.Logger, fs *gotenberg.FileSyst
|
|||||||
cancelled: false,
|
cancelled: false,
|
||||||
logger: logger,
|
logger: logger,
|
||||||
echoCtx: echoCtx,
|
echoCtx: echoCtx,
|
||||||
|
pathRename: new(osPathRename),
|
||||||
Context: processCtx,
|
Context: processCtx,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -191,14 +198,19 @@ func (ctx *Context) FormData() *FormData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GeneratePath generates a path within the context's working directory.
|
// GeneratePath generates a path within the context's working directory.
|
||||||
// It either generates a new UUID-based filename or uses the provided filename.
|
// It generates a new UUID-based filename. It does not create a file.
|
||||||
// It does not create a file.
|
func (ctx *Context) GeneratePath(extension string) string {
|
||||||
func (ctx *Context) GeneratePath(filename, extension string) string {
|
return fmt.Sprintf("%s/%s%s", ctx.dirPath, uuid.New().String(), extension)
|
||||||
if filename == "" {
|
}
|
||||||
// Generate a new UUID-based filename
|
|
||||||
filename = uuid.New().String()
|
// Rename is just a wrapper around [os.Rename], as we need to mock this
|
||||||
|
// behavior in our tests.
|
||||||
|
func (ctx *Context) Rename(oldpath, newpath string) error {
|
||||||
|
err := ctx.pathRename.Rename(oldpath, newpath)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("rename path: %w", err)
|
||||||
}
|
}
|
||||||
return fmt.Sprintf("%s/%s%s", ctx.dirPath, filename, extension)
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddOutputPaths adds the given paths. Those paths will be used later to build
|
// AddOutputPaths adds the given paths. Those paths will be used later to build
|
||||||
@@ -250,7 +262,7 @@ func (ctx *Context) BuildOutputFile() (string, error) {
|
|||||||
ImplicitTopLevelFolder: false,
|
ImplicitTopLevelFolder: false,
|
||||||
}
|
}
|
||||||
|
|
||||||
archivePath := ctx.GeneratePath("", ".zip")
|
archivePath := ctx.GeneratePath(".zip")
|
||||||
|
|
||||||
err := z.Archive(ctx.outputPaths, archivePath)
|
err := z.Archive(ctx.outputPaths, archivePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -273,3 +285,8 @@ func (ctx *Context) OutputFilename(outputPath string) string {
|
|||||||
|
|
||||||
return fmt.Sprintf("%s%s", filename, filepath.Ext(outputPath))
|
return fmt.Sprintf("%s%s", filename, filepath.Ext(outputPath))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Interface guard.
|
||||||
|
var (
|
||||||
|
_ gotenberg.PathRename = (*osPathRename)(nil)
|
||||||
|
)
|
||||||
|
|||||||
@@ -3,21 +3,77 @@ package api
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
"mime/multipart"
|
"mime/multipart"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
"github.com/labstack/echo/v4"
|
"github.com/labstack/echo/v4"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
|
|
||||||
"github.com/gotenberg/gotenberg/v8/pkg/gotenberg"
|
"github.com/gotenberg/gotenberg/v8/pkg/gotenberg"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func TestOsPathRename_Rename(t *testing.T) {
|
||||||
|
dirPath, err := gotenberg.NewFileSystem().MkdirAll()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("create working directory: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
path := "/tests/test/testdata/api/sample1.txt"
|
||||||
|
copyPath := filepath.Join(dirPath, fmt.Sprintf("%s.txt", uuid.NewString()))
|
||||||
|
|
||||||
|
in, err := os.Open(path)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("open file: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
err := in.Close()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("close file: %v", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
out, err := os.Create(copyPath)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("create new file: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
err := out.Close()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("close new file: %v", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
_, err = io.Copy(out, in)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("copy file to new file: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
rename := new(osPathRename)
|
||||||
|
newPath := filepath.Join(dirPath, fmt.Sprintf("%s.txt", uuid.NewString()))
|
||||||
|
|
||||||
|
err = rename.Rename(copyPath, newPath)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("expected no error but got: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = os.RemoveAll(dirPath)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("remove working directory: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestNewContext(t *testing.T) {
|
func TestNewContext(t *testing.T) {
|
||||||
for _, tc := range []struct {
|
for _, tc := range []struct {
|
||||||
scenario string
|
scenario string
|
||||||
@@ -190,14 +246,44 @@ func TestContext_GeneratePath(t *testing.T) {
|
|||||||
dirPath: "/foo",
|
dirPath: "/foo",
|
||||||
}
|
}
|
||||||
|
|
||||||
path := ctx.GeneratePath("", ".pdf")
|
path := ctx.GeneratePath(".pdf")
|
||||||
if !strings.HasPrefix(path, ctx.dirPath) {
|
if !strings.HasPrefix(path, ctx.dirPath) {
|
||||||
t.Errorf("expected '%s' to start with '%s'", path, ctx.dirPath)
|
t.Errorf("expected '%s' to start with '%s'", path, ctx.dirPath)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
path = ctx.GeneratePath("foo.txt", ".pdf")
|
func TestContext_Rename(t *testing.T) {
|
||||||
if !strings.Contains(path, "foo.txt.pdf") {
|
for _, tc := range []struct {
|
||||||
t.Errorf("expected '%s' to start with '%s'", path, ctx.dirPath)
|
scenario string
|
||||||
|
ctx *Context
|
||||||
|
expectError bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
scenario: "failure",
|
||||||
|
ctx: &Context{pathRename: &gotenberg.PathRenameMock{RenameMock: func(oldpath, newpath string) error {
|
||||||
|
return errors.New("cannot rename")
|
||||||
|
}}},
|
||||||
|
expectError: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
scenario: "success",
|
||||||
|
ctx: &Context{pathRename: &gotenberg.PathRenameMock{RenameMock: func(oldpath, newpath string) error {
|
||||||
|
return nil
|
||||||
|
}}},
|
||||||
|
expectError: false,
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
t.Run(tc.scenario, func(t *testing.T) {
|
||||||
|
err := tc.ctx.Rename("", "")
|
||||||
|
|
||||||
|
if tc.expectError && err == nil {
|
||||||
|
t.Fatal("expected error but got none", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !tc.expectError && err != nil {
|
||||||
|
t.Fatalf("expected no error but got: %v", err)
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ func TestParseError(t *testing.T) {
|
|||||||
{
|
{
|
||||||
err: gotenberg.ErrPdfFormatNotSupported,
|
err: gotenberg.ErrPdfFormatNotSupported,
|
||||||
expectStatus: http.StatusBadRequest,
|
expectStatus: http.StatusBadRequest,
|
||||||
expectMessage: "A least one PDF engine does not handle one of the requested PDF format, while other have failed to convert for other reasons",
|
expectMessage: "At least one PDF engine cannot process the requested PDF format, while others may have failed to convert due to different issues",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
err: WrapError(
|
err: WrapError(
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ import (
|
|||||||
"github.com/alexliesenfeld/health"
|
"github.com/alexliesenfeld/health"
|
||||||
"github.com/labstack/echo/v4"
|
"github.com/labstack/echo/v4"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
|
|
||||||
|
"github.com/gotenberg/gotenberg/v8/pkg/gotenberg"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ContextMock is a helper for tests.
|
// ContextMock is a helper for tests.
|
||||||
@@ -76,7 +78,7 @@ func (ctx *ContextMock) SetLogger(logger *zap.Logger) {
|
|||||||
ctx.logger = logger
|
ctx.logger = logger
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetEchoContext sets the echo.Context.
|
// SetEchoContext sets the [echo.Context].
|
||||||
//
|
//
|
||||||
// ctx := &api.ContextMock{Context: &api.Context{}}
|
// ctx := &api.ContextMock{Context: &api.Context{}}
|
||||||
// ctx.setEchoContext(c)
|
// ctx.setEchoContext(c)
|
||||||
@@ -84,6 +86,14 @@ func (ctx *ContextMock) SetEchoContext(c echo.Context) {
|
|||||||
ctx.Context.echoCtx = c
|
ctx.Context.echoCtx = c
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetPathRename sets the [gotenberg.PathRename].
|
||||||
|
//
|
||||||
|
// ctx := &api.ContextMock{Context: &api.Context{}}
|
||||||
|
// ctx.setPathRename(rename)
|
||||||
|
func (ctx *ContextMock) SetPathRename(rename gotenberg.PathRename) {
|
||||||
|
ctx.Context.pathRename = rename
|
||||||
|
}
|
||||||
|
|
||||||
// RouterMock is a mock for the [Router] interface.
|
// RouterMock is a mock for the [Router] interface.
|
||||||
type RouterMock struct {
|
type RouterMock struct {
|
||||||
RoutesMock func() ([]Route, error)
|
RoutesMock func() ([]Route, error)
|
||||||
|
|||||||
@@ -117,6 +117,19 @@ func TestContextMock_SetEchoContext(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestContextMock_SetPathRename(t *testing.T) {
|
||||||
|
mock := ContextMock{&Context{}}
|
||||||
|
|
||||||
|
expect := new(osPathRename)
|
||||||
|
mock.SetPathRename(expect)
|
||||||
|
|
||||||
|
actual := mock.pathRename
|
||||||
|
|
||||||
|
if actual != expect {
|
||||||
|
t.Errorf("expected %v but got %v", expect, actual)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestRouterMock(t *testing.T) {
|
func TestRouterMock(t *testing.T) {
|
||||||
mock := &RouterMock{
|
mock := &RouterMock{
|
||||||
RoutesMock: func() ([]Route, error) {
|
RoutesMock: func() ([]Route, error) {
|
||||||
|
|||||||
@@ -90,6 +90,8 @@ func (b *chromiumBrowser) Start(logger *zap.Logger) error {
|
|||||||
// https://github.com/puppeteer/puppeteer/issues/2410
|
// https://github.com/puppeteer/puppeteer/issues/2410
|
||||||
chromedp.Flag("font-render-hinting", "none"),
|
chromedp.Flag("font-render-hinting", "none"),
|
||||||
chromedp.UserDataDir(b.userProfileDirPath),
|
chromedp.UserDataDir(b.userProfileDirPath),
|
||||||
|
// See https://github.com/gotenberg/gotenberg/issues/831.
|
||||||
|
chromedp.Flag("disable-pdf-tagging", true),
|
||||||
)
|
)
|
||||||
|
|
||||||
if b.arguments.incognito {
|
if b.arguments.incognito {
|
||||||
@@ -164,7 +166,7 @@ func (b *chromiumBrowser) Stop(logger *zap.Logger) error {
|
|||||||
go func() {
|
go func() {
|
||||||
// FIXME: Chromium seems to recreate the user profile directory
|
// FIXME: Chromium seems to recreate the user profile directory
|
||||||
// right after its deletion if we do not wait a certain amount
|
// right after its deletion if we do not wait a certain amount
|
||||||
// of time before re-deleting it.
|
// of time before deleting it.
|
||||||
<-time.After(10 * time.Second)
|
<-time.After(10 * time.Second)
|
||||||
|
|
||||||
err := os.RemoveAll(userProfileDirPath)
|
err := os.RemoveAll(userProfileDirPath)
|
||||||
|
|||||||
@@ -496,7 +496,7 @@ func markdownToHtml(ctx *api.Context, inputPath string, markdownPaths []string)
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
inputPath = ctx.GeneratePath("", ".html")
|
inputPath = ctx.GeneratePath(".html")
|
||||||
|
|
||||||
err = os.WriteFile(inputPath, buffer.Bytes(), 0o600)
|
err = os.WriteFile(inputPath, buffer.Bytes(), 0o600)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -507,7 +507,7 @@ func markdownToHtml(ctx *api.Context, inputPath string, markdownPaths []string)
|
|||||||
}
|
}
|
||||||
|
|
||||||
func convertUrl(ctx *api.Context, chromium Api, engine gotenberg.PdfEngine, url string, pdfFormats gotenberg.PdfFormats, options PdfOptions) error {
|
func convertUrl(ctx *api.Context, chromium Api, engine gotenberg.PdfEngine, url string, pdfFormats gotenberg.PdfFormats, options PdfOptions) error {
|
||||||
outputPath := ctx.GeneratePath("", ".pdf")
|
outputPath := ctx.GeneratePath(".pdf")
|
||||||
|
|
||||||
err := chromium.Pdf(ctx, ctx.Log(), url, outputPath, options)
|
err := chromium.Pdf(ctx, ctx.Log(), url, outputPath, options)
|
||||||
err = handleChromiumError(err, options.Options)
|
err = handleChromiumError(err, options.Options)
|
||||||
@@ -551,7 +551,7 @@ func convertUrl(ctx *api.Context, chromium Api, engine gotenberg.PdfEngine, url
|
|||||||
zeroValued := gotenberg.PdfFormats{}
|
zeroValued := gotenberg.PdfFormats{}
|
||||||
if pdfFormats != zeroValued {
|
if pdfFormats != zeroValued {
|
||||||
convertInputPath := outputPath
|
convertInputPath := outputPath
|
||||||
convertOutputPath := ctx.GeneratePath("", ".pdf")
|
convertOutputPath := ctx.GeneratePath(".pdf")
|
||||||
|
|
||||||
err = engine.Convert(ctx, ctx.Log(), pdfFormats, convertInputPath, convertOutputPath)
|
err = engine.Convert(ctx, ctx.Log(), pdfFormats, convertInputPath, convertOutputPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -572,7 +572,7 @@ func convertUrl(ctx *api.Context, chromium Api, engine gotenberg.PdfEngine, url
|
|||||||
|
|
||||||
func screenshotUrl(ctx *api.Context, chromium Api, url string, options ScreenshotOptions) error {
|
func screenshotUrl(ctx *api.Context, chromium Api, url string, options ScreenshotOptions) error {
|
||||||
ext := fmt.Sprintf(".%s", options.Format)
|
ext := fmt.Sprintf(".%s", options.Format)
|
||||||
outputPath := ctx.GeneratePath("", ext)
|
outputPath := ctx.GeneratePath(ext)
|
||||||
|
|
||||||
err := chromium.Screenshot(ctx, ctx.Log(), url, outputPath, options)
|
err := chromium.Screenshot(ctx, ctx.Log(), url, outputPath, options)
|
||||||
err = handleChromiumError(err, options.Options)
|
err = handleChromiumError(err, options.Options)
|
||||||
|
|||||||
@@ -47,6 +47,8 @@ func printToPdfActionFunc(logger *zap.Logger, outputPath string, options PdfOpti
|
|||||||
WithMarginRight(options.MarginRight).
|
WithMarginRight(options.MarginRight).
|
||||||
WithPageRanges(pageRanges).
|
WithPageRanges(pageRanges).
|
||||||
WithPreferCSSPageSize(options.PreferCssPageSize).
|
WithPreferCSSPageSize(options.PreferCssPageSize).
|
||||||
|
// Does not seem to work.
|
||||||
|
// See https://github.com/gotenberg/gotenberg/issues/831.
|
||||||
WithGenerateTaggedPDF(false)
|
WithGenerateTaggedPDF(false)
|
||||||
|
|
||||||
hasCustomHeaderFooter := options.HeaderTemplate != DefaultPdfOptions().HeaderTemplate ||
|
hasCustomHeaderFooter := options.HeaderTemplate != DefaultPdfOptions().HeaderTemplate ||
|
||||||
|
|||||||
@@ -450,7 +450,7 @@ func TestApi_Extensions(t *testing.T) {
|
|||||||
extensions := a.Extensions()
|
extensions := a.Extensions()
|
||||||
|
|
||||||
actual := len(extensions)
|
actual := len(extensions)
|
||||||
expect := 80
|
expect := 81
|
||||||
|
|
||||||
if actual != expect {
|
if actual != expect {
|
||||||
t.Errorf("expected %d extensions, but got %d", expect, actual)
|
t.Errorf("expected %d extensions, but got %d", expect, actual)
|
||||||
|
|||||||
@@ -291,6 +291,12 @@ func (p *libreOfficeProcess) pdf(ctx context.Context, logger *zap.Logger, inputP
|
|||||||
"--export", "UseTaggedPDF=true",
|
"--export", "UseTaggedPDF=true",
|
||||||
"--export", "EnableTextAccessForAccessibilityTools=true",
|
"--export", "EnableTextAccessForAccessibilityTools=true",
|
||||||
)
|
)
|
||||||
|
} else {
|
||||||
|
args = append(
|
||||||
|
args,
|
||||||
|
"--export", "UseTaggedPDF=false",
|
||||||
|
"--export", "EnableTextAccessForAccessibilityTools=false",
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
inputPath, err := nonBasicLatinCharactersGuard(logger, inputPath)
|
inputPath, err := nonBasicLatinCharactersGuard(logger, inputPath)
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"path/filepath"
|
|
||||||
|
|
||||||
"github.com/labstack/echo/v4"
|
"github.com/labstack/echo/v4"
|
||||||
|
|
||||||
@@ -55,8 +54,7 @@ func convertRoute(libreOffice libreofficeapi.Uno, engine gotenberg.PdfEngine) ap
|
|||||||
// Alright, let's convert each document to PDF.
|
// Alright, let's convert each document to PDF.
|
||||||
outputPaths := make([]string, len(inputPaths))
|
outputPaths := make([]string, len(inputPaths))
|
||||||
for i, inputPath := range inputPaths {
|
for i, inputPath := range inputPaths {
|
||||||
// document.docx -> document.docx.pdf.
|
outputPaths[i] = ctx.GeneratePath(".pdf")
|
||||||
outputPaths[i] = ctx.GeneratePath(filepath.Base(inputPath), ".pdf")
|
|
||||||
options := libreofficeapi.Options{
|
options := libreofficeapi.Options{
|
||||||
Landscape: landscape,
|
Landscape: landscape,
|
||||||
PageRanges: nativePageRanges,
|
PageRanges: nativePageRanges,
|
||||||
@@ -91,9 +89,8 @@ func convertRoute(libreOffice libreofficeapi.Uno, engine gotenberg.PdfEngine) ap
|
|||||||
|
|
||||||
// So far so good, let's check if we have to merge the PDFs. Quick
|
// So far so good, let's check if we have to merge the PDFs. Quick
|
||||||
// win: if there is only one PDF, skip this step.
|
// win: if there is only one PDF, skip this step.
|
||||||
|
|
||||||
if len(outputPaths) > 1 && merge {
|
if len(outputPaths) > 1 && merge {
|
||||||
outputPath := ctx.GeneratePath("", ".pdf")
|
outputPath := ctx.GeneratePath(".pdf")
|
||||||
|
|
||||||
err = engine.Merge(ctx, ctx.Log(), outputPaths, outputPath)
|
err = engine.Merge(ctx, ctx.Log(), outputPaths, outputPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -105,7 +102,7 @@ func convertRoute(libreOffice libreofficeapi.Uno, engine gotenberg.PdfEngine) ap
|
|||||||
zeroValued := gotenberg.PdfFormats{}
|
zeroValued := gotenberg.PdfFormats{}
|
||||||
if !nativePdfFormats && pdfFormats != zeroValued {
|
if !nativePdfFormats && pdfFormats != zeroValued {
|
||||||
convertInputPath := outputPath
|
convertInputPath := outputPath
|
||||||
convertOutputPath := ctx.GeneratePath("", ".pdf")
|
convertOutputPath := ctx.GeneratePath(".pdf")
|
||||||
|
|
||||||
err = engine.Convert(ctx, ctx.Log(), pdfFormats, convertInputPath, convertOutputPath)
|
err = engine.Convert(ctx, ctx.Log(), pdfFormats, convertInputPath, convertOutputPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -117,8 +114,7 @@ func convertRoute(libreOffice libreofficeapi.Uno, engine gotenberg.PdfEngine) ap
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Last but not least, add the output path to the context so that
|
// Last but not least, add the output path to the context so that
|
||||||
// the Uno is able to send it as a response to the client.
|
// the API is able to send it as a response to the client.
|
||||||
|
|
||||||
err = ctx.AddOutputPaths(outputPath)
|
err = ctx.AddOutputPaths(outputPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("add output path: %w", err)
|
return fmt.Errorf("add output path: %w", err)
|
||||||
@@ -135,23 +131,34 @@ func convertRoute(libreOffice libreofficeapi.Uno, engine gotenberg.PdfEngine) ap
|
|||||||
|
|
||||||
for i, outputPath := range outputPaths {
|
for i, outputPath := range outputPaths {
|
||||||
convertInputPath := outputPath
|
convertInputPath := outputPath
|
||||||
// document.docx -> document.docx.pdf.
|
convertOutputPaths[i] = ctx.GeneratePath(".pdf")
|
||||||
convertOutputPaths[i] = ctx.GeneratePath(filepath.Base(inputPaths[i]), ".pdf")
|
|
||||||
|
|
||||||
err = engine.Convert(ctx, ctx.Log(), pdfFormats, convertInputPath, convertOutputPaths[i])
|
err = engine.Convert(ctx, ctx.Log(), pdfFormats, convertInputPath, convertOutputPaths[i])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("convert PDF: %w", err)
|
return fmt.Errorf("convert PDF: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Important: the output paths are now the converted files.
|
// Important: the output paths are now the converted files.
|
||||||
outputPaths = convertOutputPaths
|
outputPaths = convertOutputPaths
|
||||||
}
|
}
|
||||||
|
|
||||||
// Last but not least, add the output paths to the context so that
|
if len(outputPaths) > 1 {
|
||||||
// the Uno is able to send them as a response to the client.
|
// If .zip archive, document.docx -> document.docx.pdf.
|
||||||
|
for i, inputPath := range inputPaths {
|
||||||
|
outputPath := fmt.Sprintf("%s.pdf", inputPath)
|
||||||
|
|
||||||
|
err = ctx.Rename(outputPaths[i], outputPath)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("rename output path: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
outputPaths[i] = outputPath
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Last but not least, add the output paths to the context so that
|
||||||
|
// the API is able to send them as a response to the client.
|
||||||
err = ctx.AddOutputPaths(outputPaths...)
|
err = ctx.AddOutputPaths(outputPaths...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("add output paths: %w", err)
|
return fmt.Errorf("add output paths: %w", err)
|
||||||
|
|||||||
@@ -185,7 +185,32 @@ func TestConvertRoute(t *testing.T) {
|
|||||||
expectError: false,
|
expectError: false,
|
||||||
expectHttpError: false,
|
expectHttpError: false,
|
||||||
expectOutputPathsCount: 1,
|
expectOutputPathsCount: 1,
|
||||||
expectOutputPaths: []string{"/document.docx.pdf"},
|
},
|
||||||
|
{
|
||||||
|
scenario: "cannot rename many files",
|
||||||
|
ctx: func() *api.ContextMock {
|
||||||
|
ctx := &api.ContextMock{Context: new(api.Context)}
|
||||||
|
ctx.SetFiles(map[string]string{
|
||||||
|
"document.docx": "/document.docx",
|
||||||
|
"document2.docx": "/document2.docx",
|
||||||
|
"document2.doc": "/document2.doc",
|
||||||
|
})
|
||||||
|
ctx.SetPathRename(&gotenberg.PathRenameMock{RenameMock: func(oldpath, newpath string) error {
|
||||||
|
return errors.New("cannot rename")
|
||||||
|
}})
|
||||||
|
return ctx
|
||||||
|
}(),
|
||||||
|
libreOffice: &libreofficeapi.ApiMock{
|
||||||
|
PdfMock: func(ctx context.Context, logger *zap.Logger, inputPath, outputPath string, options libreofficeapi.Options) error {
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
ExtensionsMock: func() []string {
|
||||||
|
return []string{".docx", ".doc"}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectError: true,
|
||||||
|
expectHttpError: false,
|
||||||
|
expectOutputPathsCount: 0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
scenario: "success (many files)",
|
scenario: "success (many files)",
|
||||||
@@ -196,6 +221,9 @@ func TestConvertRoute(t *testing.T) {
|
|||||||
"document2.docx": "/document2.docx",
|
"document2.docx": "/document2.docx",
|
||||||
"document2.doc": "/document2.doc",
|
"document2.doc": "/document2.doc",
|
||||||
})
|
})
|
||||||
|
ctx.SetPathRename(&gotenberg.PathRenameMock{RenameMock: func(oldpath, newpath string) error {
|
||||||
|
return nil
|
||||||
|
}})
|
||||||
return ctx
|
return ctx
|
||||||
}(),
|
}(),
|
||||||
libreOffice: &libreofficeapi.ApiMock{
|
libreOffice: &libreofficeapi.ApiMock{
|
||||||
@@ -230,6 +258,9 @@ func TestConvertRoute(t *testing.T) {
|
|||||||
"false",
|
"false",
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
ctx.SetPathRename(&gotenberg.PathRenameMock{RenameMock: func(oldpath, newpath string) error {
|
||||||
|
return nil
|
||||||
|
}})
|
||||||
return ctx
|
return ctx
|
||||||
}(),
|
}(),
|
||||||
libreOffice: &libreofficeapi.ApiMock{
|
libreOffice: &libreofficeapi.ApiMock{
|
||||||
@@ -266,6 +297,9 @@ func TestConvertRoute(t *testing.T) {
|
|||||||
"true",
|
"true",
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
ctx.SetPathRename(&gotenberg.PathRenameMock{RenameMock: func(oldpath, newpath string) error {
|
||||||
|
return nil
|
||||||
|
}})
|
||||||
return ctx
|
return ctx
|
||||||
}(),
|
}(),
|
||||||
libreOffice: &libreofficeapi.ApiMock{
|
libreOffice: &libreofficeapi.ApiMock{
|
||||||
|
|||||||
@@ -4,8 +4,6 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/labstack/echo/v4"
|
"github.com/labstack/echo/v4"
|
||||||
|
|
||||||
@@ -44,8 +42,7 @@ func mergeRoute(engine gotenberg.PdfEngine) api.Route {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Alright, let's merge the PDFs.
|
// Alright, let's merge the PDFs.
|
||||||
|
outputPath := ctx.GeneratePath(".pdf")
|
||||||
outputPath := ctx.GeneratePath("", ".pdf")
|
|
||||||
|
|
||||||
err = engine.Merge(ctx, ctx.Log(), inputPaths, outputPath)
|
err = engine.Merge(ctx, ctx.Log(), inputPaths, outputPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -58,7 +55,7 @@ func mergeRoute(engine gotenberg.PdfEngine) api.Route {
|
|||||||
zeroValued := gotenberg.PdfFormats{}
|
zeroValued := gotenberg.PdfFormats{}
|
||||||
if pdfFormats != zeroValued {
|
if pdfFormats != zeroValued {
|
||||||
convertInputPath := outputPath
|
convertInputPath := outputPath
|
||||||
convertOutputPath := ctx.GeneratePath("", ".pdf")
|
convertOutputPath := ctx.GeneratePath(".pdf")
|
||||||
|
|
||||||
err = engine.Convert(ctx, ctx.Log(), pdfFormats, convertInputPath, convertOutputPath)
|
err = engine.Convert(ctx, ctx.Log(), pdfFormats, convertInputPath, convertOutputPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -71,7 +68,6 @@ func mergeRoute(engine gotenberg.PdfEngine) api.Route {
|
|||||||
|
|
||||||
// Last but not least, add the output path to the context so that
|
// Last but not least, add the output path to the context so that
|
||||||
// the API is able to send it as a response to the client.
|
// the API is able to send it as a response to the client.
|
||||||
|
|
||||||
err = ctx.AddOutputPaths(outputPath)
|
err = ctx.AddOutputPaths(outputPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("add output path: %w", err)
|
return fmt.Errorf("add output path: %w", err)
|
||||||
@@ -124,26 +120,29 @@ func convertRoute(engine gotenberg.PdfEngine) api.Route {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Alright, let's convert the PDFs.s
|
// Alright, let's convert the PDFs.
|
||||||
outputPaths := make([]string, len(inputPaths))
|
outputPaths := make([]string, len(inputPaths))
|
||||||
|
|
||||||
for i, inputPath := range inputPaths {
|
for i, inputPath := range inputPaths {
|
||||||
if len(outputPaths) > 1 {
|
outputPaths[i] = ctx.GeneratePath(".pdf")
|
||||||
// If .zip archive, keep the original filenames.
|
|
||||||
outputPaths[i] = ctx.GeneratePath(strings.TrimSuffix(filepath.Base(inputPath), filepath.Ext(inputPath)), ".pdf")
|
|
||||||
} else {
|
|
||||||
outputPaths[i] = ctx.GeneratePath("", ".pdf")
|
|
||||||
}
|
|
||||||
|
|
||||||
err = engine.Convert(ctx, ctx.Log(), pdfFormats, inputPath, outputPaths[i])
|
err = engine.Convert(ctx, ctx.Log(), pdfFormats, inputPath, outputPaths[i])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("convert PDF: %w", err)
|
return fmt.Errorf("convert PDF: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(outputPaths) > 1 {
|
||||||
|
// If .zip archive, keep the original filename.
|
||||||
|
err = ctx.Rename(outputPaths[i], inputPath)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("rename output path: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
outputPaths[i] = inputPath
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Last but not least, add the output paths to the context so that
|
// Last but not least, add the output paths to the context so that
|
||||||
// the API is able to send them as a response to the client.
|
// the API is able to send them as a response to the client.
|
||||||
|
|
||||||
err = ctx.AddOutputPaths(outputPaths...)
|
err = ctx.AddOutputPaths(outputPaths...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("add output paths: %w", err)
|
return fmt.Errorf("add output paths: %w", err)
|
||||||
|
|||||||
@@ -294,6 +294,36 @@ func TestConvertHandler(t *testing.T) {
|
|||||||
expectHttpError: false,
|
expectHttpError: false,
|
||||||
expectOutputPathsCount: 1,
|
expectOutputPathsCount: 1,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
scenario: "cannot rename many files",
|
||||||
|
ctx: func() *api.ContextMock {
|
||||||
|
ctx := &api.ContextMock{Context: new(api.Context)}
|
||||||
|
ctx.SetFiles(map[string]string{
|
||||||
|
"file.pdf": "/file.pdf",
|
||||||
|
"file2.pdf": "/file2.pdf",
|
||||||
|
})
|
||||||
|
ctx.SetValues(map[string][]string{
|
||||||
|
"pdfa": {
|
||||||
|
gotenberg.PdfA1b,
|
||||||
|
},
|
||||||
|
"pdfua": {
|
||||||
|
"true",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
ctx.SetPathRename(&gotenberg.PathRenameMock{RenameMock: func(oldpath, newpath string) error {
|
||||||
|
return errors.New("cannot rename")
|
||||||
|
}})
|
||||||
|
return ctx
|
||||||
|
}(),
|
||||||
|
engine: &gotenberg.PdfEngineMock{
|
||||||
|
ConvertMock: func(ctx context.Context, logger *zap.Logger, formats gotenberg.PdfFormats, inputPath, outputPath string) error {
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectError: true,
|
||||||
|
expectHttpError: false,
|
||||||
|
expectOutputPathsCount: 0,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
scenario: "success with PDF/A & PDF/UA form fields (many files)",
|
scenario: "success with PDF/A & PDF/UA form fields (many files)",
|
||||||
ctx: func() *api.ContextMock {
|
ctx: func() *api.ContextMock {
|
||||||
@@ -310,6 +340,9 @@ func TestConvertHandler(t *testing.T) {
|
|||||||
"true",
|
"true",
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
ctx.SetPathRename(&gotenberg.PathRenameMock{RenameMock: func(oldpath, newpath string) error {
|
||||||
|
return nil
|
||||||
|
}})
|
||||||
return ctx
|
return ctx
|
||||||
}(),
|
}(),
|
||||||
engine: &gotenberg.PdfEngineMock{
|
engine: &gotenberg.PdfEngineMock{
|
||||||
|
|||||||
Reference in New Issue
Block a user