-
-
Notifications
You must be signed in to change notification settings - Fork 501
/
routes.go
266 lines (234 loc) · 9.27 KB
/
routes.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
package libreoffice
import (
"encoding/json"
"errors"
"fmt"
"net/http"
"slices"
"strconv"
"github.com/labstack/echo/v4"
"github.com/gotenberg/gotenberg/v8/pkg/gotenberg"
"github.com/gotenberg/gotenberg/v8/pkg/modules/api"
libreofficeapi "github.com/gotenberg/gotenberg/v8/pkg/modules/libreoffice/api"
)
// convertRoute returns an [api.Route] which can convert LibreOffice documents
// to PDF.
func convertRoute(libreOffice libreofficeapi.Uno, engine gotenberg.PdfEngine) api.Route {
return api.Route{
Method: http.MethodPost,
Path: "/forms/libreoffice/convert",
IsMultipart: true,
Handler: func(c echo.Context) error {
ctx := c.Get("context").(*api.Context)
defaultOptions := libreofficeapi.DefaultOptions()
// Let's get the data from the form and validate them.
var (
inputPaths []string
landscape bool
nativePageRanges string
exportFormFields bool
allowDuplicateFieldNames bool
exportBookmarks bool
exportBookmarksToPdfDestination bool
exportPlaceholders bool
exportNotes bool
exportNotesPages bool
exportOnlyNotesPages bool
exportNotesInMargin bool
convertOooTargetToPdfTarget bool
exportLinksRelativeFsys bool
exportHiddenSlides bool
skipEmptyPages bool
addOriginalDocumentAsStream bool
singlePageSheets bool
losslessImageCompression bool
quality int
reduceImageResolution bool
maxImageResolution int
pdfa string
pdfua bool
nativePdfFormats bool
merge bool
metadata map[string]interface{}
)
err := ctx.FormData().
MandatoryPaths(libreOffice.Extensions(), &inputPaths).
Bool("landscape", &landscape, defaultOptions.Landscape).
String("nativePageRanges", &nativePageRanges, defaultOptions.PageRanges).
Bool("exportFormFields", &exportFormFields, defaultOptions.ExportFormFields).
Bool("allowDuplicateFieldNames", &allowDuplicateFieldNames, defaultOptions.AllowDuplicateFieldNames).
Bool("exportBookmarks", &exportBookmarks, defaultOptions.ExportBookmarks).
Bool("exportBookmarksToPdfDestination", &exportBookmarksToPdfDestination, defaultOptions.ExportBookmarksToPdfDestination).
Bool("exportPlaceholders", &exportPlaceholders, defaultOptions.ExportPlaceholders).
Bool("exportNotes", &exportNotes, defaultOptions.ExportNotes).
Bool("exportNotesPages", &exportNotesPages, defaultOptions.ExportNotesPages).
Bool("exportOnlyNotesPages", &exportOnlyNotesPages, defaultOptions.ExportOnlyNotesPages).
Bool("exportNotesInMargin", &exportNotesInMargin, defaultOptions.ExportNotesInMargin).
Bool("convertOooTargetToPdfTarget", &convertOooTargetToPdfTarget, defaultOptions.ConvertOooTargetToPdfTarget).
Bool("exportLinksRelativeFsys", &exportLinksRelativeFsys, defaultOptions.ExportLinksRelativeFsys).
Bool("exportHiddenSlides", &exportHiddenSlides, defaultOptions.ExportHiddenSlides).
Bool("skipEmptyPages", &skipEmptyPages, defaultOptions.SkipEmptyPages).
Bool("addOriginalDocumentAsStream", &addOriginalDocumentAsStream, defaultOptions.AddOriginalDocumentAsStream).
Bool("singlePageSheets", &singlePageSheets, defaultOptions.SinglePageSheets).
Bool("losslessImageCompression", &losslessImageCompression, defaultOptions.LosslessImageCompression).
Custom("quality", func(value string) error {
if value == "" {
quality = defaultOptions.Quality
return nil
}
intValue, err := strconv.Atoi(value)
if err != nil {
return err
}
if intValue < 1 {
return errors.New("value is inferior to 1")
}
if intValue > 100 {
return errors.New("value is superior to 100")
}
quality = intValue
return nil
}).
Bool("reduceImageResolution", &reduceImageResolution, defaultOptions.ReduceImageResolution).
Custom("maxImageResolution", func(value string) error {
if value == "" {
maxImageResolution = defaultOptions.MaxImageResolution
return nil
}
intValue, err := strconv.Atoi(value)
if err != nil {
return err
}
if !slices.Contains([]int{75, 150, 300, 600, 1200}, intValue) {
return errors.New("value is not 75, 150, 300, 600 or 1200")
}
maxImageResolution = intValue
return nil
}).
String("pdfa", &pdfa, "").
Bool("pdfua", &pdfua, false).
Bool("nativePdfFormats", &nativePdfFormats, true).
Bool("merge", &merge, false).
Custom("metadata", func(value string) error {
if len(value) > 0 {
err := json.Unmarshal([]byte(value), &metadata)
if err != nil {
return fmt.Errorf("unmarshal metadata: %w", err)
}
}
return nil
}).
Validate()
if err != nil {
return fmt.Errorf("validate form data: %w", err)
}
pdfFormats := gotenberg.PdfFormats{
PdfA: pdfa,
PdfUa: pdfua,
}
// Alright, let's convert each document to PDF.
outputPaths := make([]string, len(inputPaths))
for i, inputPath := range inputPaths {
outputPaths[i] = ctx.GeneratePath(".pdf")
options := libreofficeapi.Options{
Landscape: landscape,
PageRanges: nativePageRanges,
ExportFormFields: exportFormFields,
AllowDuplicateFieldNames: allowDuplicateFieldNames,
ExportBookmarks: exportBookmarks,
ExportBookmarksToPdfDestination: exportBookmarksToPdfDestination,
ExportPlaceholders: exportPlaceholders,
ExportNotes: exportNotes,
ExportNotesPages: exportNotesPages,
ExportOnlyNotesPages: exportOnlyNotesPages,
ExportNotesInMargin: exportNotesInMargin,
ConvertOooTargetToPdfTarget: convertOooTargetToPdfTarget,
ExportLinksRelativeFsys: exportLinksRelativeFsys,
ExportHiddenSlides: exportHiddenSlides,
SkipEmptyPages: skipEmptyPages,
AddOriginalDocumentAsStream: addOriginalDocumentAsStream,
SinglePageSheets: singlePageSheets,
LosslessImageCompression: losslessImageCompression,
Quality: quality,
ReduceImageResolution: reduceImageResolution,
MaxImageResolution: maxImageResolution,
}
if nativePdfFormats {
options.PdfFormats = pdfFormats
}
err = libreOffice.Pdf(ctx, ctx.Log(), inputPath, outputPaths[i], options)
if err != nil {
if errors.Is(err, libreofficeapi.ErrInvalidPdfFormats) {
return api.WrapError(
fmt.Errorf("convert to PDF: %w", err),
api.NewSentinelHttpError(
http.StatusBadRequest,
fmt.Sprintf("A PDF format in '%+v' is not supported", pdfFormats),
),
)
}
if errors.Is(err, libreofficeapi.ErrMalformedPageRanges) {
return api.WrapError(
fmt.Errorf("convert to PDF: %w", err),
api.NewSentinelHttpError(http.StatusBadRequest, fmt.Sprintf("Malformed page ranges '%s' (nativePageRanges)", options.PageRanges)),
)
}
return fmt.Errorf("convert to PDF: %w", err)
}
}
// So far so good, let's check if we have to merge the PDFs.
if len(outputPaths) > 1 && merge {
outputPath := ctx.GeneratePath(".pdf")
err = engine.Merge(ctx, ctx.Log(), outputPaths, outputPath)
if err != nil {
return fmt.Errorf("merge PDFs: %w", err)
}
// Only one output path.
outputPaths = []string{outputPath}
}
// Let's check if the client want to convert each PDF to a specific
// PDF format.
zeroValued := gotenberg.PdfFormats{}
if !nativePdfFormats && pdfFormats != zeroValued {
convertOutputPaths := make([]string, len(outputPaths))
for i, outputPath := range outputPaths {
convertInputPath := outputPath
convertOutputPaths[i] = ctx.GeneratePath(".pdf")
err = engine.Convert(ctx, ctx.Log(), pdfFormats, convertInputPath, convertOutputPaths[i])
if err != nil {
return fmt.Errorf("convert PDF: %w", err)
}
}
// Important: the output paths are now the converted files.
outputPaths = convertOutputPaths
}
// Writes and potentially overrides metadata entries, if any.
if len(metadata) > 0 {
for _, outputPath := range outputPaths {
err = engine.WriteMetadata(ctx, ctx.Log(), metadata, outputPath)
if err != nil {
return fmt.Errorf("write metadata: %w", err)
}
}
}
if len(outputPaths) > 1 {
// 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...)
if err != nil {
return fmt.Errorf("add output paths: %w", err)
}
return nil
},
}
}