Skip to content

Commit

Permalink
bumped up to version 0.2.5, with --force and slightly improved output
Browse files Browse the repository at this point in the history
  • Loading branch information
gernotstarke committed Sep 9, 2024
1 parent 528d5d8 commit de5fade
Show file tree
Hide file tree
Showing 6 changed files with 287 additions and 187 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ BINARY_UNIX=$(BINARY_NAME)_unix
BUILDTIME=$(shell date -u +'%Y %b %d %H:%M')

# Build flags
LDFLAGS=-ldflags "-X 'pdfminion/internal/config.BuildTime=$(BUILDTIME)'"
LDFLAGS=-ldflags "-s -w -X 'pdfminion/internal/config.BuildTime=$(BUILDTIME)'"

# Install directory
INSTALL_DIR=/usr/local/bin
Expand Down
7 changes: 7 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
## PDFminion version history

0.2.3 add --force flag to allow existing target directory
0.2.2
0.2.1 add more command line flags, -h, -s
0.2.0 re-structured packages, add command line flags -t
0.1.0 minimal viable product, make it work.
208 changes: 24 additions & 184 deletions cmd/pdfminion/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,31 +9,16 @@ minimal viable product version of PDFminion:
*/
import (
"fmt"
"github.com/pdfcpu/pdfcpu/pkg/api"
"github.com/pdfcpu/pdfcpu/pkg/pdfcpu/model"
"github.com/pdfcpu/pdfcpu/pkg/pdfcpu/types"
"io"
"log"
"os"
"path/filepath"
"pdfminion/internal/config"
"pdfminion/internal/util"
"pdfminion/internal/pdf"
"sort"
"strconv"
)

type singleFileToProcess struct {
filename string
pageCount int
origByteCount int64
}

const blankPageNote = "Diese Seite bleibt\n absichtlich frei"
const pageNrPrefix = ""
const chapterPrefix = "Kap."
const chapterPageSeparator = " - "

