Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ Thanks goes to these wonderful people:
| :---: | :---: | :---: | :---: | :---: | :---: | :---: |
| [<img src="https://avatars1.githubusercontent.com/u/11322155?v=4" width="100px"/><br/><sub><b>Horst Rutter</b></sub>](https://github.com/hhrutter) | [<img src="https://avatars0.githubusercontent.com/u/5140211?v=4" width="100px"/><br/><sub><b>haldyr</b></sub>](https://github.com/haldyr) | [<img src="https://avatars3.githubusercontent.com/u/20608155?v=4" width="100px"/><br/><sub><b>Vyacheslav</b></sub>](https://github.com/SimePel) | [<img src="https://avatars1.githubusercontent.com/u/617459?v=4" width="100px"/><br/><sub><b>Erik Unger</b></sub>](https://github.com/ungerik) | [<img src="https://avatars1.githubusercontent.com/u/13079058?v=4" width="100px"/><br/><sub><b>Richard Wilkes</b></sub>](https://github.com/richardwilkes) | [<img src="https://avatars1.githubusercontent.com/u/16303386?s=400&v=4" width="100px"/><br/><sub><b>minenok-tutu</b></sub>](https://github.com/minenok-tutu) | [<img src="https://avatars0.githubusercontent.com/u/1965445?s=400&v=4" width="100px"/><br/><sub><b>Mateusz Burniak</b></sub>](https://github.com/matbur) |
| [<img src="https://avatars2.githubusercontent.com/u/1175110?s=400&v=4" width="100px"/><br/><sub><b>Dmitry Harnitski</b></sub>](https://github.com/dharnitski) | [<img src="https://avatars0.githubusercontent.com/u/1074083?s=400&v=4" width="100px"/><br/><sub><b>ryarnyah</b></sub>](https://github.com/ryarnyah) | [<img src="https://avatars0.githubusercontent.com/u/13267?s=400&v=4" width="100px"/><br/><sub><b>Sam Giffney</b></sub>](https://github.com/s01ipsist) | [<img src="https://avatars3.githubusercontent.com/u/32948066?s=400&v=4" width="100px"/><br /><sub><b>Carlos Eduardo Witte</b></sub>](https://github.com/cewitte) | [<img src="https://avatars1.githubusercontent.com/u/2374948?s=400&u=a36e5f8da8dc1c102bc4d283f25e4c61cae7f985&v=4" width="100px"/><br/><sub><b>minusworld</b></sub>](https://github.com/minusworld) | [<img src="https://avatars0.githubusercontent.com/u/18538487?s=400&u=b9e628dfc60f672a887be2ed04a791195829943e&v=4" width="100px"/><br/><sub><b>Witold Konior</b></sub>](https://github.com/jozuenoon) | [<img src="https://avatars0.githubusercontent.com/u/630151?s=400&v=4" width="100px"/><br/><sub><b>joonas.fi</b></sub>](https://github.com/joonas-fi) |
| [<img src="https://avatars3.githubusercontent.com/u/10349817?s=400&u=93bacb23bd2909d5b6c5b644a8d4cdd947422ee1&v=4" width="100px"/><br/><sub><b>Henrik Reinstädtler</b></sub>](https://github.com/henrixapp) | [<img src="https://avatars1.githubusercontent.com/u/72016286?s=400&v=4" width="100px"/><br/><sub><b>VMorozov-wh</b></sub>](https://github.com/VMorozov-wh) | [<img src="https://avatars0.githubusercontent.com/u/31929422?s=400&v=4" width="100px"/><br/><sub><b>Benoit KUGLER</b></sub>](https://github.com/benoitkugler) | [<img src="https://avatars.githubusercontent.com/u/704919?s=400&v=4" width="100px"/><br/><sub><b>Adam Greenhall</b></sub>](https://github.com/adamgreenhall) | [<img src="https://avatars.githubusercontent.com/u/5201812?s=400&u=8a0a9fca4560be71d4923299ddebf877854eea54&v=4" width="100px"/><br/><sub><b>moritamori</b></sub>](https://github.com/moritamori) | [<img src="https://avatars.githubusercontent.com/u/41904529?s=400&u=044396494285ad806e86d1936c390b3071ce57c0&v=4" width="100px"/><br/><sub><b>JanBaryla</b></sub>](https://github.com/JanBaryla)
| [<img src="https://avatars3.githubusercontent.com/u/10349817?s=400&u=93bacb23bd2909d5b6c5b644a8d4cdd947422ee1&v=4" width="100px"/><br/><sub><b>Henrik Reinstädtler</b></sub>](https://github.com/henrixapp) | [<img src="https://avatars1.githubusercontent.com/u/72016286?s=400&v=4" width="100px"/><br/><sub><b>VMorozov-wh</b></sub>](https://github.com/VMorozov-wh) | [<img src="https://avatars0.githubusercontent.com/u/31929422?s=400&v=4" width="100px"/><br/><sub><b>Benoit KUGLER</b></sub>](https://github.com/benoitkugler) | [<img src="https://avatars.githubusercontent.com/u/704919?s=400&v=4" width="100px"/><br/><sub><b>Adam Greenhall</b></sub>](https://github.com/adamgreenhall) | [<img src="https://avatars.githubusercontent.com/u/5201812?s=400&u=8a0a9fca4560be71d4923299ddebf877854eea54&v=4" width="100px"/><br/><sub><b>moritamori</b></sub>](https://github.com/moritamori) | [<img src="https://avatars.githubusercontent.com/u/41904529?s=400&u=044396494285ad806e86d1936c390b3071ce57c0&v=4" width="100px"/><br/><sub><b>JanBaryla</b></sub>](https://github.com/JanBaryla) | [<img src="https://avatars.githubusercontent.com/u/43145244?s=400&u=89a689f1a854ce0f57ae2a0333c82bfdc5723bb9&v=4" width="100px"/><br/><sub><b>TheDiscordian</b></sub>](https://github.com/TheDiscordian)


<!-- ALL-CONTRIBUTORS-LIST:END - Do not remove or modify this section -->
Expand Down
87 changes: 87 additions & 0 deletions pkg/api/stamp.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,93 @@ func AddWatermarksMapFile(inFile, outFile string, m map[int]*pdfcpu.Watermark, c
return AddWatermarksMap(f1, f2, m, conf)
}

// AddWatermarksSliceMap adds watermarks in m to corresponding pages in rs and writes the result to w.
func AddWatermarksSliceMap(rs io.ReadSeeker, w io.Writer, m map[int][]*pdfcpu.Watermark, conf *pdfcpu.Configuration) error {
if conf == nil {
conf = pdfcpu.NewDefaultConfiguration()
}
conf.Cmd = pdfcpu.ADDWATERMARKS

if len(m) == 0 {
return errors.New("pdfcpu: missing watermarks")
}

fromStart := time.Now()
ctx, durRead, durVal, durOpt, err := readValidateAndOptimize(rs, conf, fromStart)
if err != nil {
return err
}

from := time.Now()

if err = ctx.AddWatermarksSliceMap(m); err != nil {
return err
}

log.Stats.Printf("XRefTable:\n%s\n", ctx)

if conf.ValidationMode != pdfcpu.ValidationNone {
if err = ValidateContext(ctx); err != nil {
return err
}
}

durStamp := time.Since(from).Seconds()
fromWrite := time.Now()

if err = WriteContext(ctx, w); err != nil {
return err
}

durWrite := durStamp + time.Since(fromWrite).Seconds()
durTotal := time.Since(fromStart).Seconds()
logOperationStats(ctx, "watermark, write", durRead, durVal, durOpt, durWrite, durTotal)

return nil
}

// AddWatermarksSliceMapFile adds watermarks to corresponding pages in m of inFile and writes the result to outFile.
func AddWatermarksSliceMapFile(inFile, outFile string, m map[int][]*pdfcpu.Watermark, conf *pdfcpu.Configuration) (err error) {
var f1, f2 *os.File

if f1, err = os.Open(inFile); err != nil {
return err
}

tmpFile := inFile + ".tmp"
if outFile != "" && inFile != outFile {
tmpFile = outFile
log.CLI.Printf("writing %s...\n", outFile)
} else {
log.CLI.Printf("writing %s...\n", inFile)
}
if f2, err = os.Create(tmpFile); err != nil {
return err
}

defer func() {
if err != nil {
f2.Close()
f1.Close()
os.Remove(tmpFile)
return
}
if err = f2.Close(); err != nil {
return
}
if err = f1.Close(); err != nil {
return
}
if outFile == "" || inFile == outFile {
if err = os.Rename(tmpFile, inFile); err != nil {
return
}
}
}()

return AddWatermarksSliceMap(f1, f2, m, conf)
}

// AddWatermarks adds watermarks to all pages selected in rs and writes the result to w.
func AddWatermarks(rs io.ReadSeeker, w io.Writer, selectedPages []string, wm *pdfcpu.Watermark, conf *pdfcpu.Configuration) error {
if conf == nil {
Expand Down
99 changes: 94 additions & 5 deletions pkg/api/test/stampVersatile_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ func TestAlternatingPageNumbersViaWatermarkMap(t *testing.T) {
t.Fatalf("%s: %v\n", msg, err)
}

// Prepare a map of watermarks.
// This maps pages to corresponding watermarks.
// Any page may be assigned a single watermark of type text, image or PDF.
m := map[int]*pdfcpu.Watermark{}

// Start stamping with page 2.
Expand Down Expand Up @@ -66,7 +69,7 @@ func TestAlternatingPageNumbersViaWatermarkMap(t *testing.T) {
}

// Add a stamp with the creation date on the center of the bottom of every page.
text := fmt.Sprintf("Creation date: %v", time.Now().Format("2006-01-02 15:04"))
text := fmt.Sprintf("%%p of %%P - Creation date: %v", time.Now().Format("2006-01-02 15:04"))
if err := api.AddTextWatermarksFile(outFile, outFile, nil, true, text, "fo:Roboto-Regular, points:12, sc:1 abs, pos:bc, off:0 10, rot:0", nil); err != nil {
t.Fatalf("%s %s: %v\n", msg, outFile, err)
}
Expand Down Expand Up @@ -119,7 +122,7 @@ func TestAlternatingPageNumbersViaWatermarkMapLowLevel(t *testing.T) {
}

// Add a stamp with the creation date on the center of the bottom of every page.
text := fmt.Sprintf("Creation date: %v", time.Now().Format("2006-01-02 15:04"))
text := fmt.Sprintf("%%p of %%P - Creation date: %v", time.Now().Format("2006-01-02 15:04"))
wm, err := api.TextWatermark(text, "fo:Roboto-Regular, points:12, sc:1 abs, pos:bc, off:0 10, rot:0", true, false, unit)
if err != nil {
t.Fatalf("%s %s: %v\n", msg, outFile, err)
Expand All @@ -143,6 +146,81 @@ func TestAlternatingPageNumbersViaWatermarkMapLowLevel(t *testing.T) {
}
}

func TestAlternatingPageNumbersViaWatermarkSliceMap(t *testing.T) {
msg := "TestAlternatingPageNumbersViaWatermarkSliceMap"
inFile := filepath.Join(inDir, "WaldenFull.pdf")
outFile := filepath.Join("../../samples/stamp/mixed", "AlternatingPageNumbersViaWatermarkSliceMap.pdf")

pageCount, err := api.PageCountFile(inFile)
if err != nil {
t.Fatalf("%s: %v\n", msg, err)
}

m := map[int][]*pdfcpu.Watermark{}
opacity := 1.0
onTop := true // All stamps!
update := false
unit := pdfcpu.POINTS

// Prepare a map of watermark slices.
// This maps pages to corresponding watermarks.
// Each page may be assigned an arbitrary number of watermarks of type text, image or PDF.
for i := 2; i <= pageCount; i++ {

wms := []*pdfcpu.Watermark{}

// 1st watermark on page
// For odd page numbers add a blue stamp on the bottom right corner using Roboto-Regular
// For even page numbers add a green stamp on the bottom left corner using Times-Italic
text := fmt.Sprintf("%d of %d", i, pageCount)
fontName := "Times-Italic"
pos := "bl"
dx := 10
fillCol := "#008000"
if i%2 > 0 {
fontName = "Roboto-Regular"
pos = "br"
dx = -10
fillCol = "#0000E0"
}
desc := fmt.Sprintf("font:%s, points:12, sc:1 abs, pos:%s, off:%d 10, fillcol:%s, rot:0, op:%f", fontName, pos, dx, fillCol, opacity)
wm, err := api.TextWatermark(text, desc, onTop, update, unit)
if err != nil {
t.Fatalf("%s: %v\n", msg, err)
}
wms = append(wms, wm)

// 2nd watermark on page
// Add a stamp with the creation date on the center of the bottom of every page.
text = fmt.Sprintf("%%p of %%P - Creation date: %v", time.Now().Format("2006-01-02 15:04"))
desc = fmt.Sprintf("fo:Roboto-Regular, points:12, sc:1 abs, pos:bc, off:0 10, rot:0, op:%f", opacity)
wm, err = api.TextWatermark(text, desc, onTop, update, unit)
if err != nil {
t.Fatalf("%s: %v\n", msg, err)
}
wms = append(wms, wm)

// 3rd watermark on page
// Add a "Draft" stamp with opacity 0.6 along the 1st diagonale in light blue using Courier.
text = "Draft"
desc = fmt.Sprintf("fo:Courier, sc:.9, fillcol:#00aacc, op:%f", opacity)
wm, err = api.TextWatermark(text, desc, onTop, update, unit)
if err != nil {
t.Fatalf("%s: %v\n", msg, err)
}
wms = append(wms, wm)

m[i] = wms
}

// Apply all watermarks in one Go.
// Assumption: All watermarks share the same opacity and onTop (all stamps or watermarks).
// If you cannot ensure this you have to do something along the lines of func TestAlternatingPageNumbersViaWatermarkMap
if err := api.AddWatermarksSliceMapFile(inFile, outFile, m, nil); err != nil {
t.Fatalf("%s %s: %v\n", msg, outFile, err)
}
}

func TestImagesTextAndPDFWMViaWatermarkMap(t *testing.T) {
msg := "TestImagesTextAndPDFWMViaWatermarkMap"
inFile := filepath.Join(inDir, "WaldenFull.pdf")
Expand All @@ -156,33 +234,44 @@ func TestImagesTextAndPDFWMViaWatermarkMap(t *testing.T) {
m := map[int]*pdfcpu.Watermark{}
fileNames := imageFileNames(t, "../../../resources")

opacity := 1.0
onTop := true // All stamps!
update := false
unit := pdfcpu.POINTS

// Apply a mix of image, text and PDF watermarks in one go.
for i := 1; i <= pageCount; i++ {
if i <= len(fileNames) {
wm, err := api.ImageWatermark(fileNames[i-1], "pos:bl, sc:.25, rot:0", true, false, unit)
desc := fmt.Sprintf("pos:bl, sc:.25, rot:0, op:%f", opacity)
wm, err := api.ImageWatermark(fileNames[i-1], desc, onTop, update, unit)
if err != nil {
t.Fatalf("%s: %v\n", msg, err)
}
m[i] = wm
continue
}

if i%2 > 0 {
wm, err := api.PDFWatermark(inFile+":1", "sc:.25, pos:br, rot:0", true, false, unit)
desc := fmt.Sprintf("sc:.25, pos:br, rot:0, op:%f", opacity)
wm, err := api.PDFWatermark(inFile+":1", desc, onTop, update, unit)
if err != nil {
t.Fatalf("%s: %v\n", msg, err)
}
m[i] = wm
continue
}
wm, err := api.TextWatermark("Even page number", "rot:0", true, false, unit)

desc := fmt.Sprintf("rot:0, op:%f", opacity)
wm, err := api.TextWatermark("Even page number", desc, onTop, update, unit)
if err != nil {
t.Fatalf("%s: %v\n", msg, err)
}
m[i] = wm
}

// Apply all watermarks in one Go.
// Assumption: All watermarks share the same opacity and onTop (all stamps or watermarks).
// If you cannot ensure this you have to do something along the lines of func TestAlternatingPageNumbersViaWatermarkMap
if err := api.AddWatermarksMapFile(inFile, outFile, m, nil); err != nil {
t.Fatalf("%s %s: %v\n", msg, outFile, err)
}
Expand Down
Loading