forked from pdfcpu/pdfcpu
/
configuration.go
482 lines (424 loc) · 12.1 KB
/
configuration.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
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
/*
Copyright 2018 The pdfcpu Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package model
import (
_ "embed"
"fmt"
"os"
"path/filepath"
"time"
"github.com/angel-one/pdfcpu/pkg/font"
"github.com/angel-one/pdfcpu/pkg/log"
"github.com/angel-one/pdfcpu/pkg/pdfcpu/types"
)
const (
// ValidationStrict ensures 100% compliance with the spec (PDF 32000-1:2008).
ValidationStrict int = iota
// ValidationRelaxed ensures PDF compliance based on frequently encountered validation errors.
ValidationRelaxed
)
// See table 22 - User access permissions
type PermissionFlags int
const (
UnusedFlag1 PermissionFlags = 1 << iota // Bit 1: unused
UnusedFlag2 // Bit 2: unused
PermissionPrintRev2 // Bit 3: Print (security handlers rev.2), draft print (security handlers >= rev.3)
PermissionModify // Bit 4: Modify contents by operations other than controlled by bits 6, 9, 11.
PermissionExtract // Bit 5: Copy, extract text & graphics
PermissionModAnnFillForm // Bit 6: Add or modify annotations, fill form fields, in conjunction with bit 4 create/mod form fields.
UnusedFlag7 // Bit 7: unused
UnusedFlag8 // Bit 8: unused
PermissionFillRev3 // Bit 9: Fill form fields (security handlers >= rev.3)
PermissionExtractRev3 // Bit 10: Copy, extract text & graphics (security handlers >= rev.3) (unused since PDF 2.0)
PermissionAssembleRev3 // Bit 11: Assemble document (security handlers >= rev.3)
PermissionPrintRev3 // Bit 12: Print (security handlers >= rev.3)
)
const (
PermissionsNone = PermissionFlags(0xF0C3)
PermissionsPrint = PermissionsNone + PermissionPrintRev2 + PermissionPrintRev3
PermissionsAll = PermissionFlags(0xFFFF)
)
const (
// StatsFileNameDefault is the standard stats filename.
StatsFileNameDefault = "stats.csv"
)
// CommandMode specifies the operation being executed.
type CommandMode int
// The available commands.
const (
VALIDATE CommandMode = iota
LISTINFO
OPTIMIZE
SPLIT
SPLITBYPAGENR
MERGECREATE
MERGECREATEZIP
MERGEAPPEND
EXTRACTIMAGES
EXTRACTFONTS
EXTRACTPAGES
EXTRACTCONTENT
EXTRACTMETADATA
TRIM
LISTATTACHMENTS
EXTRACTATTACHMENTS
ADDATTACHMENTS
ADDATTACHMENTSPORTFOLIO
REMOVEATTACHMENTS
LISTPERMISSIONS
SETPERMISSIONS
ADDWATERMARKS
REMOVEWATERMARKS
IMPORTIMAGES
INSERTPAGESBEFORE
INSERTPAGESAFTER
REMOVEPAGES
LISTKEYWORDS
ADDKEYWORDS
REMOVEKEYWORDS
LISTPROPERTIES
ADDPROPERTIES
REMOVEPROPERTIES
COLLECT
CROP
LISTBOXES
ADDBOXES
REMOVEBOXES
LISTANNOTATIONS
ADDANNOTATIONS
REMOVEANNOTATIONS
ROTATE
NUP
BOOKLET
LISTBOOKMARKS
ADDBOOKMARKS
REMOVEBOOKMARKS
IMPORTBOOKMARKS
EXPORTBOOKMARKS
LISTIMAGES
CREATE
DUMP
LISTFORMFIELDS
REMOVEFORMFIELDS
LOCKFORMFIELDS
UNLOCKFORMFIELDS
RESETFORMFIELDS
EXPORTFORMFIELDS
FILLFORMFIELDS
MULTIFILLFORMFIELDS
ENCRYPT
DECRYPT
CHANGEUPW
CHANGEOPW
CHEATSHEETSFONTS
INSTALLFONTS
LISTFONTS
RESIZE
POSTER
NDOWN
CUT
LISTPAGELAYOUT
SETPAGELAYOUT
RESETPAGELAYOUT
LISTPAGEMODE
SETPAGEMODE
RESETPAGEMODE
LISTVIEWERPREFERENCES
SETVIEWERPREFERENCES
RESETVIEWERPREFERENCES
ZOOM
)
// Configuration of a Context.
type Configuration struct {
// Location of corresponding config.yml
Path string
// Check filename extensions.
CheckFileNameExt bool
// Enables PDF V1.5 compatible processing of object streams, xref streams, hybrid PDF files.
Reader15 bool
// Enables decoding of all streams (fontfiles, images..) for logging purposes.
DecodeAllStreams bool
// Validate against ISO-32000: strict or relaxed.
ValidationMode int
// Enable validation right before writing.
PostProcessValidate bool
// Check for broken links in LinkedAnnotations/URIActions.
ValidateLinks bool
// End of line char sequence for writing.
Eol string
// Turns on object stream generation.
// A signal for compressing any new non-stream-object into an object stream.
// true enforces WriteXRefStream to true.
// false does not prevent xRefStream generation.
WriteObjectStream bool
// Switches between xRefSection (<=V1.4) and objectStream/xRefStream (>=V1.5) writing.
WriteXRefStream bool
// Turns on stats collection.
// TODO Decision - unused.
CollectStats bool
// A CSV-filename holding the statistics.
StatsFileName string
// Supplied user password.
UserPW string
UserPWNew *string
// Supplied owner password.
OwnerPW string
OwnerPWNew *string
// EncryptUsingAES ensures AES encryption.
// true: AES encryption
// false: RC4 encryption.
EncryptUsingAES bool
// AES:40,128,256 RC4:40,128
EncryptKeyLength int
// Supplied user access permissions, see Table 22.
Permissions PermissionFlags // int16
// Command being executed.
Cmd CommandMode
// Display unit in effect.
Unit types.DisplayUnit
// Timestamp format.
TimestampFormat string
// Date format.
DateFormat string
// Optimize duplicate content streams across pages.
OptimizeDuplicateContentStreams bool
// Merge creates bookmarks
CreateBookmarks bool
// PDF Viewer is expected to supply appearance streams for form fields.
NeedAppearances bool
}
// ConfigPath defines the location of pdfcpu's configuration directory.
// If set to a file path, pdfcpu will ensure the config dir at this location.
// Other possible values:
//
// default: Ensure config dir at default location
// disable: Disable config dir usage
//
// If you want to disable config dir usage in a multi threaded environment
// you are encouraged to use api.DisableConfigDir().
var ConfigPath string = "default"
var loadedDefaultConfig *Configuration
//go:embed resources/config.yml
var configFileBytes []byte
//go:embed resources/Roboto-Regular.ttf
var robotoFontFileBytes []byte
func ensureConfigFileAt(path string) error {
f, err := os.Open(path)
if err != nil {
f.Close()
s := fmt.Sprintf("#############################\n# pdfcpu %s #\n# Created: %s #\n", VersionStr, time.Now().Format("2006-01-02 15:04"))
bb := append([]byte(s), configFileBytes...)
if err := os.WriteFile(path, bb, os.ModePerm); err != nil {
return err
}
f, err = os.Open(path)
if err != nil {
return err
}
}
defer f.Close()
// Load configuration into loadedDefaultConfig.
return parseConfigFile(f, path)
}
// EnsureDefaultConfigAt tries to load the default configuration from path.
// If path/pdfcpu/config.yaml is not found, it will be created.
func EnsureDefaultConfigAt(path string) error {
configDir := filepath.Join(path, "pdfcpu")
font.UserFontDir = filepath.Join(configDir, "fonts")
if err := os.MkdirAll(font.UserFontDir, os.ModePerm); err != nil {
return err
}
if err := ensureConfigFileAt(filepath.Join(configDir, "config.yml")); err != nil {
return err
}
//fmt.Println(loadedDefaultConfig)
files, err := os.ReadDir(font.UserFontDir)
if err != nil {
return err
}
if len(files) == 0 {
// Ensure Roboto font for form filling.
fn := "Roboto-Regular"
if log.CLIEnabled() {
log.CLI.Printf("installing user font:")
}
if err := font.InstallFontFromBytes(font.UserFontDir, fn, robotoFontFileBytes); err != nil {
if log.CLIEnabled() {
log.CLI.Printf("%v", err)
}
}
}
return font.LoadUserFonts()
}
func newDefaultConfiguration() *Configuration {
// NOTE: Needs to stay in sync with config.yml
//
// Takes effect whenever the installed config.yml is disabled:
// cli: supply -conf disable
// api: call api.DisableConfigDir()
return &Configuration{
CheckFileNameExt: true,
Reader15: true,
DecodeAllStreams: false,
ValidationMode: ValidationRelaxed,
ValidateLinks: false,
Eol: types.EolLF,
WriteObjectStream: true,
WriteXRefStream: true,
EncryptUsingAES: true,
EncryptKeyLength: 256,
Permissions: PermissionsPrint,
TimestampFormat: "2006-01-02 15:04",
DateFormat: "2006-01-02",
OptimizeDuplicateContentStreams: false,
CreateBookmarks: true,
NeedAppearances: false,
}
}
// NewDefaultConfiguration returns the default pdfcpu configuration.
func NewDefaultConfiguration() *Configuration {
if loadedDefaultConfig != nil {
c := *loadedDefaultConfig
return &c
}
if ConfigPath != "disable" {
path, err := os.UserConfigDir()
if err != nil {
path = os.TempDir()
}
if err = EnsureDefaultConfigAt(path); err == nil {
c := *loadedDefaultConfig
return &c
}
fmt.Fprintf(os.Stderr, "pdfcpu: config dir problem: %v\n", err)
os.Exit(1)
}
// Bypass config.yml
return newDefaultConfiguration()
}
// NewAESConfiguration returns a default configuration for AES encryption.
func NewAESConfiguration(userPW, ownerPW string, keyLength int) *Configuration {
c := NewDefaultConfiguration()
c.UserPW = userPW
c.OwnerPW = ownerPW
c.EncryptUsingAES = true
c.EncryptKeyLength = keyLength
return c
}
// NewRC4Configuration returns a default configuration for RC4 encryption.
func NewRC4Configuration(userPW, ownerPW string, keyLength int) *Configuration {
c := NewDefaultConfiguration()
c.UserPW = userPW
c.OwnerPW = ownerPW
c.EncryptUsingAES = false
c.EncryptKeyLength = keyLength
return c
}
func (c Configuration) String() string {
path := "default"
if len(c.Path) > 0 {
path = c.Path
}
return fmt.Sprintf("pdfcpu configuration:\n"+
"Path: %s\n"+
"CheckFileNameExt: %t\n"+
"Reader15: %t\n"+
"DecodeAllStreams: %t\n"+
"ValidationMode: %s\n"+
"Eol: %s\n"+
"WriteObjectStream: %t\n"+
"WriteXrefStream: %t\n"+
"EncryptUsingAES: %t\n"+
"EncryptKeyLength: %d\n"+
"Permissions: %d\n"+
"Unit : %s\n"+
"TimestampFormat: %s\n"+
"DateFormat: %s\n"+
"OptimizeDuplicateContentStreams %t\n"+
"CreateBookmarks %t\n"+
"NeedAppearances %t\n",
path,
c.CheckFileNameExt,
c.Reader15,
c.DecodeAllStreams,
c.ValidationModeString(),
c.EolString(),
c.WriteObjectStream,
c.WriteXRefStream,
c.EncryptUsingAES,
c.EncryptKeyLength,
c.Permissions,
c.UnitString(),
c.TimestampFormat,
c.DateFormat,
c.OptimizeDuplicateContentStreams,
c.CreateBookmarks,
c.NeedAppearances,
)
}
// EolString returns a string rep for the eol in effect.
func (c *Configuration) EolString() string {
var s string
switch c.Eol {
case types.EolLF:
s = "EolLF"
case types.EolCR:
s = "EolCR"
case types.EolCRLF:
s = "EolCRLF"
}
return s
}
// ValidationModeString returns a string rep for the validation mode in effect.
func (c *Configuration) ValidationModeString() string {
if c.ValidationMode == ValidationStrict {
return "strict"
}
return "relaxed"
}
// UnitString returns a string rep for the display unit in effect.
func (c *Configuration) UnitString() string {
var s string
switch c.Unit {
case types.POINTS:
s = "points"
case types.INCHES:
s = "inches"
case types.CENTIMETRES:
s = "cm"
case types.MILLIMETRES:
s = "mm"
}
return s
}
// SetUnit configures the display unit.
func (c *Configuration) SetUnit(s string) {
switch s {
case "points":
c.Unit = types.POINTS
case "inches":
c.Unit = types.INCHES
case "cm":
c.Unit = types.CENTIMETRES
case "mm":
c.Unit = types.MILLIMETRES
}
}
// ApplyReducedFeatureSet returns true if complex entries like annotations shall not be written.
func (c *Configuration) ApplyReducedFeatureSet() bool {
switch c.Cmd {
case SPLIT, TRIM, EXTRACTPAGES, IMPORTIMAGES:
return true
}
return false
}