Skip to content

Commit

Permalink
Fix #716, add option for merging with divider
Browse files Browse the repository at this point in the history
  • Loading branch information
hhrutter committed Nov 21, 2023
1 parent 4f99328 commit 125cee2
Show file tree
Hide file tree
Showing 17 changed files with 642 additions and 200 deletions.
38 changes: 32 additions & 6 deletions Dockerfile
@@ -1,4 +1,23 @@
# Dockerfile References: https://docs.docker.com/engine/reference/builder/
#
# Usage:
#
# docker build -t pdfcpu .
#
# Simple one off container:
# docker run pdfcpu
#
# One off container with dir binding:
# docker run -v $(pwd):/data -it --rm pdfcpu pdfcpu val test.pdf
#
# Create & run reusable container with dir binding:
# docker run --name pdfcpu -v $(pwd):/data -it pdfcpu /bin/sh
# /data # ... // run pdfcpu commands against your data
# /data # exit // exit container
#
# docker start -i pdfcpu // restart container with dir binding
# /data # ... // run pdfcpu commands against your data
# /data # exit // exit container

# Start from the latest golang base image
FROM golang:latest as builder
Expand All @@ -10,12 +29,19 @@ RUN go install github.com/pdfcpu/pdfcpu/cmd/pdfcpu@latest

FROM alpine:latest

RUN apk --no-cache add ca-certificates
RUN apk --no-cache add ca-certificates gcompat

WORKDIR /root/
WORKDIR /root

# Copy the pre-built binary file from the previous stage
COPY --from=builder /go/bin ./

# Export path of executable
ENV PATH="${PATH}:/root"

WORKDIR /data

# Command to run executable
CMD pdfcpu && echo && pdfcpu version -v

# Copy the Pre-built binary file from the previous stage
COPY --from=builder /go/bin .

# Command to run the executable
CMD ["./pdfcpu"]
1 change: 1 addition & 0 deletions c1.out
@@ -0,0 +1 @@
mode: set
15 changes: 9 additions & 6 deletions cmd/pdfcpu/init.go
Expand Up @@ -303,6 +303,8 @@ func initCommandMap() {
}

