@@ -30,6 +30,7 @@ This file is part of the iText (R) project.
30
30
import com .itextpdf .forms .fields .PdfFormCreator ;
31
31
import com .itextpdf .forms .fields .PdfSignatureFormField ;
32
32
import com .itextpdf .forms .fields .SignatureFormFieldBuilder ;
33
+ import com .itextpdf .forms .form .element .SignatureFieldAppearance ;
33
34
import com .itextpdf .io .image .ImageDataFactory ;
34
35
import com .itextpdf .commons .utils .MessageFormatUtil ;
35
36
import com .itextpdf .kernel .colors .ColorConstants ;
@@ -575,6 +576,47 @@ public void signExistedSignatureFieldTest() throws IOException, GeneralSecurityE
575
576
compareSignatureAppearances (dest , SOURCE_FOLDER + "cmp_" + fileName );
576
577
}
577
578
579
+ @ Test
580
+ public void reuseAppearanceTest () throws GeneralSecurityException ,
581
+ IOException , InterruptedException {
582
+ // Field is not merged with widget and has /P key
583
+ String src = SOURCE_FOLDER + "emptyFieldNotMerged.pdf" ;
584
+ String fileName = "reuseAppearance.pdf" ;
585
+ testReuseAppearance (src , fileName , false , true , false );
586
+ }
587
+
588
+ @ Test
589
+ public void reuseAppearanceDeprecatedTest () throws GeneralSecurityException ,
590
+ IOException , InterruptedException {
591
+ // Field is not merged with widget and has /P key
592
+ String src = SOURCE_FOLDER + "emptyFieldNotMerged.pdf" ;
593
+ String fileName = "reuseAppearanceDeprecated.pdf" ;
594
+ testReuseAppearance (src , fileName , true , false , true );
595
+ }
596
+
597
+ @ Test
598
+ public void reuseAppearanceCompatibilityTest () throws GeneralSecurityException ,
599
+ IOException , InterruptedException {
600
+ // Field is not merged with widget and has /P key
601
+ String src = SOURCE_FOLDER + "emptyFieldNotMerged.pdf" ;
602
+ String fileName = "reuseAppearanceCompatibility.pdf" ;
603
+ testReuseAppearance (src , fileName , true , true , false );
604
+ }
605
+
606
+ @ Test
607
+ public void fieldLayersTest () throws IOException , GeneralSecurityException {
608
+ String src = SOURCE_FOLDER + "noSignatureField.pdf" ;
609
+ String fileName = "fieldLayersTest.pdf" ;
610
+ testLayers (src , fileName , false );
611
+ }
612
+
613
+ @ Test
614
+ public void deprecatedLayersTest () throws IOException , GeneralSecurityException {
615
+ String src = SOURCE_FOLDER + "noSignatureField.pdf" ;
616
+ String fileName = "deprecatedLayersTest.pdf" ;
617
+ testLayers (src , fileName , true );
618
+ }
619
+
578
620
private static void compareSignatureAppearances (String outPdf , String cmpPdf ) throws IOException {
579
621
ITextTest .printOutCmpPdfNameAndDir (outPdf , cmpPdf );
580
622
try (PdfDocument outDoc = new PdfDocument (new PdfReader (outPdf ))) {
@@ -590,6 +632,94 @@ private static void compareSignatureAppearances(String outPdf, String cmpPdf) th
590
632
}
591
633
}
592
634
635
+ private void testReuseAppearance (String src , String fileName , boolean useDeprecated , boolean fieldReuseAp ,
636
+ boolean deprecatedReuseAp ) throws IOException , GeneralSecurityException , InterruptedException {
637
+ String cmp = SOURCE_FOLDER + "cmp_" + fileName ;
638
+ String dest = DESTINATION_FOLDER + fileName ;
639
+ String fieldName = "Signature1" ;
640
+
641
+ PdfSigner signer = new PdfSigner (new PdfReader (src ), new FileOutputStream (dest ), new StampingProperties ());
642
+ signer .setFieldName (fieldName );
643
+ signer .getSignatureField ().setReuseAppearance (fieldReuseAp );
644
+ if (useDeprecated ) {
645
+ signer .getSignatureAppearance ().setReuseAppearance (deprecatedReuseAp );
646
+ }
647
+
648
+ signer .setReason ("Test 1" ).setLocation ("TestCity" )
649
+ .setSignatureAppearance (new SignatureFieldAppearance (fieldName )
650
+ .setContent ("New appearance" ).setFontColor (ColorConstants .GREEN ));
651
+
652
+ IExternalSignature pks = new PrivateKeySignature (pk , DigestAlgorithms .SHA256 , FACTORY .getProviderName ());
653
+ signer .signDetached (new BouncyCastleDigest (), pks , chain , null , null , null , 0 , PdfSigner .CryptoStandard .CADES );
654
+
655
+ Assert .assertNull (new CompareTool ().compareVisually (dest , cmp , DESTINATION_FOLDER , "diff_" ));
656
+ }
657
+
658
+ private void testLayers (String src , String fileName , boolean useDeprecated )
659
+ throws IOException , GeneralSecurityException {
660
+ String dest = DESTINATION_FOLDER + fileName ;
661
+ String fieldName = "Signature1" ;
662
+
663
+ PdfSigner signer = new PdfSigner (new PdfReader (src ), new FileOutputStream (dest ), new StampingProperties ());
664
+ signer .setFieldName (fieldName );
665
+ signer .setPageRect (new Rectangle (250 , 500 , 100 , 100 )).setReason ("Test 1" ).setLocation ("TestCity" )
666
+ .setSignatureAppearance (new SignatureFieldAppearance (fieldName ));
667
+
668
+ PdfFormXObject layer0 = new PdfFormXObject (new Rectangle (0 , 0 , 100 , 100 ));
669
+ // Draw pink rectangle with blue border
670
+ new PdfCanvas (layer0 , signer .getDocument ())
671
+ .saveState ()
672
+ .setFillColor (ColorConstants .PINK )
673
+ .setStrokeColor (ColorConstants .BLUE )
674
+ .rectangle (0 , 0 , 100 , 100 )
675
+ .fillStroke ()
676
+ .restoreState ();
677
+
678
+ PdfFormXObject layer2 = new PdfFormXObject (new Rectangle (0 , 0 , 100 , 100 ));
679
+ // Draw yellow circle with gray border
680
+ new PdfCanvas (layer2 , signer .getDocument ())
681
+ .saveState ()
682
+ .setFillColor (ColorConstants .YELLOW )
683
+ .setStrokeColor (ColorConstants .DARK_GRAY )
684
+ .circle (50 , 50 , 50 )
685
+ .fillStroke ()
686
+ .restoreState ();
687
+
688
+ signer .getSignatureField ().setBackgroundLayer (layer0 ).setSignatureAppearanceLayer (layer2 );
689
+
690
+ if (useDeprecated ) {
691
+ // Creating the appearance
692
+ PdfSignatureAppearance appearance = signer .getSignatureAppearance ();
693
+
694
+ PdfFormXObject deprecatedLayer0 = appearance .getLayer0 ();
695
+ // Draw yellow rectangle with gray border
696
+ new PdfCanvas (deprecatedLayer0 , signer .getDocument ())
697
+ .saveState ()
698
+ .setFillColor (ColorConstants .YELLOW )
699
+ .setStrokeColor (ColorConstants .DARK_GRAY )
700
+ .rectangle (0 , 0 , 100 , 100 )
701
+ .fillStroke ()
702
+ .restoreState ();
703
+
704
+ PdfFormXObject deprecatedLayer2 = appearance .getLayer2 ();
705
+ // Draw pink circle with blue border
706
+ new PdfCanvas (deprecatedLayer2 , signer .getDocument ())
707
+ .saveState ()
708
+ .setFillColor (ColorConstants .PINK )
709
+ .setStrokeColor (ColorConstants .BLUE )
710
+ .circle (50 , 50 , 50 )
711
+ .fillStroke ()
712
+ .restoreState ();
713
+ }
714
+
715
+ // Signing
716
+ IExternalSignature pks = new PrivateKeySignature (pk , DigestAlgorithms .SHA256 ,
717
+ FACTORY .getProviderName ());
718
+ signer .signDetached (new BouncyCastleDigest (), pks , chain , null , null , null , 0 , PdfSigner .CryptoStandard .CADES );
719
+
720
+ compareSignatureAppearances (dest , SOURCE_FOLDER + "cmp_" + fileName );
721
+ }
722
+
593
723
private void testSignatureOnRotatedPage (int pageNum , PdfSignatureAppearance .RenderingMode renderingMode ,
594
724
StringBuilder assertionResults ) throws IOException , GeneralSecurityException , InterruptedException {
595
725
String fileName = "signaturesOnRotatedPages" + pageNum + "_mode_" + renderingMode .name () + ".pdf" ;
0 commit comments