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

     1  /*
     2  Copyright 2021 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  	"os"
    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/color"
    27  	"github.com/pdfcpu/pdfcpu/pkg/pdfcpu/model"
    28  	"github.com/pdfcpu/pdfcpu/pkg/pdfcpu/types"
    29  )
    30  
    31  var textAnn model.AnnotationRenderer = model.NewTextAnnotation(
    32  	*types.NewRectangle(0, 0, 100, 100), // rect
    33  	0,                                   // apObjNr
    34  	"Text Annotation",                   // contents
    35  	"ID1",                               // id
    36  	"",                                  // modDate
    37  	0,                                   // f
    38  	&color.Gray,                         // col
    39  	"Title1",                            // title
    40  	nil,                                 // popupIndRef
    41  	nil,                                 // ca
    42  	"",                                  // rc
    43  	"",                                  // subject
    44  	0,                                   // borderRadX
    45  	0,                                   // borderRadY
    46  	2,                                   // borderWidth
    47  	false,                               // displayOpen
    48  	"Comment")                           // name
    49  
    50  var textAnnCJK model.AnnotationRenderer = model.NewTextAnnotation(
    51  	*types.NewRectangle(0, 100, 100, 200), // rect
    52  	0,                                     // apObjNr
    53  	"文字注释",                                // contents
    54  	"ID1CJK",                              // id
    55  	"",                                    // modDate
    56  	0,                                     // f
    57  	&color.Gray,                           // col
    58  	"标题1",                                 // title
    59  	nil,                                   // popupIndRef
    60  	nil,                                   // ca
    61  	"RC",                                  // rc
    62  	"",                                    // subject
    63  	0,                                     // borderRadX
    64  	0,                                     // borderRadY
    65  	2,                                     // borderWidth
    66  	true,                                  // displayOpen
    67  	"Comment")                             // name
    68  
    69  var freeTextAnn model.AnnotationRenderer = model.NewFreeTextAnnotation(
    70  	*types.NewRectangle(200, 300, 400, 500), // rect
    71  	0,                                       // apObjNr
    72  	`Mac Preview shows "Contents"
    73  line 2
    74  line 3`, // contents
    75  	"ID1",           // id
    76  	"",              // modDate
    77  	model.AnnLocked, // f
    78  	&color.Gray,     // col
    79  	"Title1",        // title
    80  	nil,             // popupIndRef
    81  	nil,             // ca
    82  	"",              // rc
    83  	"",              // subject
    84  	`A.Reader renders rich text ("RC").
    85  line 2
    86  line 3`,
    87  	// `<?xml version="1.0" encoding="UTF-8"?>
    88  	//  <xhtml xmlns="http://www.w3.org/1999/xhtml" xmlns:xfa="http://www.xfa.org/schema/xfa-data/1.0/">
    89  	//  	<body>
    90  	// 		<p>This is some <b>rich text.</b></p>
    91  	// 	</body>
    92  	//  </xhmtl>`, // rich text (ignored by Mac Preview and rendered mediocre by Adobe Reader)
    93  	types.AlignCenter, // horizontal alignment
    94  	"Helvetica",       // font name (TODO)
    95  	12,                // font size in points (TODO)
    96  	&color.Green,      // font color
    97  	"",                // DS (default style string)
    98  	nil,               // Intent
    99  	nil,               // callOutLine
   100  	nil,               // callOutLineEndingStyle
   101  	0, 0, 0, 0,        // margin
   102  	0,             // borderWidth
   103  	model.BSSolid, // borderStyle
   104  	false,         // cloudyBorder
   105  	0)             // cloudyBorderIntensity
   106  
   107  var linkAnn model.AnnotationRenderer = model.NewLinkAnnotation(
   108  	*types.NewRectangle(200, 0, 300, 100), // rect
   109  	0,                                     // apObjNr
   110  	"",                                    // contents
   111  	"ID2",                                 // id
   112  	"",                                    // modDate
   113  	0,                                     // f
   114  	&color.Red,                            // borderCol
   115  	nil,                                   // dest
   116  	"https://pdfcpu.io",                   // uri
   117  	nil,                                   // quad
   118  	true,                                  // border
   119  	1,                                     // borderWidth
   120  	model.BSSolid,                         // borderStyle
   121  )
   122  
   123  var squareAnn model.AnnotationRenderer = model.NewSquareAnnotation(
   124  	*types.NewRectangle(300, 0, 350, 50), // rect
   125  	0,                                    // apObjNr
   126  	"Square Annotation",                  // contents
   127  	"ID3",                                // id
   128  	"",                                   // modDate
   129  	0,                                    // f
   130  	&color.Gray,                          // col
   131  	"Title1",                             // title
   132  	nil,                                  // popupIndRef
   133  	nil,                                  // ca
   134  	"",                                   // rc
   135  	"",                                   // subject
   136  	&color.Blue,                          // fillCol
   137  	0,                                    // MLeft
   138  	0,                                    // MTop
   139  	0,                                    // MRight
   140  	0,                                    // MBot
   141  	1,                                    // borderWidth
   142  	model.BSSolid,                        // borderStyle
   143  	false,                                // cloudyBorder
   144  	0,                                    // cloudyBorderIntensity
   145  )
   146  
   147  var squareAnnCJK model.AnnotationRenderer = model.NewSquareAnnotation(
   148  	*types.NewRectangle(300, 50, 350, 100), // rect
   149  	0,                                      // apObjNr
   150  	"方形注释",                                 // contents
   151  	"ID3CJK",                               // id
   152  	"",                                     // modDate
   153  	0,                                      // f
   154  	&color.Gray,                            // col
   155  	"Title1",                               // title
   156  	nil,                                    // popupIndRef
   157  	nil,                                    // ca
   158  	"",                                     // rc
   159  	"",                                     // subject
   160  	&color.Green,                           // fillCol
   161  	0,                                      // MLeft
   162  	0,                                      // MTop
   163  	0,                                      // MRight
   164  	0,                                      // MBot
   165  	1,                                      // borderWidth
   166  	model.BSDashed,                         // borderStyle
   167  	false,                                  // cloudyBorder
   168  	0,                                      // cloudyBorderIntensity
   169  )
   170  
   171  var circleAnn model.AnnotationRenderer = model.NewCircleAnnotation(
   172  	*types.NewRectangle(400, 0, 450, 50), // rect
   173  	0,                                    // apObjNr
   174  	"Circle Annotation",                  // contents
   175  	"ID4",                                // id
   176  	"",                                   // modDate
   177  	0,                                    // f
   178  	&color.Gray,                          // col
   179  	"Title1",                             // title
   180  	nil,                                  // popupIndRef
   181  	nil,                                  // ca
   182  	"",                                   // rc
   183  	"",                                   // subject
   184  	&color.Blue,                          // fillCol
   185  	0,                                    // MLeft
   186  	0,                                    // MTop
   187  	0,                                    // MRight
   188  	0,                                    // MBot
   189  	1,                                    // borderWidth
   190  	model.BSSolid,                        // borderStyle
   191  	false,                                // cloudyBorder
   192  	0,                                    // cloudyBorderIntensity
   193  )
   194  
   195  var circleAnnCJK model.AnnotationRenderer = model.NewCircleAnnotation(
   196  	*types.NewRectangle(400, 50, 450, 100), // rect
   197  	0,                                      // apObjNr
   198  	"圆圈注释",                                 // contents
   199  	"ID4CJK",                               // id
   200  	"",                                     // modDate
   201  	0,                                      // f
   202  	&color.Green,                           // col
   203  	"Title1",                               // title
   204  	nil,                                    // popupIndRef
   205  	nil,                                    // ca
   206  	"",                                     // rc
   207  	"",                                     // subject
   208  	&color.Blue,                            // fillCol
   209  	10,                                     // MLeft
   210  	10,                                     // MTop
   211  	10,                                     // MRight
   212  	10,                                     // MBot
   213  	1,                                      // borderWidth
   214  	model.BSBeveled,                        // borderStyle
   215  	false,                                  // cloudyBorder
   216  	0,                                      // cloudyBorderIntensity
   217  )
   218  
   219  func annotationCount(t *testing.T, inFile string) int {
   220  	t.Helper()
   221  
   222  	msg := "annotationCount"
   223  
   224  	f, err := os.Open(inFile)
   225  	if err != nil {
   226  		t.Fatalf("%s open: %v\n", msg, err)
   227  	}
   228  	defer f.Close()
   229  
   230  	annots, err := api.Annotations(f, nil, conf)
   231  	if err != nil {
   232  		t.Fatalf("%s annotations: %v\n", msg, err)
   233  	}
   234  
   235  	count, _, err := pdfcpu.ListAnnotations(annots)
   236  	if err != nil {
   237  		t.Fatalf("%s listAnnotations: %v\n", msg, err)
   238  	}
   239  
   240  	return count
   241  }
   242  
   243  func add2Annotations(t *testing.T, msg, inFile string, incr bool) {
   244  	t.Helper()
   245  
   246  	// We start with 0 annotations.
   247  	if i := annotationCount(t, inFile); i > 0 {
   248  		t.Fatalf("%s count: got %d want 0\n", msg, i)
   249  	}
   250  
   251  	// Add a text annotation to page 1.
   252  	if err := api.AddAnnotationsFile(inFile, "", []string{"1"}, textAnn, nil, incr); err != nil {
   253  		t.Fatalf("%s add: %v\n", msg, err)
   254  	}
   255  
   256  	// Add a link annotation to page 1.
   257  	if err := api.AddAnnotationsFile(inFile, "", []string{"1"}, linkAnn, nil, incr); err != nil {
   258  		t.Fatalf("%s add: %v\n", msg, err)
   259  	}
   260  
   261  	// Now we should have 2 annotations.
   262  	if i := annotationCount(t, inFile); i != 2 {
   263  		t.Fatalf("%s count: got %d want 2\n", msg, i)
   264  	}
   265  }
   266  
   267  func TestAddRemoveAnnotationsByAnnotType(t *testing.T) {
   268  	msg := "TestAddRemoveAnnotationsByAnnotType"
   269  
   270  	incr := false // incremental updates
   271  
   272  	fn := "test.pdf"
   273  	copyFile(t, filepath.Join(inDir, fn), filepath.Join(outDir, fn))
   274  	inFile := filepath.Join(outDir, fn)
   275  
   276  	add2Annotations(t, msg, inFile, incr)
   277  
   278  	// Remove annotations by annotation type.
   279  	if err := api.RemoveAnnotationsFile(inFile, "", nil, []string{"Link", "Text"}, nil, nil, false); err != nil {
   280  		t.Fatalf("%s remove: %v\n", msg, err)
   281  	}
   282  
   283  	// We should have 0 annotations as at the beginning.
   284  	if i := annotationCount(t, inFile); i > 0 {
   285  		t.Fatalf("%s count: got %d want 0\n", msg, i)
   286  	}
   287  }
   288  
   289  func TestAddRemoveAnnotationsById(t *testing.T) {
   290  	msg := "TestAddRemoveAnnotationsById"
   291  
   292  	incr := false // incremental updates
   293  
   294  	fn := "test.pdf"
   295  	copyFile(t, filepath.Join(inDir, fn), filepath.Join(outDir, fn))
   296  	inFile := filepath.Join(outDir, fn)
   297  
   298  	add2Annotations(t, msg, inFile, incr)
   299  
   300  	// Remove annotations by id.
   301  	if err := api.RemoveAnnotationsFile(inFile, "", nil, []string{"ID1", "ID2"}, nil, nil, incr); err != nil {
   302  		t.Fatalf("%s remove: %v\n", msg, err)
   303  	}
   304  
   305  	// We should have 0 annotations as at the beginning.
   306  	if i := annotationCount(t, inFile); i > 0 {
   307  		t.Fatalf("%s count: got %d want 0\n", msg, i)
   308  	}
   309  }
   310  
   311  func TestAddRemoveAnnotationsByIdAndAnnotType(t *testing.T) {
   312  	msg := "TestAddRemoveAnnotationsByIdAndAnnotType"
   313  
   314  	incr := false // incremental updates
   315  
   316  	fn := "test.pdf"
   317  	copyFile(t, filepath.Join(inDir, fn), filepath.Join(outDir, fn))
   318  	inFile := filepath.Join(outDir, fn)
   319  
   320  	add2Annotations(t, msg, inFile, incr)
   321  
   322  	// Remove annotations by id annotation type.
   323  	if err := api.RemoveAnnotationsFile(inFile, "", nil, []string{"ID1", "Link"}, nil, nil, incr); err != nil {
   324  		t.Fatalf("%s remove: %v\n", msg, err)
   325  	}
   326  
   327  	// We should have 0 annotations as at the beginning.
   328  	if i := annotationCount(t, inFile); i > 0 {
   329  		t.Fatalf("%s count: got %d want 0\n", msg, i)
   330  	}
   331  }
   332  
   333  func TestAddRemoveAnnotationsByObjNr(t *testing.T) {
   334  	msg := "TestAddRemoveAnnotationsByObjNr"
   335  
   336  	fn := "test.pdf"
   337  	copyFile(t, filepath.Join(inDir, fn), filepath.Join(outDir, fn))
   338  	inFile := filepath.Join(outDir, fn)
   339  
   340  	// Create a context.
   341  	ctx, err := api.ReadContextFile(inFile)
   342  	if err != nil {
   343  		t.Fatalf("%s readContext: %v\n", msg, err)
   344  	}
   345  
   346  	allPages, err := api.PagesForPageSelection(ctx.PageCount, nil, true, true)
   347  	if err != nil {
   348  		t.Fatalf("%s pagesForPageSelection: %v\n", msg, err)
   349  	}
   350  
   351  	// Add link annotation to all pages.
   352  	ok, err := pdfcpu.AddAnnotations(ctx, allPages, linkAnn, false)
   353  	if err != nil || !ok {
   354  		t.Fatalf("%s add: %v\n", msg, err)
   355  	}
   356  
   357  	// Write context to file.
   358  	err = api.WriteContextFile(ctx, inFile)
   359  	if err != nil {
   360  		t.Fatalf("%s write: %v\n", msg, err)
   361  	}
   362  
   363  	// We should have 1 annotation
   364  	if i := annotationCount(t, inFile); i != 1 {
   365  		t.Fatalf("%s count: got %d want 0\n", msg, i)
   366  	}
   367  
   368  	// Create a context.
   369  	ctx, err = api.ReadContextFile(inFile)
   370  	if err != nil {
   371  		t.Fatalf("%s readContext: %v\n", msg, err)
   372  	}
   373  
   374  	// Identify object numbers for located annotations
   375  	objNrs, err := pdfcpu.CachedAnnotationObjNrs(ctx)
   376  	if err != nil {
   377  		t.Fatalf("%s annObjNrs: %v\n", msg, err)
   378  	}
   379  	if len(objNrs) != 1 {
   380  		t.Fatalf("%s want 1 annotation, got: %d\n", msg, len(objNrs))
   381  	}
   382  
   383  	// Remove annotations by their object numbers
   384  	// We could also do: api.RemoveAnnotationsFile
   385  	// but since we already have the ctx this is more straight forward.
   386  	_, err = pdfcpu.RemoveAnnotations(ctx, allPages, nil, objNrs, false)
   387  	if err != nil {
   388  		t.Fatalf("%s remove: %v\n", msg, err)
   389  	}
   390  
   391  	// Write context to file.
   392  	err = api.WriteContextFile(ctx, inFile)
   393  	if err != nil {
   394  		t.Fatalf("%s write: %v\n", msg, err)
   395  	}
   396  
   397  	// We should have 0 annotations like at the beginning.
   398  	if i := annotationCount(t, inFile); i > 0 {
   399  		t.Fatalf("%s count: got %d want 0\n", msg, i)
   400  	}
   401  }
   402  
   403  func TestAddRemoveAnnotationsByObjNrAndAnnotType(t *testing.T) {
   404  	msg := "TestAddRemoveAnnotationsByObjNrAndAnnotType"
   405  
   406  	incr := false // incremental updates
   407  
   408  	fn := "test.pdf"
   409  	copyFile(t, filepath.Join(inDir, fn), filepath.Join(outDir, fn))
   410  	inFile := filepath.Join(outDir, fn)
   411  
   412  	add2Annotations(t, msg, inFile, incr)
   413  
   414  	// Remove annotations by obj and annotation type.
   415  	// Here we use the obj# of the link Annotation to be removed.
   416  	if err := api.RemoveAnnotationsFile(inFile, "", nil, []string{"Link"}, []int{6}, nil, incr); err != nil {
   417  		t.Fatalf("%s remove: %v\n", msg, err)
   418  	}
   419  
   420  	// We should have 1 annotations.
   421  	if i := annotationCount(t, inFile); i != 1 {
   422  		t.Fatalf("%s count: got %d want 0\n", msg, i)
   423  	}
   424  }
   425  
   426  func TestAddRemoveAnnotationsByIdAndObjNrAndAnnotType(t *testing.T) {
   427  	msg := "TestAddRemoveAnnotationsByObjNrAndAnnotType"
   428  
   429  	incr := false // incremental updates
   430  
   431  	fn := "test.pdf"
   432  	copyFile(t, filepath.Join(inDir, fn), filepath.Join(outDir, fn))
   433  	inFile := filepath.Join(outDir, fn)
   434  
   435  	add2Annotations(t, msg, inFile, incr)
   436  
   437  	// Remove annotations by id annotation type.
   438  	if err := api.RemoveAnnotationsFile(inFile, "", nil, []string{"ID1", "Link"}, nil, nil, incr); err != nil {
   439  		t.Fatalf("%s remove: %v\n", msg, err)
   440  	}
   441  
   442  	// We should have 0 annotations as at the beginning.
   443  	if i := annotationCount(t, inFile); i > 0 {
   444  		t.Fatalf("%s count: got %d want 0\n", msg, i)
   445  	}
   446  }
   447  
   448  func TestRemoveAllAnnotations(t *testing.T) {
   449  	msg := "TestRemoveAllAnnotations"
   450  
   451  	incr := false
   452  
   453  	fn := "test.pdf"
   454  	copyFile(t, filepath.Join(inDir, fn), filepath.Join(outDir, fn))
   455  	inFile := filepath.Join(outDir, fn)
   456  
   457  	m := map[int][]model.AnnotationRenderer{}
   458  	anns := make([]model.AnnotationRenderer, 2)
   459  	anns[0] = textAnn
   460  	anns[1] = linkAnn
   461  	m[1] = anns
   462  
   463  	err := api.AddAnnotationsMapFile(inFile, "", m, nil, incr)
   464  	if err != nil {
   465  		t.Fatalf("%s add: %v\n", msg, err)
   466  	}
   467  
   468  	// We should have 2 annotations.
   469  	if i := annotationCount(t, inFile); i != 2 {
   470  		t.Fatalf("%s count: got %d want 0\n", msg, i)
   471  	}
   472  
   473  	// Remove all annotations.
   474  	err = api.RemoveAnnotationsFile(inFile, "", nil, nil, nil, nil, incr)
   475  	if err != nil {
   476  		t.Fatalf("%s remove: %v\n", msg, err)
   477  	}
   478  
   479  	// We should have 0 annotations like at the beginning.
   480  	if i := annotationCount(t, inFile); i > 0 {
   481  		t.Fatalf("%s count: got %d want 0\n", msg, i)
   482  	}
   483  }
   484  
   485  func TestAddRemoveAllAnnotationsAsIncrements(t *testing.T) {
   486  	msg := "TestAddRemoveAnnotationsAsIncrements"
   487  
   488  	incr := true // incremental updates
   489  
   490  	fn := "test.pdf"
   491  	copyFile(t, filepath.Join(inDir, fn), filepath.Join(outDir, fn))
   492  	inFile := filepath.Join(outDir, fn)
   493  
   494  	add2Annotations(t, msg, inFile, incr)
   495  
   496  	// Remove all page annotations and append the result as PDF increment to inFile.
   497  	if err := api.RemoveAnnotationsFile(inFile, "", nil, nil, nil, nil, true); err != nil {
   498  		t.Fatalf("%s remove: %v\n", msg, err)
   499  	}
   500  
   501  	// We should have 0 annotations like at the beginning.
   502  	if i := annotationCount(t, inFile); i > 0 {
   503  		t.Fatalf("%s count: got %d want 0\n", msg, i)
   504  	}
   505  }
   506  
   507  func TestAddAnnotationsLowLevel(t *testing.T) {
   508  	msg := "TestAddAnnotationsLowLevel"
   509  
   510  	fn := "test.pdf"
   511  	inFile := filepath.Join(inDir, fn)
   512  	outFile := filepath.Join(outDir, fn)
   513  
   514  	// Create a context.
   515  	ctx, err := api.ReadContextFile(inFile)
   516  	if err != nil {
   517  		t.Fatalf("%s readContext: %v\n", msg, err)
   518  	}
   519  
   520  	m := map[int][]model.AnnotationRenderer{}
   521  	anns := make([]model.AnnotationRenderer, 2)
   522  	anns[0] = textAnn
   523  	anns[1] = linkAnn
   524  	m[1] = anns
   525  
   526  	// Add 2 annotations to page 1.
   527  	if ok, err := pdfcpu.AddAnnotationsMap(ctx, m, false); err != nil || !ok {
   528  		t.Fatalf("%s add: %v\n", msg, err)
   529  	}
   530  
   531  	// Write context to file.
   532  	if err := api.WriteContextFile(ctx, outFile); err != nil {
   533  		t.Fatalf("%s write: %v\n", msg, err)
   534  	}
   535  
   536  	// Create a context.
   537  	ctx, err = api.ReadContextFile(outFile)
   538  	if err != nil {
   539  		t.Fatalf("%s readContext: %v\n", msg, err)
   540  	}
   541  
   542  	// We should have 2 annotations.
   543  	i, _, err := pdfcpu.ListAnnotations(ctx.PageAnnots)
   544  	if err != nil || i != 2 {
   545  		t.Fatalf("%s list: %v\n", msg, err)
   546  	}
   547  
   548  	// Remove all annotations.
   549  	_, err = pdfcpu.RemoveAnnotations(ctx, nil, nil, nil, false)
   550  	if err != nil {
   551  		t.Fatalf("%s remove: %v\n", msg, err)
   552  	}
   553  
   554  	// (before writing) We should have 0 annotations like at the beginning.
   555  	i, _, err = pdfcpu.ListAnnotations(ctx.PageAnnots)
   556  	if err != nil || i != 0 {
   557  		t.Fatalf("%s list: %v\n", msg, err)
   558  	}
   559  
   560  	// Write context to file.
   561  	if err := api.WriteContextFile(ctx, outFile); err != nil {
   562  		t.Fatalf("%s write: %v\n", msg, err)
   563  	}
   564  
   565  	// (after writing) We should have 0 annotations like at the beginning.
   566  	if i := annotationCount(t, inFile); i > 0 {
   567  		t.Fatalf("%s count: got %d want 0\n", msg, i)
   568  	}
   569  }
   570  
   571  func TestAddLinkAnnotationWithDest(t *testing.T) {
   572  	msg := "TestAddLinkAnnotationWithDest"
   573  
   574  	// Best viewed with Adobe Reader.
   575  
   576  	inFile := filepath.Join(inDir, "Walden.pdf")
   577  	outFile := filepath.Join(samplesDir, "annotations", "LinkAnnotWithDestTopLeft.pdf")
   578  
   579  	// Create internal link:
   580  	// Add a 100x100 link rectangle on the bottom left corner of page 2.
   581  	// Set destination to top left corner of page 1.
   582  	dest := &model.Destination{Typ: model.DestXYZ, PageNr: 1, Left: -1, Top: -1}
   583  
   584  	internalLink := model.NewLinkAnnotation(
   585  		*types.NewRectangle(0, 0, 100, 100), // rect
   586  		0,                                   // apObjNr
   587  		"",                                  // contents
   588  		"ID2",                               // id
   589  		"",                                  // modDate
   590  		0,                                   // f
   591  		&color.Red,                          // borderCol
   592  		dest,                                // dest
   593  		"",                                  // uri
   594  		nil,                                 // quad
   595  		true,                                // border
   596  		1,                                   // borderWidth
   597  		model.BSSolid,                       // borderStyle
   598  	)
   599  
   600  	err := api.AddAnnotationsFile(inFile, outFile, []string{"2"}, internalLink, nil, false)
   601  	if err != nil {
   602  		t.Fatalf("%s add: %v\n", msg, err)
   603  	}
   604  }
   605  
   606  func TestAddAnnotationsFile(t *testing.T) {
   607  	msg := "TestAddAnnotationsFile"
   608  
   609  	// Best viewed with Adobe Reader.
   610  
   611  	inFile := filepath.Join(inDir, "test.pdf")
   612  	outFile := filepath.Join(samplesDir, "annotations", "Annotations.pdf")
   613  
   614  	// Add text annotation.
   615  	if err := api.AddAnnotationsFile(inFile, outFile, nil, textAnn, nil, false); err != nil {
   616  		t.Fatalf("%s add: %v\n", msg, err)
   617  	}
   618  
   619  	// Add CJK text annotation.
   620  	if err := api.AddAnnotationsFile(outFile, outFile, nil, textAnnCJK, nil, false); err != nil {
   621  		t.Fatalf("%s add: %v\n", msg, err)
   622  	}
   623  
   624  	// Add link annotation.
   625  	if err := api.AddAnnotationsFile(outFile, outFile, nil, linkAnn, nil, false); err != nil {
   626  		t.Fatalf("%s add: %v\n", msg, err)
   627  	}
   628  
   629  	// Add square annotation.
   630  	if err := api.AddAnnotationsFile(outFile, outFile, nil, squareAnn, nil, false); err != nil {
   631  		t.Fatalf("%s add: %v\n", msg, err)
   632  	}
   633  
   634  	// Add CJK square annotation.
   635  	if err := api.AddAnnotationsFile(outFile, outFile, nil, squareAnnCJK, nil, false); err != nil {
   636  		t.Fatalf("%s add: %v\n", msg, err)
   637  	}
   638  
   639  	// Add circle annotation.
   640  	if err := api.AddAnnotationsFile(outFile, outFile, nil, circleAnn, nil, false); err != nil {
   641  		t.Fatalf("%s add: %v\n", msg, err)
   642  	}
   643  
   644  	// Add CJK circle annotation.
   645  	if err := api.AddAnnotationsFile(outFile, outFile, nil, circleAnnCJK, nil, false); err != nil {
   646  		t.Fatalf("%s add: %v\n", msg, err)
   647  	}
   648  
   649  }
   650  
   651  func TestAddAnnotations(t *testing.T) {
   652  	msg := "TestAddAnnotations"
   653  
   654  	inFile := filepath.Join(inDir, "test.pdf")
   655  	outFile := filepath.Join(outDir, "Annotations.pdf")
   656  
   657  	// Create a context from inFile.
   658  	ctx, err := api.ReadContextFile(inFile)
   659  	if err != nil {
   660  		t.Fatalf("%s readContext: %v\n", msg, err)
   661  	}
   662  
   663  	// Prepare annotations for page 1.
   664  	m := map[int][]model.AnnotationRenderer{}
   665  	anns := make([]model.AnnotationRenderer, 7)
   666  
   667  	anns[0] = textAnn
   668  	anns[1] = textAnnCJK
   669  	anns[2] = squareAnn
   670  	anns[3] = squareAnnCJK
   671  	anns[4] = circleAnn
   672  	anns[5] = circleAnnCJK
   673  	anns[6] = linkAnn
   674  
   675  	m[1] = anns
   676  
   677  	// Add 7 annotations to page 1.
   678  	if ok, err := pdfcpu.AddAnnotationsMap(ctx, m, false); err != nil || !ok {
   679  		t.Fatalf("%s add: %v\n", msg, err)
   680  	}
   681  
   682  	// Write context to outFile.
   683  	if err := api.WriteContextFile(ctx, outFile); err != nil {
   684  		t.Fatalf("%s write: %v\n", msg, err)
   685  	}
   686  
   687  }
   688  
   689  func TestPopupAnnotation(t *testing.T) {
   690  	msg := "TestPopupAnnotation"
   691  
   692  	// Add a Markup annotation and a linked Popup annotation.
   693  	// Best viewed with Adobe Reader.
   694  
   695  	inFile := filepath.Join(inDir, "test.pdf")
   696  	outFile := filepath.Join(samplesDir, "annotations", "PopupAnnotation.pdf")
   697  
   698  	incr := false
   699  	pageNr := 1
   700  
   701  	// Create a context.
   702  	ctx, err := api.ReadContextFile(inFile)
   703  	if err != nil {
   704  		t.Fatalf("%s readContext: %v\n", msg, err)
   705  	}
   706  
   707  	// Add Markup annotation.
   708  	parentIndRef, textAnnotDict, err := pdfcpu.AddAnnotationToPage(ctx, pageNr, textAnn, incr)
   709  	if err != nil {
   710  		t.Fatalf("%s Add Text AnnotationToPage: %v\n", msg, err)
   711  	}
   712  
   713  	// Add Markup annotation as parent of Popup annotation.
   714  	popupAnn := model.NewPopupAnnotation(
   715  		*types.NewRectangle(0, 0, 100, 100), // rect
   716  		0,                                   // apObjNr
   717  		"Popup content",                     // contents
   718  		"IDPopup",                           // id
   719  		"",                                  // modDate
   720  		0,                                   // f
   721  		&color.Green,                        // col
   722  		0,                                   // borderRadX
   723  		0,                                   // borderRadY
   724  		2,                                   // borderWidth
   725  		parentIndRef,                        // parentIndRef,
   726  		false,                               // displayOpen
   727  	)
   728  
   729  	// Add Popup annotation.
   730  	popupIndRef, _, err := pdfcpu.AddAnnotationToPage(ctx, pageNr, popupAnn, incr)
   731  	if err != nil {
   732  		t.Fatalf("%s Add Popup AnnotationToPage: %v\n", msg, err)
   733  	}
   734  
   735  	// Add Popup annotation to Markup annotation.
   736  	textAnnotDict["Popup"] = *popupIndRef
   737  
   738  	// Write context to file.
   739  	if err := api.WriteContextFile(ctx, outFile); err != nil {
   740  		t.Fatalf("%s write: %v\n", msg, err)
   741  	}
   742  }
   743  
   744  func TestInkAnnotation(t *testing.T) {
   745  	msg := "TestInkAnnotation"
   746  
   747  	// Best viewed with Adobe Reader.
   748  
   749  	inFile := filepath.Join(inDir, "test.pdf")
   750  	outFile := filepath.Join(samplesDir, "annotations", "InkAnnotation.pdf")
   751  
   752  	p1 := model.InkPath{100., 542., 150., 492., 200., 542.}
   753  	p2 := model.InkPath{100, 592, 150, 592}
   754  
   755  	inkAnn := model.NewInkAnnotation(
   756  		*types.NewRectangle(0, 0, 100, 100), // rect
   757  		0,                                   // apObjNr
   758  		"Ink content",                       // contents
   759  		"IDInk",                             // id
   760  		"",                                  // modDate
   761  		0,                                   // f
   762  		&color.Red,                          // col
   763  		"Title1",                            // title
   764  		nil,                                 // popupIndRef
   765  		nil,                                 // ca
   766  		"",                                  // rc
   767  		"",                                  // subject
   768  		[]model.InkPath{p1, p2},             // InkList
   769  		0,                                   // borderWidth
   770  		model.BSSolid,                       // borderStyle
   771  	)
   772  
   773  	// Add Ink annotation.
   774  	if err := api.AddAnnotationsFile(inFile, outFile, nil, inkAnn, nil, false); err != nil {
   775  		t.Fatalf("%s add: %v\n", msg, err)
   776  	}
   777  }
   778  
   779  func TestHighlightAnnotation(t *testing.T) {
   780  	msg := "TestHighlightAnnotation"
   781  
   782  	// Best viewed with Adobe Reader.
   783  
   784  	inFile := filepath.Join(inDir, "testWithText.pdf")
   785  	outFile := filepath.Join(samplesDir, "annotations", "HighlightAnnotation.pdf")
   786  
   787  	r := types.NewRectangle(205, 624.16, 400, 645.88)
   788  
   789  	ql := types.NewQuadLiteralForRect(r)
   790  
   791  	inkAnn := model.NewHighlightAnnotation(
   792  		*r,                    // rect
   793  		0,                     // apObjNr
   794  		"Highlight content",   // contents
   795  		"IDHighlight",         // id
   796  		"",                    // modDate
   797  		model.AnnLocked,       // f
   798  		&color.Yellow,         // col
   799  		0,                     // borderRadX
   800  		0,                     // borderRadY
   801  		2,                     // borderWidth
   802  		"Comment by Horst",    // title
   803  		nil,                   // popupIndRef
   804  		nil,                   // ca
   805  		"",                    // rc
   806  		"Subject",             // subject
   807  		types.QuadPoints{*ql}, // quad points
   808  	)
   809  
   810  	// Add Highlight annotation.
   811  	if err := api.AddAnnotationsFile(inFile, outFile, nil, inkAnn, nil, false); err != nil {
   812  		t.Fatalf("%s add: %v\n", msg, err)
   813  	}
   814  }
   815  
   816  func TestUnderlineAnnotation(t *testing.T) {
   817  	msg := "TestUnderlineAnnotation"
   818  
   819  	// Best viewed with Adobe Reader.
   820  
   821  	inFile := filepath.Join(inDir, "testWithText.pdf")
   822  	outFile := filepath.Join(samplesDir, "annotations", "UnderlineAnnotation.pdf")
   823  
   824  	r := types.NewRectangle(205, 624.16, 400, 645.88)
   825  
   826  	ql := types.NewQuadLiteralForRect(r)
   827  
   828  	underlineAnn := model.NewUnderlineAnnotation(
   829  		*r,                    // rect
   830  		0,                     // apObjNr
   831  		"Underline content",   // contents
   832  		"IDUnderline",         // id
   833  		"",                    // modDate
   834  		model.AnnLocked,       // f
   835  		&color.Yellow,         // col
   836  		0,                     // borderRadX
   837  		0,                     // borderRadY
   838  		2,                     // borderWidth
   839  		"Title1",              // title
   840  		nil,                   // popupIndRef
   841  		nil,                   // ca
   842  		"",                    // rc
   843  		"",                    // subject
   844  		types.QuadPoints{*ql}, // quad points
   845  	)
   846  
   847  	// Add Underline annotation.
   848  	if err := api.AddAnnotationsFile(inFile, outFile, nil, underlineAnn, nil, false); err != nil {
   849  		t.Fatalf("%s add: %v\n", msg, err)
   850  	}
   851  }
   852  
   853  func TestSquigglyAnnotation(t *testing.T) {
   854  	msg := "TestSquigglyAnnotation"
   855  
   856  	// Best viewed with Adobe Reader.
   857  
   858  	inFile := filepath.Join(inDir, "testWithText.pdf")
   859  	outFile := filepath.Join(samplesDir, "annotations", "SquigglyAnnotation.pdf")
   860  
   861  	r := types.NewRectangle(205, 624.16, 400, 645.88)
   862  
   863  	ql := types.NewQuadLiteralForRect(r)
   864  
   865  	squigglyAnn := model.NewSquigglyAnnotation(
   866  		*r,                    // rect
   867  		0,                     // apObjNr
   868  		"Squiggly content",    // contents
   869  		"IDSquiggly",          // id
   870  		"",                    // modDate
   871  		model.AnnLocked,       // f
   872  		&color.Yellow,         // col
   873  		0,                     // borderRadX
   874  		0,                     // borderRadY
   875  		2,                     // borderWidth
   876  		"Title1",              // title
   877  		nil,                   // popupIndRef
   878  		nil,                   // ca
   879  		"",                    // rc
   880  		"",                    // subject
   881  		types.QuadPoints{*ql}, // quad points
   882  	)
   883  
   884  	// Add Squiggly annotation.
   885  	if err := api.AddAnnotationsFile(inFile, outFile, nil, squigglyAnn, nil, false); err != nil {
   886  		t.Fatalf("%s add: %v\n", msg, err)
   887  	}
   888  }
   889  
   890  func TestStrikeOutAnnotation(t *testing.T) {
   891  	msg := "TestStrikeOutAnnotation"
   892  
   893  	// Best viewed with Adobe Reader.
   894  
   895  	inFile := filepath.Join(inDir, "testWithText.pdf")
   896  	outFile := filepath.Join(samplesDir, "annotations", "StrikeOutAnnotation.pdf")
   897  
   898  	r := types.NewRectangle(205, 624.16, 400, 645.88)
   899  
   900  	ql := types.NewQuadLiteralForRect(r)
   901  
   902  	strikeOutAnn := model.NewStrikeOutAnnotation(
   903  		*r,                    // rect
   904  		0,                     // apObjNr
   905  		"StrikeOut content",   // contents
   906  		"IDStrikeOut",         // id
   907  		"",                    // modDate
   908  		model.AnnLocked,       // f
   909  		&color.Yellow,         // col
   910  		0,                     // borderRadX
   911  		0,                     // borderRadY
   912  		2,                     // borderWidth
   913  		"Title1",              // title
   914  		nil,                   // popupIndRef
   915  		nil,                   // ca
   916  		"",                    // rc
   917  		"",                    // subject
   918  		types.QuadPoints{*ql}, // quad points
   919  	)
   920  
   921  	// Add StrikeOut annotation.
   922  	if err := api.AddAnnotationsFile(inFile, outFile, nil, strikeOutAnn, nil, false); err != nil {
   923  		t.Fatalf("%s add: %v\n", msg, err)
   924  	}
   925  }
   926  
   927  func TestFreeTextAnnotation(t *testing.T) {
   928  	msg := "TestFreeTextAnnotation"
   929  
   930  	// Best viewed with Adobe Reader.
   931  
   932  	inFile := filepath.Join(inDir, "test.pdf")
   933  	outFile := filepath.Join(samplesDir, "annotations", "FreeTextAnnotation.pdf")
   934  
   935  	// Add Free text annotation.
   936  	if err := api.AddAnnotationsFile(inFile, outFile, nil, freeTextAnn, nil, false); err != nil {
   937  		t.Fatalf("%s add: %v\n", msg, err)
   938  	}
   939  }
   940  
   941  func TestPolyLineAnnotation(t *testing.T) {
   942  	msg := "TestPolyLineAnnotation"
   943  
   944  	// Best viewed with Adobe Reader.
   945  
   946  	inFile := filepath.Join(inDir, "test.pdf")
   947  	outFile := filepath.Join(samplesDir, "annotations", "PolyLineAnnotation.pdf")
   948  
   949  	leButt := model.LEButt
   950  	leOpenArrow := model.LEOpenArrow
   951  
   952  	polyLineAnn := model.NewPolyLineAnnotation(
   953  		*types.NewRectangle(30, 30, 110, 110), // rect
   954  		0,                                     // apObjNr
   955  		"PolyLine Annotation",                 // contents
   956  		"IDPolyLine",                          // id
   957  		"",                                    // modDate
   958  		0,                                     // f
   959  		&color.Gray,                           // col
   960  		"Title1",                              // title
   961  		nil,                                   // popupIndRef
   962  		nil,                                   // ca
   963  		"",                                    // rc
   964  		"",                                    // subject
   965  		types.NewNumberArray(30, 30, 110, 110, 110, 30), // vertices
   966  		nil,            // path
   967  		nil,            // intent
   968  		nil,            // measure
   969  		&color.Green,   // fillCol
   970  		1,              // borderWidth
   971  		model.BSDashed, // borderStyle
   972  		&leButt,        // start lineEndingStyle
   973  		&leOpenArrow,   // end lineEndingStyle
   974  	)
   975  
   976  	// Add PolyLine annotation.
   977  	if err := api.AddAnnotationsFile(inFile, outFile, nil, polyLineAnn, nil, false); err != nil {
   978  		t.Fatalf("%s add: %v\n", msg, err)
   979  	}
   980  }
   981  
   982  func TestPolygonAnnotation(t *testing.T) {
   983  	msg := "TestPolygonAnnotation"
   984  
   985  	// Best viewed with Adobe Reader.
   986  
   987  	inFile := filepath.Join(inDir, "test.pdf")
   988  	outFile := filepath.Join(samplesDir, "annotations", "PolygonAnnotation.pdf")
   989  
   990  	polygonAnn := model.NewPolygonAnnotation(
   991  		*types.NewRectangle(30, 30, 110, 110), // rect
   992  		0,                                     // apObjNr
   993  		"Polygon Annotation",                  // contents
   994  		"IDPolygon",                           // id
   995  		"",                                    // modDate
   996  		0,                                     // f
   997  		&color.Gray,                           // col
   998  		"Title1",                              // title
   999  		nil,                                   // popupIndRef
  1000  		nil,                                   // ca
  1001  		"",                                    // rc
  1002  		"",                                    // subject
  1003  		types.NewNumberArray(30, 30, 110, 110, 110, 30), // vertices
  1004  		nil,            // path
  1005  		nil,            // intent
  1006  		nil,            // measure
  1007  		&color.Green,   // fillCol
  1008  		5,              // borderWidth
  1009  		model.BSDashed, // borderStyle
  1010  		true,           // cloudyBorder
  1011  		2)              // cloudyBorderIntensity
  1012  
  1013  	// Add Polygon annotation.
  1014  	if err := api.AddAnnotationsFile(inFile, outFile, nil, polygonAnn, nil, false); err != nil {
  1015  		t.Fatalf("%s add: %v\n", msg, err)
  1016  	}
  1017  }
  1018  
  1019  func TestLineAnnotation(t *testing.T) {
  1020  	msg := "TestLineAnnotation"
  1021  
  1022  	// Best viewed with Adobe Reader.
  1023  
  1024  	inFile := filepath.Join(inDir, "test.pdf")
  1025  	outFile := filepath.Join(samplesDir, "annotations", "LineAnnotation.pdf")
  1026  
  1027  	leOpenArrow := model.LEOpenArrow
  1028  
  1029  	lineAnn := model.NewLineAnnotation(
  1030  		*types.NewRectangle(30, 30, 110, 110), // rect
  1031  		0,                                     // apObjNr
  1032  		"Diagonal",                            // contents
  1033  		"IDLine",                              // id
  1034  		"",                                    // modDate
  1035  		0,                                     // f
  1036  		&color.DarkGray,                       // col
  1037  		"Title1",                              // title
  1038  		nil,                                   // popupIndRef
  1039  		nil,                                   // ca
  1040  		"",                                    // rc
  1041  		"",                                    // subject
  1042  		types.NewPoint(148.75, 140.33),        // P1
  1043  		types.NewPoint(297.5, 280.66),         // P2
  1044  		&leOpenArrow,                          // start lineEndingStyle
  1045  		&leOpenArrow,                          // end lineEndingStyle
  1046  		50,                                    // leader line length
  1047  		0,                                     // leader line offset
  1048  		10,                                    // leader line extension length
  1049  		nil,                                   // intent
  1050  		nil,                                   // measure
  1051  		true,                                  // caption
  1052  		false,                                 // caption position top
  1053  		0,                                     // caption offset X
  1054  		0,                                     // caption offset Y
  1055  		nil,                                   // fillCol
  1056  		1,                                     // borderWidth
  1057  		model.BSSolid)                         // borderStyle
  1058  
  1059  	// Add line annotation.
  1060  	if err := api.AddAnnotationsFile(inFile, outFile, nil, lineAnn, nil, false); err != nil {
  1061  		t.Fatalf("%s add: %v\n", msg, err)
  1062  	}
  1063  }
  1064  
  1065  func TestCaretAnnotation(t *testing.T) {
  1066  	msg := "TestCaretAnnotation"
  1067  
  1068  	// Best viewed with Adobe Reader.
  1069  
  1070  	inFile := filepath.Join(inDir, "test.pdf")
  1071  	outFile := filepath.Join(samplesDir, "annotations", "CaretAnnotation.pdf")
  1072  
  1073  	caretAnn := model.NewCaretAnnotation(
  1074  		*types.NewRectangle(30, 30, 110, 110), // rect
  1075  		0,                                     // apObjNr
  1076  		"Caret Annotation",                    // contents
  1077  		"IDCaret",                             // id
  1078  		"",                                    // modDate
  1079  		0,                                     // f,
  1080  		nil,                                   // col
  1081  		0,                                     // borderRadX
  1082  		0,                                     // borderRadY
  1083  		0,                                     // borderWidth
  1084  		"Title1",                              // title
  1085  		nil,                                   // popupIndRef
  1086  		nil,                                   // ca
  1087  		"",                                    // rc
  1088  		"",                                    // subject
  1089  		types.NewRectangle(20, 20, 20, 20),    // RD
  1090  		true)                                  // paragraph symbol
  1091  
  1092  	// Add line annotation.
  1093  	if err := api.AddAnnotationsFile(inFile, outFile, nil, caretAnn, nil, false); err != nil {
  1094  		t.Fatalf("%s add: %v\n", msg, err)
  1095  	}
  1096  }