346 lines
8.1 KiB
Go
346 lines
8.1 KiB
Go
/*
|
|
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 pdfcpu
|
|
|
|
import (
|
|
"fmt"
|
|
"io/ioutil"
|
|
"os"
|
|
"path/filepath"
|
|
|
|
"github.com/pdfcpu/pdfcpu/internal/config"
|
|
"github.com/pdfcpu/pdfcpu/pkg/font"
|
|
)
|
|
|
|
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
|
|
|
|
// ValidationNone bypasses validation.
|
|
ValidationNone
|
|
)
|
|
|
|
const (
|
|
|
|
// StatsFileNameDefault is the standard stats filename.
|
|
StatsFileNameDefault = "stats.csv"
|
|
|
|
// PermissionsAll enables all user access permission bits.
|
|
PermissionsAll int16 = -1 // 0xFFFF
|
|
|
|
// PermissionsNone disables all user access permissions bits.
|
|
PermissionsNone int16 = -3901 // 0xF0C3
|
|
|
|
)
|
|
|
|
// CommandMode specifies the operation being executed.
|
|
type CommandMode int
|
|
|
|
// The available commands.
|
|
const (
|
|
VALIDATE CommandMode = iota
|
|
OPTIMIZE
|
|
SPLIT
|
|
MERGECREATE
|
|
MERGEAPPEND
|
|
EXTRACTIMAGES
|
|
EXTRACTFONTS
|
|
EXTRACTPAGES
|
|
EXTRACTCONTENT
|
|
EXTRACTMETADATA
|
|
TRIM
|
|
ADDATTACHMENTS
|
|
ADDATTACHMENTSPORTFOLIO
|
|
REMOVEATTACHMENTS
|
|
EXTRACTATTACHMENTS
|
|
LISTATTACHMENTS
|
|
SETPERMISSIONS
|
|
LISTPERMISSIONS
|
|
ENCRYPT
|
|
DECRYPT
|
|
CHANGEUPW
|
|
CHANGEOPW
|
|
ADDWATERMARKS
|
|
REMOVEWATERMARKS
|
|
IMPORTIMAGES
|
|
INSERTPAGESBEFORE
|
|
INSERTPAGESAFTER
|
|
REMOVEPAGES
|
|
ROTATE
|
|
NUP
|
|
BOOKLET
|
|
INFO
|
|
CHEATSHEETSFONTS
|
|
INSTALLFONTS
|
|
LISTFONTS
|
|
LISTKEYWORDS
|
|
ADDKEYWORDS
|
|
REMOVEKEYWORDS
|
|
LISTPROPERTIES
|
|
ADDPROPERTIES
|
|
REMOVEPROPERTIES
|
|
COLLECT
|
|
CROP
|
|
LISTBOXES
|
|
ADDBOXES
|
|
REMOVEBOXES
|
|
)
|
|
|
|
// Configuration of a Context.
|
|
type Configuration struct {
|
|
Path string
|
|
|
|
// 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
|
|
|
|
// 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 int16
|
|
|
|
// Command being executed.
|
|
Cmd CommandMode
|
|
|
|
// Display unit in effect.
|
|
Unit DisplayUnit
|
|
}
|
|
|
|
// 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
|
|
var ConfigPath string = "default"
|
|
|
|
var loadedDefaultConfig *Configuration
|
|
|
|
func generateConfigFile(fileName string) error {
|
|
if err := ioutil.WriteFile(fileName, config.ConfigFileBytes, os.ModePerm); err != nil {
|
|
return err
|
|
}
|
|
loadedDefaultConfig = newDefaultConfiguration()
|
|
loadedDefaultConfig.Path = fileName
|
|
return nil
|
|
}
|
|
|
|
func ensureConfigFileAt(path string) error {
|
|
// Accept config.yml and config.yaml
|
|
f, err := os.Open(path)
|
|
if err != nil {
|
|
// Create path/pdfcpu/config.yml
|
|
//fmt.Printf("writing %s ..\n", path)
|
|
return generateConfigFile(path)
|
|
}
|
|
defer f.Close()
|
|
// Load configuration into loadedDefaultConfig.
|
|
//fmt.Printf("loading %s ...\n", path)
|
|
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)
|
|
return font.LoadUserFonts()
|
|
}
|
|
|
|
func newDefaultConfiguration() *Configuration {
|
|
// NOTE: pdfcpu/internal/config/config.yml must be updated whenever the default configuration changes.
|
|
return &Configuration{
|
|
Reader15: true,
|
|
DecodeAllStreams: false,
|
|
ValidationMode: ValidationRelaxed,
|
|
Eol: EolLF,
|
|
WriteObjectStream: true,
|
|
WriteXRefStream: true,
|
|
EncryptUsingAES: true,
|
|
EncryptKeyLength: 256,
|
|
Permissions: PermissionsNone,
|
|
}
|
|
}
|
|
|
|
// 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)
|
|
}
|
|
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"+
|
|
"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",
|
|
path,
|
|
c.Reader15,
|
|
c.DecodeAllStreams,
|
|
c.ValidationModeString(),
|
|
c.EolString(),
|
|
c.WriteObjectStream,
|
|
c.WriteXRefStream,
|
|
c.EncryptUsingAES,
|
|
c.EncryptKeyLength,
|
|
c.Permissions,
|
|
c.UnitString())
|
|
}
|
|
|
|
// EolString returns a string rep for the eol in effect.
|
|
func (c *Configuration) EolString() string {
|
|
var s string
|
|
switch c.Eol {
|
|
case EolLF:
|
|
s = "EolLF"
|
|
case EolCR:
|
|
s = "EolCR"
|
|
case 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"
|
|
}
|
|
if c.ValidationMode == ValidationRelaxed {
|
|
return "relaxed"
|
|
}
|
|
return "none"
|
|
}
|
|
|
|
// UnitString returns a string rep for the display unit in effect.
|
|
func (c *Configuration) UnitString() string {
|
|
var s string
|
|
switch c.Unit {
|
|
case POINTS:
|
|
s = "points"
|
|
case INCHES:
|
|
s = "inches"
|
|
case CENTIMETRES:
|
|
s = "cm"
|
|
case MILLIMETRES:
|
|
s = "mm"
|
|
}
|
|
return s
|
|
}
|
|
|
|
// ApplyReducedFeatureSet returns true if complex entries like annotations shall not be written.
|
|
func (c *Configuration) ApplyReducedFeatureSet() bool {
|
|
switch c.Cmd {
|
|
case SPLIT, TRIM, EXTRACTPAGES, MERGECREATE, MERGEAPPEND, IMPORTIMAGES:
|
|
return true
|
|
}
|
|
return false
|
|
}
|