func initFlags() {
flag.BoolVar(&all, "all", false, "")
flag.BoolVar(&all, "a", false, "")

bookmarksUsage := "create bookmarks while merging"
flag.BoolVar(&bookmarks, "bookmarks", true, bookmarksUsage)
Expand All @@ -313,6 +315,10 @@ func initFlags() {
flag.StringVar(&conf, "conf", "", confUsage)
flag.StringVar(&conf, "c", "", confUsage)

dividerPageUsage := "create divider pages while merging"
flag.BoolVar(&dividerPage, "dividerPage", false, dividerPageUsage)
flag.BoolVar(&dividerPage, "d", false, dividerPageUsage)

jsonUsage := "produce JSON output"
flag.BoolVar(&json, "json", false, jsonUsage)
flag.BoolVar(&json, "j", false, jsonUsage)
Expand Down Expand Up @@ -354,15 +360,12 @@ func initFlags() {
flag.StringVar(&unit, "unit", "", unitUsage)
flag.StringVar(&unit, "u", "", unitUsage)

flag.BoolVar(&verbose, "verbose", false, "")
flag.BoolVar(&verbose, "v", false, "")
flag.BoolVar(&veryVerbose, "vv", false, "")

flag.StringVar(&upw, "upw", "", "user password")
flag.StringVar(&opw, "opw", "", "owner password")

flag.BoolVar(&all, "all", false, "")
flag.BoolVar(&all, "a", false, "")
flag.BoolVar(&verbose, "verbose", false, "")
flag.BoolVar(&verbose, "v", false, "")
flag.BoolVar(&veryVerbose, "vv", false, "")
}

func initLogging(verbose, veryVerbose bool) {
Expand Down
14 changes: 7 additions & 7 deletions cmd/pdfcpu/main.go
Expand Up @@ -23,13 +23,13 @@ import (
)

var (
fileStats, mode, selectedPages string
upw, opw, key, perm, unit, conf string
verbose, veryVerbose bool
links, quiet, sorted, bookmarks bool
all, json, replaceBookmarks bool
needStackTrace = true
cmdMap commandMap
fileStats, mode, selectedPages string
upw, opw, key, perm, unit, conf string
verbose, veryVerbose bool
links, quiet, sorted, bookmarks bool
all, dividerPage, json, replaceBookmarks bool
needStackTrace = true
cmdMap commandMap
)

// Set by Goreleaser.
Expand Down
87 changes: 59 additions & 28 deletions cmd/pdfcpu/process.go
Expand Up @@ -24,6 +24,8 @@ import (
"os"
"path/filepath"
"regexp"
"runtime"
"runtime/debug"
"sort"
"strconv"
"strings"
Expand Down Expand Up @@ -123,13 +125,24 @@ func printVersion(conf *model.Configuration) {
os.Exit(1)
}

fmt.Fprintf(os.Stdout, "pdfcpu: %v\n", model.VersionStr)
if date != "?" {
fmt.Fprintf(os.Stdout, "build : %v\ncommit: %v\n", date, commit)
}
if verbose {
fmt.Fprintf(os.Stdout, "config: %s\n", conf.Path)
fmt.Fprintf(os.Stdout, "pdfcpu: %s\n", model.VersionStr)

if date == "?" {
if info, ok := debug.ReadBuildInfo(); ok {
for _, setting := range info.Settings {
if setting.Key == "vcs.revision" {
commit = setting.Value
}
if setting.Key == "vcs.time" {
date = setting.Value
}
}
}
}

fmt.Fprintf(os.Stdout, "commit: %s (%s)\n", commit[:8], date)
fmt.Fprintf(os.Stdout, "base : %s\n", runtime.Version())
fmt.Fprintf(os.Stdout, "config: %s\n", conf.Path)
}

func process(cmd *cli.Command) {
Expand Down Expand Up @@ -247,7 +260,7 @@ func processSplitCommand(conf *model.Configuration) {
if mode == "" {
mode = "span"
}
mode = extractModeCompletion(mode, []string{"span", "bookmark", "page"})
mode = modeCompletion(mode, []string{"span", "bookmark", "page"})
if mode == "" || len(flag.Args()) < 2 || selectedPages != "" {
fmt.Fprintf(os.Stderr, "%s\n\n", usageSplit)
os.Exit(1)
Expand Down Expand Up @@ -302,21 +315,7 @@ func sortFiles(filesIn []string) {
})
}

func processMergeCommand(conf *model.Configuration) {
if mode == "" {
mode = "create"
}
mode = extractModeCompletion(mode, []string{"create", "append"})
if mode == "" {
fmt.Fprintf(os.Stderr, "%s\n\n", usageMerge)
os.Exit(1)
}

if len(flag.Args()) < 2 || selectedPages != "" {
fmt.Fprintf(os.Stderr, "%s\n\n", usageMerge)
os.Exit(1)
}

func processArgsForMerge(conf *model.Configuration) ([]string, string) {
filesIn := []string{}
outFile := ""
for i, arg := range flag.Args() {
Expand All @@ -329,7 +328,7 @@ func processMergeCommand(conf *model.Configuration) {
fmt.Fprintf(os.Stderr, "%s may appear as inFile or outFile only\n", outFile)
os.Exit(1)
}
if strings.Contains(arg, "*") {
if mode != "zip" && strings.Contains(arg, "*") {
matches, err := filepath.Glob(arg)
if err != nil {
fmt.Fprintf(os.Stderr, "%s", err)
Expand All @@ -343,6 +342,34 @@ func processMergeCommand(conf *model.Configuration) {
}
filesIn = append(filesIn, arg)
}
return filesIn, outFile
}

func processMergeCommand(conf *model.Configuration) {
if mode == "" {
mode = "create"
}
mode = modeCompletion(mode, []string{"create", "append", "zip"})
if mode == "" {
fmt.Fprintf(os.Stderr, "%s\n\n", usageMerge)
os.Exit(1)
}

if len(flag.Args()) < 2 || selectedPages != "" {
fmt.Fprintf(os.Stderr, "%s\n\n", usageMerge)
os.Exit(1)
}

if mode == "zip" && len(flag.Args()) != 3 {
fmt.Fprintf(os.Stderr, "merge zip: expecting outFile inFile1 inFile2\n")
os.Exit(1)
}

if mode == "zip" && dividerPage {
fmt.Fprintf(os.Stderr, "merge zip: -d(ivider) not applicable and will be ignored\n")
}

filesIn, outFile := processArgsForMerge(conf)

if sorted {
sortFiles(filesIn)
Expand All @@ -360,16 +387,20 @@ func processMergeCommand(conf *model.Configuration) {
switch mode {

case "create":
cmd = cli.MergeCreateCommand(filesIn, outFile, conf)
cmd = cli.MergeCreateCommand(filesIn, outFile, dividerPage, conf)

case "zip":
cmd = cli.MergeCreateZipCommand(filesIn, outFile, conf)

case "append":
cmd = cli.MergeAppendCommand(filesIn, outFile, conf)
cmd = cli.MergeAppendCommand(filesIn, outFile, dividerPage, conf)

}

process(cmd)
}

func extractModeCompletion(modePrefix string, modes []string) string {
func modeCompletion(modePrefix string, modes []string) string {
var modeStr string
for _, mode := range modes {
if !strings.HasPrefix(mode, modePrefix) {
Expand All @@ -384,7 +415,7 @@ func extractModeCompletion(modePrefix string, modes []string) string {
}

func processExtractCommand(conf *model.Configuration) {
mode = extractModeCompletion(mode, []string{"image", "font", "page", "content", "meta"})
mode = modeCompletion(mode, []string{"image", "font", "page", "content", "meta"})
if len(flag.Args()) != 2 || mode == "" {
fmt.Fprintf(os.Stderr, "%s\n\n", usageExtract)
os.Exit(1)
Expand Down Expand Up @@ -2061,7 +2092,7 @@ func processMultiFillFormCommand(conf *model.Configuration) {
if mode == "" {
mode = "single"
}
mode = extractModeCompletion(mode, []string{"single", "merge"})
mode = modeCompletion(mode, []string{"single", "merge"})
if mode == "" {
fmt.Fprintf(os.Stderr, "usage: %s\n\n", usageFormMultiFill)
os.Exit(1)
Expand Down
9 changes: 6 additions & 3 deletions cmd/pdfcpu/usage.go
Expand Up @@ -153,12 +153,13 @@ Eg. pdfcpu split test.pdf . (= pdfcpu split -m span test.pdf . 1)
test_4-9.pdf
test_10-20.pdf`

usageMerge = "usage: pdfcpu merge [-m(ode) create|append] [-s(ort) -b(ookmarks)] outFile inFile..." + generalFlags
usageMerge = "usage: pdfcpu merge [-m(ode) create|append|zip] [ -s(ort) -b(ookmarks) -d(ivider)] outFile inFile..." + generalFlags
usageLongMerge = `Concatenate a sequence of PDFs/inFiles into outFile.
mode ... merge mode (defaults to create)
sort ... sort inFiles by file name
bookmarks ... create bookmarks
divider ... insert blank page between merged documents
outFile ... output PDF file
inFile ... a list of PDF files subject to concatenation.
Expand All @@ -168,6 +169,8 @@ The merge modes are:
append ... if outFile does not exist, it will be created (like in default mode).
if outFile already exists, inFiles will be appended to outFile.
zip ... zip inFile1 and inFile2 into outFile (which will be created and possibly overwritten).
Skip bookmark creation like so: -bookmarks=false`

Expand Down Expand Up @@ -791,8 +794,8 @@ Examples: pdfcpu grid out.pdf 1 10 in.pdf
Kiku4, Kiku5
AB, B40, Shikisen`

usageVersion = "usage: pdfcpu version [-v(erbose)|vv]"
usageLongVersion = "Print the pdfcpu version."
usageVersion = "usage: pdfcpu version"
usageLongVersion = "Print the pdfcpu version & build info."

usagePaper = "usage: pdfcpu paper"
usageLongPaper = "Print a list of supported paper sizes."
Expand Down
4 changes: 2 additions & 2 deletions pkg/api/example_test.go
Expand Up @@ -84,15 +84,15 @@ func ExampleMergeCreateFile() {
// Merge inFiles by concatenation in the order specified and write the result to out.pdf.
// out.pdf will be overwritten.
inFiles := []string{"in1.pdf", "in2.pdf"}
MergeCreateFile(inFiles, "out.pdf", nil)
MergeCreateFile(inFiles, "out.pdf", false, nil)
}

func ExampleMergeAppendFile() {

// Merge inFiles by concatenation in the order specified and write the result to out.pdf.
// If out.pdf already exists it will be preserved and serves as the beginning of the merge result.
inFiles := []string{"in1.pdf", "in2.pdf"}
MergeAppendFile(inFiles, "out.pdf", nil)
MergeAppendFile(inFiles, "out.pdf", false, nil)
}

func ExampleInsertPagesFile() {
Expand Down
2 changes: 1 addition & 1 deletion pkg/api/form.go
Expand Up @@ -629,7 +629,7 @@ func parseFormGroup(rd io.Reader) (*form.FormGroup, error) {

func mergeForms(outDir, fileName string, outFiles []string, conf *model.Configuration) error {
outFile := filepath.Join(outDir, fileName+".pdf")
if err := MergeCreateFile(outFiles, outFile, conf); err != nil {
if err := MergeCreateFile(outFiles, outFile, false, conf); err != nil {
return err
}
if log.CLIEnabled() {
Expand Down

0 comments on commit 125cee2

Please sign in to comment.