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 }