github.com/pdfcpu/pdfcpu@v0.11.1/pkg/api/test/stamp_test.go (about)

     1  /*
     2  Copyright 2020 The pdfcpu Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8  	http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package test
    18  
    19  import (
    20  	"fmt"
    21  	"path/filepath"
    22  	"testing"
    23  
    24  	"github.com/pdfcpu/pdfcpu/pkg/api"
    25  	"github.com/pdfcpu/pdfcpu/pkg/pdfcpu"
    26  	"github.com/pdfcpu/pdfcpu/pkg/pdfcpu/types"
    27  )
    28  
    29  func testAddWatermarks(t *testing.T, msg, inFile, outFile string, selectedPages []string, mode, modeParam, desc string, onTop bool) {
    30  	t.Helper()
    31  	inFile = filepath.Join(inDir, inFile)
    32  	s := "watermark"
    33  	if onTop {
    34  		s = "stamp"
    35  	}
    36  	outFile = filepath.Join(samplesDir, s, mode, outFile)
    37  
    38  	var err error
    39  	switch mode {
    40  	case "text":
    41  		err = api.AddTextWatermarksFile(inFile, outFile, selectedPages, onTop, modeParam, desc, nil)
    42  	case "image":
    43  		err = api.AddImageWatermarksFile(inFile, outFile, selectedPages, onTop, modeParam, desc, nil)
    44  	case "pdf":
    45  		err = api.AddPDFWatermarksFile(inFile, outFile, selectedPages, onTop, modeParam, desc, nil)
    46  	}
    47  	if err != nil {
    48  		t.Fatalf("%s %s: %v\n", msg, outFile, err)
    49  	}
    50  	if err := api.ValidateFile(outFile, nil); err != nil {
    51  		t.Fatalf("%s: %v\n", msg, err)
    52  	}
    53  }
    54  
    55  func TestAddWatermarks(t *testing.T) {
    56  	for _, tt := range []struct {
    57  		msg             string
    58  		inFile, outFile string
    59  		selectedPages   []string
    60  		mode            string
    61  		modeParm        string
    62  		wmConf          string
    63  	}{
    64  
    65  		// Avoid font embedding for CJK fonts like so:
    66  
    67  		// Any font name ending with GB2312 will be recognized as using HANS:
    68  
    69  		// {"TestWatermarkText",
    70  		// 	"sample.pdf",
    71  		// 	"chinese.pdf",
    72  		// 	[]string{"1-"},
    73  		// 	"text",
    74  		// 	"测试中文字体水印增加的文件大小\n2023-10-16",
    75  		// 	"font: KaiTi_GB2312, points: 36, scale: 1 abs, color: #ff0000, op: 0.3, ro: 30"},
    76  
    77  		// Configure script manually:
    78  
    79  		// {"TestWatermarkText",
    80  		// 	"sample.pdf",
    81  		// 	"chinese1.pdf",
    82  		// 	[]string{"1-"},
    83  		// 	"text",
    84  		// 	"测试中文字体水印增加的文件大小\n2023-10-16",
    85  		// 	"font: KaiTi_GB2312, script: hans,  points: 36, scale: 1 abs, color: #ff0000, op: 0.3, ro: 30"},
    86  
    87  		{"TestWatermarkText",
    88  			"Walden.pdf",
    89  			"TextDefaults.pdf",
    90  			[]string{"1-"},
    91  			"text",
    92  			"A simple text watermark using defaults:\n" +
    93  				"\"font:Helvetica, points:24, aligntext:center,\n" +
    94  				"position:c, offset:0 0, scale:0.5 rel, diagonal:1,\n" +
    95  				"opacity:1, rendermode:0, fillcolor: 0.5 0.5 0.5,\n" +
    96  				"strokecol: 0.5 0.5 0.5\"",
    97  			""},
    98  
    99  		{"TestWatermarkText",
   100  			"Walden.pdf",
   101  			"TextDefaultsAbbr.pdf",
   102  			[]string{"1-"},
   103  			"text",
   104  			`A simple text watermark using defaults:
   105  					Unique abbreviations also work:
   106  					"fo:Helvetica, poi:24, align:c,
   107  					pos:c, off:0 0, scale:0.5 rel, d:1,
   108  					op:1, mode:0, fillc: 0.5 0.5 0.5,
   109  					strokec: #808080"`,
   110  			""},
   111  
   112  		{"TestWatermarkText",
   113  			"Walden.pdf",
   114  			"TextAlongLeftBorder.pdf",
   115  			[]string{"1-"},
   116  			"text",
   117  			"Welcome to pdfcpu",
   118  			"pos:l, off:0 0, rot:-90"},
   119  
   120  		{"TestWatermarkText",
   121  			"Walden.pdf",
   122  			"TextPagenumbers.pdf",
   123  			[]string{"1-"},
   124  			"text",
   125  			"Page %p of %P",
   126  			"scale:1 abs, pos:bc, rot:0"},
   127  
   128  		{"TestWatermarkText",
   129  			"Walden.pdf",
   130  			"TextRenderMode0.pdf",
   131  			[]string{"1-"},
   132  			"text",
   133  			"Rendermode 0 fills text using fill color.\n" +
   134  				"\"rendermode\" or \"mode\" works - also abbreviated: \n" +
   135  				"\"mode:0, fillc:#3277d3, rot:0, scale:.8\"",
   136  			"mode:0, fillc:#3277d3, rot:0, scale:.8"},
   137  
   138  		{"TestWatermarkText",
   139  			"Walden.pdf",
   140  			"TextRenderMode1.pdf",
   141  			[]string{"1-"},
   142  			"text",
   143  			"Rendermode 1 strokes text using stroke color.\n" +
   144  				"\"rendermode\" or \"mode\" works - also abbreviated: \n" +
   145  				"\"mo:1, strokec:#335522, rot:0, scale:.8\"",
   146  			"mo:1, strokec:#335522, rot:0, scale:.8"},
   147  
   148  		{"TestWatermarkText",
   149  			"Walden.pdf",
   150  			"TextRenderMode2.pdf",
   151  			[]string{"1-"},
   152  			"text",
   153  			"Rendermode 2 strokes text using stroke color\n" +
   154  				"and fills text using fill color\n" +
   155  				"\"rendermode\" or \"mode\" works - also abbreviated: \n" +
   156  				"\"re:2, fillc:#3277d3, strokec:#335522, rot:0, scale:.8\"",
   157  			"re:2, fillc:#3277d3, strokec:#335522, rot:0, scale:.8"},
   158  
   159  		{"TestWatermarkText",
   160  			"Walden.pdf",
   161  			"TextAlignLeft.pdf",
   162  			[]string{"1-"},
   163  			"text",
   164  			"Here we have\n" +
   165  				"some left aligned text lines\n" +
   166  				"\"align:l, fillc:#3277d3, rot:0\"",
   167  			"align:l, fillc:#3277d3, rot:0"},
   168  
   169  		{"TestWatermarkText",
   170  			"Walden.pdf",
   171  			"TextAlignRight.pdf",
   172  			[]string{"1-"},
   173  			"text",
   174  			"Here we have\n" +
   175  				"some right aligned text lines\n" +
   176  				"with background color\n" +
   177  				"\"align:l, fillc:#3277d3, bgcol:#f7e6c7, rot:0\"",
   178  			"align:r, fillc:#3277d3, bgcol:#f7e6c7, rot:0"},
   179  
   180  		{"TestWatermarkText",
   181  			"Walden.pdf",
   182  			"TextAlignCenter.pdf",
   183  			[]string{"1-"},
   184  			"text",
   185  			"Here we have\n" +
   186  				"some centered text lines\n" +
   187  				"with background color\n" +
   188  				"\"fillc:#3277d3, bgcol:#beded9, rot:0\"",
   189  			"fillc:#3277d3, bgcol:#beded9, rot:0"},
   190  
   191  		{"TestWatermarkText",
   192  			"Walden.pdf",
   193  			"TextAlignJustify.pdf",
   194  			[]string{"1-"},
   195  			"text",
   196  			"Here we have\n" +
   197  				"some justified text lines\n" +
   198  				"with background color\n" +
   199  				"\"al:j, fillc:#3277d3, bgcol:#000000, rot:0\"",
   200  			"al:j, fillc:#3277d3, bgcol:#000000, rot:0"},
   201  
   202  		{"TestWatermarkText",
   203  			"Walden.pdf",
   204  			"TextScaleRel25.pdf",
   205  			[]string{"1-"},
   206  			"text",
   207  			"Relative scale factor: .25\n" +
   208  				"scales relative to page dimensions.\n" +
   209  				"\"scale:.25 rel, fillc:#3277d3, rot:0\"",
   210  			"scale:.25 rel, fillc:#3277d3, rot:0"},
   211  
   212  		{"TestWatermarkText",
   213  			"Walden.pdf",
   214  			"TextScaleRel50.pdf",
   215  			[]string{"1-"},
   216  			"text",
   217  			"Relative scale factor: .5\n" +
   218  				"scales relative to page dimensions.\n" +
   219  				"\"scale:.5, fillc:#3277d3, rot:0\"",
   220  			"scale:.5, fillc:#3277d3, rot:0"},
   221  
   222  		{"TestWatermarkText",
   223  			"Walden.pdf",
   224  			"TextScaleRel100.pdf",
   225  			[]string{"1-"},
   226  			"text",
   227  			"Relative scale factor: 1\n" +
   228  				"scales relative to page dimensions.\n" +
   229  				"\"scale:1, fillc:#3277d3, rot:0\"",
   230  			"scale:1, fillc:#3277d3, rot:0"},
   231  
   232  		{"TestWatermarkText",
   233  			"Walden.pdf",
   234  			"TextScaleAbs50.pdf",
   235  			[]string{"1-"},
   236  			"text",
   237  			"Absolute scale factor: .5\n" +
   238  				"scales fontsize\n" +
   239  				"(here using the 24 points default)\n" +
   240  				"\"scale:.5 abs, font:Courier, rot:0\"",
   241  			"scale:.5 abs, font:Courier, rot:0"},
   242  
   243  		{"TestWatermarkText",
   244  			"Walden.pdf",
   245  			"TextScaleAbs100.pdf",
   246  			[]string{"1-"},
   247  			"text",
   248  			"Absolute scale factor: 1\n" +
   249  				"scales fontsize\n" +
   250  				"(here using the 24 points default)\n" +
   251  				"\"scale:1 abs, font:Courier, rot:0\"",
   252  			"scale:1 abs, font:Courier, rot:0"},
   253  
   254  		{"TestWatermarkText",
   255  			"Walden.pdf",
   256  			"TextScaleAbs150.pdf",
   257  			[]string{"1-"},
   258  			"text",
   259  			"Absolute scale factor: 1.5\n" +
   260  				"scales fontsize\n" +
   261  				"(here using the 24 points default)\n" +
   262  				"\"scale:1.5 abs, font:Courier, rot:0\"",
   263  			"scale:1.5 abs, font:Courier, rot:0"},
   264  
   265  		{"TestWatermarkText",
   266  			"Walden.pdf",
   267  			"TextPosBotLeft.pdf",
   268  			[]string{"1-"},
   269  			"text",
   270  			"Positioning using anchors:\n" +
   271  				"bottom left corner with left alignment\n" +
   272  				"\"pos:bl, bgcol:#f7e6c7, rot:0\"",
   273  			"pos:bl, bgcol:#f7e6c7, rot:0"},
   274  
   275  		{"TestWatermarkText",
   276  			"Walden.pdf",
   277  			"TextPosBotRightWithOffset.pdf",
   278  			[]string{"1-"},
   279  			"text",
   280  			"Positioning using anchors and offset:\n" +
   281  				"bottom right corner with right alignment\n" +
   282  				"\"pos:br, off: -10 10, align:r, bgcol:#f7e6c7, rot:0\"",
   283  			"pos:br, off: -10 10, align:r, bgcol:#f7e6c7, rot:0"},
   284  
   285  		{"TestWatermarkText",
   286  			"Walden.pdf",
   287  			"TextOffAndRot.pdf",
   288  			[]string{"1-"},
   289  			"text",
   290  			"Confidential\n\"scale:1 abs, points:20, pos:c, off:0 50, fillc:#000000, rot:20\"",
   291  			"scale:1 abs, points:20, pos:c, off:0 50, fillc:#000000, rot:20"},
   292  
   293  		{"TestWatermarkText",
   294  			"Walden.pdf",
   295  			"TextMargins1Value.pdf",
   296  			[]string{"1-"},
   297  			"text",
   298  			"Set all margins:\n" +
   299  				"(needs \"bgcol\")\n" +
   300  				"\"margins: 10, fillc:#3277d3, bgcol:#beded9, rot:0\"",
   301  			"margins: 10,fillc:#3277d3, bgcol:#beded9, rot:0"},
   302  
   303  		{"TestWatermarkText",
   304  			"Walden.pdf",
   305  			"TextMargins2Values.pdf",
   306  			[]string{"1-"},
   307  			"text",
   308  			"Set top/bottom and left/right margins:\n" +
   309  				"(needs \"bgcol\")\n" +
   310  				"\"ma: 5 10, fillc:#3277d3, bgcol:#beded9, rot:0\"",
   311  			"ma: 5 10, fillc:#3277d3, bgcol:#beded9, rot:0"},
   312  
   313  		{"TestWatermarkText",
   314  			"Walden.pdf",
   315  			"TextMargins3Values.pdf",
   316  			[]string{"1-"},
   317  			"text",
   318  			"Set top, left/right and  bottom margins:\n" +
   319  				"(needs \"bgcol\")\n" +
   320  				"\"ma: 5 10 15, fillc:#3277d3, bgcol:#beded9, rot:0\"",
   321  			"ma: 5 10 15, fillc:#3277d3, bgcol:#beded9, rot:0"},
   322  
   323  		{"TestWatermarkText",
   324  			"Walden.pdf",
   325  			"TextMargins4Values.pdf",
   326  			[]string{"1-"},
   327  			"text",
   328  			"Set all margins individually:\n" +
   329  				"(needs \"bgcol\")\n" +
   330  				"\"ma: 5 10 15 20, fillc:#3277d3, bgcol:#beded9, rot:0\"",
   331  			"ma: 5 10 15 20, fillc:#3277d3, bgcol:#beded9, rot:0"},
   332  
   333  		{"TestWatermarkText",
   334  			"Walden.pdf",
   335  			"TextRoundCornersAndBorder5.pdf",
   336  			[]string{"1-"},
   337  			"text",
   338  			"Set round corners and border:\n" +
   339  				"(needs \"bgcol\" and a border)\n" +
   340  				"round corner effect depends on border width\n" +
   341  				"\"border: 5 round, fillc:#3277d3, bgcol:#beded9, rot:0\"",
   342  			"border: 5 round, fillc:#3277d3, bgcol:#beded9, rot:0"},
   343  
   344  		{"TestWatermarkText",
   345  			"Walden.pdf",
   346  			"TextRoundCornersAndBorder10.pdf",
   347  			[]string{"1-"},
   348  			"text",
   349  			"Set round corners and border:\n" +
   350  				"(needs \"bgcol\" and a border)\n" +
   351  				"round corner effect depends on border width\n" +
   352  				"\"border: 10 round, fillc:#3277d3, bgcol:#beded9, rot:0\"",
   353  			"border: 10 round, fillc:#3277d3, bgcol:#beded9, rot:0"},
   354  
   355  		{"TestWatermarkText",
   356  			"Walden.pdf",
   357  			"TextRoundCornersAndColoredBorder.pdf",
   358  			[]string{"1-"},
   359  			"text",
   360  			"Set round corners and colored border:\n" +
   361  				"(needs \"bgcol\")\n" +
   362  				"round corner effect depends on border width\n" +
   363  				"\"border: 10 round #f7e6c7, fillc:#3277d3, bgcol:#beded9, rot:0\"",
   364  			"border: 10 round #f7e6c7, fillc:#3277d3, bgcol:#beded9, rot:0"},
   365  
   366  		{"TestWatermarkText",
   367  			"Walden.pdf",
   368  			"TextMarginsAndColoredBorder.pdf",
   369  			[]string{"1-"},
   370  			"text",
   371  			"Set margins and colored border:\n" +
   372  				"(needs \"bgcol\")\n" +
   373  				"\"ma: 10, bo: 5 .3 .7 .7, fillc:#3277d3, bgcol:#beded9, rot:0\"",
   374  			"ma: 10, bo: 5 .3 .7 .7, fillc:#3277d3, bgcol:#beded9, rot:0"},
   375  
   376  		{"TestWatermarkText",
   377  			"Walden.pdf",
   378  			"TextMarginsRoundCornersAndColoredBorder.pdf",
   379  			[]string{"1-"},
   380  			"text",
   381  			"Set margins and round colored border:\n" +
   382  				"(needs \"bgcol\")\n" +
   383  				"round corner effect depends on border width\n" +
   384  				"\"ma: 5, bo: 7 round .3 .7 .7, fillc:#3277d3, bgcol:#beded9, rot:0\"",
   385  			"ma: 5, bo: 7 round .3 .7 .7, fillc:#3277d3, bgcol:#beded9, rot:0"},
   386  
   387  		// Add image watermark to inFile starting at page 1 using no rotation.
   388  		{"TestWatermarkImage",
   389  			"Walden.pdf",
   390  			"ImageRotate90.pdf",
   391  			[]string{"1-"},
   392  			"image",
   393  			filepath.Join(resDir, "logoSmall.png"),
   394  			"scale:.25, rot:90"},
   395  
   396  		// Add image watermark to inFile for all pages using defaults..
   397  		{"TestWatermarkImage2",
   398  			"Walden.pdf",
   399  			"ImagePosBottomLeftWithOffset.pdf",
   400  			nil,
   401  			"image",
   402  			filepath.Join(resDir, "logoSmall.png"),
   403  			"scale:.1, pos:bl, off:15 20, rot:0"},
   404  
   405  		// Add image stamp to inFile using absolute scaling and a rotation of 45 degrees.
   406  		{"TestStampImageAbsScaling",
   407  			"Walden.pdf",
   408  			"ImageAbsScaling.pdf",
   409  			[]string{"1-"},
   410  			"image",
   411  			filepath.Join(resDir, "logoSmall.png"),
   412  			"scale:.33 abs, rot:45"},
   413  
   414  		// Add a PDF stamp to all pages of inFile using the 1st page of pdfFile
   415  		// and rotate along the 2nd diagonal running from upper left to lower right corner.
   416  		{"TestWatermarkPDF",
   417  			"Walden.pdf",
   418  			"PdfSingleStampDefault.pdf",
   419  			nil,
   420  			"pdf",
   421  			filepath.Join(inDir, "Walden.pdf:1"),
   422  			"d:2"},
   423  
   424  		// Add a PDF multistamp in the top right corner to all pages of inFile.
   425  		{"TestWatermarkPDF",
   426  			"Walden.pdf",
   427  			"PdfMultistampDefault.pdf",
   428  			nil,
   429  			"pdf",
   430  			filepath.Join(inDir, "Walden.pdf"),
   431  			"scale:.2, pos:tr, off:-10 -10, rot:0"},
   432  
   433  		// Add a PDF multistamp to all pages of inFile.
   434  		// Start by stamping page 3 with page 1.
   435  		// You may filter stamping by defining selected Pages.
   436  		{"TestWatermarkPDF",
   437  			"zineTest.pdf",
   438  			"PdfMultistamp13.pdf",
   439  			nil,
   440  			"pdf",
   441  			filepath.Join(inDir, "zineTest.pdf:1:3"),
   442  			"scale:.2, pos:tr, off:-10 -10, rot:0"},
   443  
   444  		// Add a PDF multistamp to all pages of inFile.
   445  		// Start by stamping page 1 with page 3.
   446  		// You may filter stamping by defining selected Pages.
   447  		{"TestWatermarkPDF",
   448  			"zineTest.pdf",
   449  			"PdfMultistamp31.pdf",
   450  			nil,
   451  			"pdf",
   452  			filepath.Join(inDir, "zineTest.pdf:3:1"),
   453  			"scale:.2, pos:tr, off:-10 -10, rot:0"},
   454  
   455  		// Add a PDF multistamp to all pages of inFile.
   456  		// Start by stamping page 3 with page 3.
   457  		// You may filter stamping by defining selected Pages.
   458  		{"TestWatermarkPDF",
   459  			"zineTest.pdf",
   460  			"PdfMultistamp33.pdf",
   461  			nil,
   462  			"pdf",
   463  			filepath.Join(inDir, "zineTest.pdf:3:3"),
   464  			"scale:.2, pos:tr, off:-10 -10, rot:0"},
   465  	} {
   466  		testAddWatermarks(t, tt.msg, tt.inFile, tt.outFile, tt.selectedPages, tt.mode, tt.modeParm, tt.wmConf, false)
   467  		testAddWatermarks(t, tt.msg, tt.inFile, tt.outFile, tt.selectedPages, tt.mode, tt.modeParm, tt.wmConf, true)
   468  	}
   469  }
   470  
   471  func TestAddStampWithLink(t *testing.T) {
   472  	for _, tt := range []struct {
   473  		msg             string
   474  		inFile, outFile string
   475  		selectedPages   []string
   476  		mode            string
   477  		modeParm        string
   478  		wmConf          string
   479  	}{
   480  		{"TestStampTextWithLink",
   481  			"Walden.pdf",
   482  			"TextWithLink.pdf",
   483  			[]string{"1-"},
   484  			"text",
   485  			"A simple text watermark with link",
   486  			"url:pdfcpu.io"},
   487  
   488  		{"TestStampImageWithLink",
   489  			"Walden.pdf",
   490  			"ImageWithLink.pdf",
   491  			[]string{"1-"},
   492  			"image",
   493  			filepath.Join(resDir, "logoSmall.png"),
   494  			"url:pdfcpu.io, scale:.33 abs, rot:45"},
   495  	} {
   496  		// Links supported for stamps only (watermark onTop:true).
   497  		testAddWatermarks(t, tt.msg, tt.inFile, tt.outFile, tt.selectedPages, tt.mode, tt.modeParm, tt.wmConf, true)
   498  	}
   499  
   500  }
   501  
   502  func TestCropBox(t *testing.T) {
   503  	msg := "TestCropBox"
   504  	inFile := filepath.Join(inDir, "empty.pdf")
   505  	outFile := filepath.Join(samplesDir, "stamp", "pdf", "PdfWithCropBox.pdf")
   506  	pdfFile := filepath.Join(inDir, "grid_example.pdf")
   507  
   508  	// Create a context.
   509  	ctx, err := api.ReadContextFile(inFile)
   510  	if err != nil {
   511  		t.Fatalf("%s readContext: %v\n", msg, err)
   512  	}
   513  
   514  	for _, pos := range []string{"tl", "tc", "tr", "l", "c", "r", "bl", "bc", "br"} {
   515  		wm, err := api.PDFWatermark(pdfFile+":1", fmt.Sprintf("scale:.25 rel, pos:%s, rot:0", pos), true, false, types.POINTS)
   516  		if err != nil {
   517  			t.Fatalf("%s %s: %v\n", msg, outFile, err)
   518  		}
   519  		if err := pdfcpu.AddWatermarks(ctx, nil, wm); err != nil {
   520  			t.Fatalf("%s %s: %v\n", msg, outFile, err)
   521  		}
   522  	}
   523  
   524  	// Write context to file.
   525  	if err := api.WriteContextFile(ctx, outFile); err != nil {
   526  		t.Fatalf("%s write: %v\n", msg, err)
   527  	}
   528  
   529  	if err := api.ValidateFile(outFile, nil); err != nil {
   530  		t.Fatalf("%s: %v\n", msg, err)
   531  	}
   532  }
   533  
   534  func hasWatermarks(inFile string, t *testing.T) bool {
   535  	t.Helper()
   536  	ok, err := api.HasWatermarksFile(inFile, nil)
   537  	if err != nil {
   538  		t.Fatalf("Checking for watermarks: %s: %v\n", inFile, err)
   539  	}
   540  	return ok
   541  }
   542  
   543  func TestStampingLifecycle(t *testing.T) {
   544  	msg := "TestStampingLifecycle"
   545  	inFile := filepath.Join(inDir, "Acroforms2.pdf")
   546  	outFile := filepath.Join(outDir, "stampLC.pdf")
   547  	onTop := true // we are testing stamps
   548  
   549  	// Check for existing stamps.
   550  	if ok := hasWatermarks(inFile, t); ok {
   551  		t.Fatalf("Watermarks found: %s\n", inFile)
   552  	}
   553  
   554  	unit := types.POINTS
   555  
   556  	// Stamp all pages.
   557  	wm, err := api.TextWatermark("Demo", "", onTop, false, unit)
   558  	if err != nil {
   559  		t.Fatalf("%s %s: %v\n", msg, outFile, err)
   560  	}
   561  	if err := api.AddWatermarksFile(inFile, outFile, nil, wm, nil); err != nil {
   562  		t.Fatalf("%s %s: %v\n", msg, outFile, err)
   563  	}
   564  
   565  	// Check for existing stamps.
   566  	if ok := hasWatermarks(outFile, t); !ok {
   567  		t.Fatalf("No watermarks found: %s\n", outFile)
   568  	}
   569  
   570  	// // Update stamp on page 1.
   571  	wm, err = api.TextWatermark("Confidential", "", onTop, true, unit)
   572  	if err != nil {
   573  		t.Fatalf("%s %s: %v\n", msg, outFile, err)
   574  	}
   575  	if err := api.AddWatermarksFile(outFile, "", []string{"1"}, wm, nil); err != nil {
   576  		t.Fatalf("%s %s: %v\n", msg, outFile, err)
   577  	}
   578  
   579  	// Add another stamp on top for all pages.
   580  	// This is a redish transparent footer.
   581  	wm, err = api.TextWatermark("Footer", "pos:bc, c:0.8 0 0, op:.6, rot:0", onTop, false, unit)
   582  	if err != nil {
   583  		t.Fatalf("%s %s: %v\n", msg, outFile, err)
   584  	}
   585  	if err := api.AddWatermarksFile(outFile, "", nil, wm, nil); err != nil {
   586  		t.Fatalf("%s %s: %v\n", msg, outFile, err)
   587  	}
   588  
   589  	// Remove stamp on page 1.
   590  	if err := api.RemoveWatermarksFile(outFile, "", []string{"1"}, nil); err != nil {
   591  		t.Fatalf("%s %s: %v\n", msg, outFile, err)
   592  	}
   593  
   594  	// Check for existing stamps.
   595  	if ok := hasWatermarks(outFile, t); !ok {
   596  		t.Fatalf("No watermarks found: %s\n", outFile)
   597  	}
   598  
   599  	// Remove all stamps.
   600  	if err := api.RemoveWatermarksFile(outFile, "", nil, nil); err != nil {
   601  		t.Fatalf("%s %s: %v\n", msg, outFile, err)
   602  	}
   603  
   604  	// Validate the result.
   605  	if err := api.ValidateFile(outFile, nil); err != nil {
   606  		t.Fatalf("%s: %v\n", msg, err)
   607  	}
   608  
   609  	// Check for existing stamps.
   610  	if ok := hasWatermarks(outFile, t); ok {
   611  		t.Fatalf("Watermarks found: %s\n", outFile)
   612  	}
   613  }
   614  
   615  func TestRecycleWM(t *testing.T) {
   616  	msg := "TestRecycleWM"
   617  	inFile := filepath.Join(inDir, "test.pdf")
   618  	outFile := filepath.Join(samplesDir, "watermark", "text", "TextRecycled.pdf")
   619  	onTop := false // we are testing watermarks
   620  
   621  	desc := "pos:tl, points:22, rot:0, scale:1 abs, off:0 -5, opacity:0.3"
   622  	wm, err := api.TextWatermark("This is a watermark", desc, onTop, false, types.POINTS)
   623  	if err != nil {
   624  		t.Fatalf("%s %s: %v\n", msg, outFile, err)
   625  	}
   626  
   627  	if err = api.AddWatermarksFile(inFile, outFile, nil, wm, nil); err != nil {
   628  		t.Fatalf("%s %s: %v\n", msg, outFile, err)
   629  	}
   630  
   631  	wm.Recycle()
   632  
   633  	// Shift down watermark.
   634  	wm.Dy = -55
   635  
   636  	if err = api.AddWatermarksFile(outFile, outFile, nil, wm, nil); err != nil {
   637  		t.Fatalf("%s %s: %v\n", msg, outFile, err)
   638  	}
   639  }