func main() {

// TODO: simplify cfg handling -> put all flag handling (parsing, eval + error handling) in config package
cfg := config.New()
cfg.ParseFlags()

Expand All @@ -42,187 +27,42 @@ func main() {
os.Exit(1)
}

// count PDFs in current directory
// abort, if no PDF file is present
var nrOfCandidatePDFs int

// collect all candidate PDFs with Glob
// "candidate" means, PDF has not been validated
pattern := filepath.Join(cfg.SourceDir, "*.pdf")
files, err := filepath.Glob(pattern)
if err != nil {
log.Println("Error:", err)
}
// some technical stuff needs to be initialized before we can handle PDF files
pdf.InitializePDFInternals()

nrOfCandidatePDFs = len(files)
files, err := pdf.CollectCandidatePDFs(cfg)

// exit if no PDF files can be found
if nrOfCandidatePDFs == 0 {
fmt.Fprintf(os.Stderr, "error: no PDF files found in %s\n", cfg.SourceDir)
os.Exit(1)
}
// sort, validate, copy

// sort files alphabetically (as we cannot assume any sort order from `os.Glob)
// TODO: move sort to processFiles!!
sort.Slice(files, func(i, j int) bool {
return files[i] < files[j]
})

// create slice of singleFileToProcess of required length
pdfFiles := make([]singleFileToProcess, nrOfCandidatePDFs)

// initialize slice of singleFileToProcess
// move over only the validated pdf files into pdfFiles variable

var originalFile, newFile *os.File

// relax the validation mode, otherwise asciidoc generated pdfs
// will break and lead to errors
// see https://github.com/pdfcpu/pdfcpu/issues/498
// ensure we process only valid PDFs
// * correct file format
// * at least one page
// TODO: handle zero-page PDFs
// TODO: validatePDFs should return error if error...
pdfFiles, nrOfValidPDFs := pdf.ValidatePDFs(files)

relaxedConf := model.NewDefaultConfiguration()
relaxedConf.ValidationMode = model.ValidationRelaxed
// copy all valid PDFs to target directory
// update pdfFiles slice to contain full path to target files
err = pdf.CopyValidatedPDFs(pdfFiles, cfg.SourceDir, cfg.TargetDir, cfg.Force)

var nrOfValidPDFs = 0
for i := 0; i < nrOfCandidatePDFs; i++ {

// check if file-i is a valid PDF with pdfcpu.api
// use default configuration for pdfcpu ("nil")
err = api.ValidateFile(files[i], relaxedConf)
if err != nil {
log.Printf("%v is no valid PDF, %v\n", files[i], err)
} else {

// we have a valid PDF

nrOfValidPDFs++

// count the pages of this particular file
// TODO: handle zero-page PDFs
pdfFiles[i].pageCount, err = api.PageCountFile(files[i])

if err != nil {
log.Printf("error counting pages in %v\n", files[i])
} else {

// create target filePath
pdfFiles[i].filename = filepath.Join(cfg.TargetDir, filepath.Base(files[i]))

// copy that particular file to _target
// Open original file
originalFile, err = os.Open(files[i])
if err != nil {
log.Fatal(err)
}
defer originalFile.Close()

// Create new file
newFile, err = os.Create(pdfFiles[i].filename)
if err != nil {
log.Fatal(err)
}
defer newFile.Close()

//This will copy.
bytesWritten, err := io.Copy(newFile, originalFile)
if err != nil {
log.Fatal(err)
}
pdfFiles[i].origByteCount = bytesWritten
}

}
if err != nil {
fmt.Fprintf(os.Stderr, "Error during copy, aborting %v\n", err)
os.Exit(1)
}

// TODO: log only in DEBUG mode
log.Printf("%v", pdfFiles)

// evenify: add empty page to every file with even pagecount
for i := 0; i < nrOfValidPDFs; i++ {
if !util.IsEven(pdfFiles[i].pageCount) {
// add single blank page at the end of the file
_ = api.InsertPagesFile(pdfFiles[i].filename, "", []string{strconv.Itoa(pdfFiles[i].pageCount)}, false, relaxedConf)

pdfFiles[i].pageCount++

// TODO: add huge diagonal marker text "deliberately left blank" to new blank page

onTop := true
update := false

wm, err := api.TextWatermark(blankPageNote, "font:Helvetica, points:48, col: 0.5 0.6 0.5, rot:45, sc:1 abs",
onTop, update, types.POINTS)
if err != nil {
log.Println("Error creating watermark configuration %v: %v", wm, err)
} else {

err = api.AddWatermarksFile(pdfFiles[i].filename, "", []string{strconv.Itoa(pdfFiles[i].pageCount)}, wm,
relaxedConf)

if err != nil {
log.Println("error stamping blank page in file %v: %v", pdfFiles[i].filename, err)
}

}
log.Println("File %s was evenified", pdfFiles[i].filename)
}
}
// Evenify: add empty page to every file with even page count
pdf.Evenify(nrOfValidPDFs, pdfFiles)

// add page numbers
pdf.AddPageNumbersToAllFiles(nrOfValidPDFs, pdfFiles)

// currentOffset is the _previous_ pagenumber
var currentOffset = 0

for i := 0; i < nrOfValidPDFs; i++ {
var currentFilePageCount = pdfFiles[i].pageCount
var currentFileName = pdfFiles[i].filename
log.Printf("File %s starts %d, ends %d", currentFileName, currentOffset+1,
currentOffset+currentFilePageCount)

err := api.AddWatermarksMapFile(currentFileName,
"",
watermarkConfigurationForFile(i+1,
currentOffset,
currentFilePageCount),
relaxedConf)
if err != nil {
log.Println(err)
}
currentOffset += currentFilePageCount
}

}

// create a map[int] of TextWatermark configurations
func watermarkConfigurationForFile(chapterNr, previousPageNr, pageCount int) map[int]*model.Watermark {

wmcs := make(map[int]*model.Watermark)

for page := 1; page <= (pageCount); page++ {
var currentPageNr = previousPageNr + page
var chapterStr = chapterPrefix + strconv.Itoa(chapterNr)
var pageStr = pageNrPrefix + strconv.Itoa(currentPageNr)

wmcs[page], _ = api.TextWatermark(chapterStr+chapterPageSeparator+pageStr,
waterMarkDescription(currentPageNr), true, false, types.POINTS)
}
return wmcs
}

const fontColorSize = "font:Helvetica, points:16, scale: 0.9 abs, rot: 0, color: 0.5 0.5 0.5"

// creates a pdfcpu TextWatermark description
func waterMarkDescription(pageNumber int) string {

const evenPos string = "position: bl"
const evenOffset string = "offset: 20 6"
const oddPos string = "position: br"
const oddOffset string = "offset: -20 6"

positionAndOffset := ""

if util.IsEven(pageNumber) {
positionAndOffset = evenPos + "," + evenOffset
} else {
positionAndOffset = oddPos + "," + oddOffset
}
return fontColorSize + "," + positionAndOffset
}
13 changes: 11 additions & 2 deletions internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,21 +14,27 @@ type Config struct {
TargetDir string
showHelp bool
showVersion bool
Force bool
}

const (
defaultSourceDir = "_pdfs"
defaultTargetDir = "_target"
Version = "0.2.1"
Version = "0.2.5b"
)

const PageNrPrefix = ""
const ChapterPrefix = "Kap."
const ChapterPageSeparator = " - "

// BuildTime will be injected at build time
var BuildTime string

func New() *Config {
return &Config{
SourceDir: defaultSourceDir,
TargetDir: defaultTargetDir,
Force: false,
}
}

Expand All @@ -37,6 +43,7 @@ func (c *Config) ParseFlags() {
flag.StringVar(&c.SourceDir, "source", defaultSourceDir, "Specify the source directory")
flag.StringVar(&c.TargetDir, "t", defaultTargetDir, "Specify the target directory")
flag.StringVar(&c.TargetDir, "target", defaultTargetDir, "Specify the target directory")
flag.BoolVar(&c.Force, "force", false, "Skips check of empty target directory, forces overwrite of existing files")
flag.BoolVar(&c.showHelp, "h", false, "Show help information")
flag.BoolVar(&c.showHelp, "help", false, "Show help information")
flag.BoolVar(&c.showVersion, "v", false, "Show version information")
Expand Down Expand Up @@ -98,6 +105,7 @@ func (c *Config) printHelp() {
fmt.Println("\n\nUsage:")
fmt.Printf(" -s, --source string\n\tSpecify the source directory (default \"%s\")\n", defaultSourceDir)
fmt.Printf(" -t, --target string\n\tSpecify the target directory (default \"%s\")\n", defaultTargetDir)
fmt.Printf(" --force string\n\tSkips check of empty target directory, forces overwrite of existing files (default false)\n")
fmt.Println(" -h, --help, ?, -?, help\n\tShow this help message")
fmt.Println(" -v, --version\n\tShow version information")
}
Expand Down Expand Up @@ -145,12 +153,13 @@ func (c *Config) validateTargetDir() error {
if err := os.MkdirAll(c.TargetDir, os.ModePerm); err != nil {
return fmt.Errorf("error creating directory %s: %v", c.TargetDir, err)
}
} else {
} else if !c.Force {
// Check if the directory is empty
files, err := ioutil.ReadDir(c.TargetDir)
if err != nil {
return fmt.Errorf("error reading directory %s: %v", c.TargetDir, err)
}

if len(files) > 0 {
return fmt.Errorf("target directory %s is not empty", c.TargetDir)
}
Expand Down
Loading

0 comments on commit de5fade

Please sign in to comment.