Skip to content

Commit c3e97d5

Browse files
SnipxiText-CI
authored andcommitted
Add tests demonstrating problems with annotation relationship incorrectly transferred during copying
If annotations refer to other annotations with Popup or IRT keys, this relationship is not correctly transferred at the moment and the result is annotation on one page referring to an annotation on another page, which for IRT key for example is forbidden by the specification DEVSIX-3585
1 parent eb43d78 commit c3e97d5

File tree

4 files changed

+149
-1
lines changed

4 files changed

+149
-1
lines changed

kernel/src/main/java/com/itextpdf/kernel/pdf/annot/PdfAnnotation.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ This file is part of the iText (R) project.
6363
import com.itextpdf.kernel.pdf.PdfString;
6464
import com.itextpdf.kernel.pdf.filespec.PdfFileSpec;
6565
import com.itextpdf.kernel.pdf.layer.IPdfOCG;
66+
6667
import org.slf4j.Logger;
6768
import org.slf4j.LoggerFactory;
6869

@@ -916,7 +917,7 @@ public PdfAnnotation setTitle(PdfString title) {
916917
* title bar of the annotation’s pop-up window when open and active. For movie annotation Movie actions
917918
* (ISO-320001 12.6.4.9, "Movie Actions") may use this title to reference the movie annotation.
918919
*
919-
* @return {@link PdfString} which value is an annotation title or null if it isn't specifed.
920+
* @return {@link PdfString} which value is an annotation title or null if it isn't specified.
920921
*/
921922
public PdfString getTitle() {
922923
return getPdfObject().getAsString(PdfName.T);
Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
/*
2+
This file is part of the iText (R) project.
3+
Copyright (c) 1998-2019 iText Group NV
4+
Authors: iText Software.
5+
6+
This program is offered under a commercial and under the AGPL license.
7+
For commercial licensing, contact us at https://itextpdf.com/sales. For AGPL licensing, see below.
8+
9+
AGPL licensing:
10+
This program is free software: you can redistribute it and/or modify
11+
it under the terms of the GNU Affero General Public License as published by
12+
the Free Software Foundation, either version 3 of the License, or
13+
(at your option) any later version.
14+
15+
This program is distributed in the hope that it will be useful,
16+
but WITHOUT ANY WARRANTY; without even the implied warranty of
17+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18+
GNU Affero General Public License for more details.
19+
20+
You should have received a copy of the GNU Affero General Public License
21+
along with this program. If not, see <https://www.gnu.org/licenses/>.
22+
*/
23+
package com.itextpdf.kernel.pdf.copy;
24+
25+
import com.itextpdf.io.util.MessageFormatUtil;
26+
import com.itextpdf.kernel.pdf.PdfDictionary;
27+
import com.itextpdf.kernel.pdf.PdfDocument;
28+
import com.itextpdf.kernel.pdf.PdfPage;
29+
import com.itextpdf.kernel.pdf.PdfReader;
30+
import com.itextpdf.kernel.pdf.PdfWriter;
31+
import com.itextpdf.kernel.pdf.annot.PdfAnnotation;
32+
import com.itextpdf.kernel.pdf.annot.PdfMarkupAnnotation;
33+
import com.itextpdf.kernel.pdf.annot.PdfPopupAnnotation;
34+
import com.itextpdf.test.ExtendedITextTest;
35+
import com.itextpdf.test.annotations.type.IntegrationTest;
36+
37+
import java.io.IOException;
38+
import org.junit.Assert;
39+
import org.junit.BeforeClass;
40+
import org.junit.Rule;
41+
import org.junit.Test;
42+
import org.junit.experimental.categories.Category;
43+
import org.junit.rules.ExpectedException;
44+
45+
@Category(IntegrationTest.class)
46+
public class PdfAnnotationCopyingTest extends ExtendedITextTest {
47+
48+
public static final String destinationFolder = "./target/test/com/itextpdf/kernel/pdf/PdfAnnotationCopyingTest/";
49+
public static final String sourceFolder = "./src/test/resources/com/itextpdf/kernel/pdf/PdfAnnotationCopyingTest/";
50+
51+
@Rule
52+
public ExpectedException junitExpectedException = ExpectedException.none();
53+
54+
@BeforeClass
55+
public static void beforeClass() {
56+
createOrClearDestinationFolder(destinationFolder);
57+
}
58+
59+
@Test
60+
// TODO remove expected exception and thus enable assertions when DEVSIX-3585 is implemented
61+
public void testCopyingPageWithAnnotationContainingPopupKey() throws IOException {
62+
junitExpectedException.expect(AssertionError.class);
63+
64+
String inFilePath = sourceFolder + "annotation-with-popup.pdf";
65+
String outFilePath = destinationFolder + "copy-annotation-with-popup.pdf";
66+
PdfDocument originalDocument = new PdfDocument(new PdfReader(inFilePath));
67+
PdfDocument outDocument = new PdfDocument(new PdfWriter(outFilePath));
68+
69+
originalDocument.copyPagesTo(1, 1, outDocument);
70+
// During the second copy call we have to rebuild/preserve all the annotation relationship (Popup in this case),
71+
// so that we don't end up with annotation on one page referring to an annotation on another page as its popup
72+
// or as its parent
73+
originalDocument.copyPagesTo(1, 1, outDocument);
74+
75+
originalDocument.close();
76+
outDocument.close();
77+
78+
outDocument = new PdfDocument(new PdfReader(outFilePath));
79+
for (int pageNum = 1; pageNum <= outDocument.getNumberOfPages(); pageNum++) {
80+
PdfPage page = outDocument.getPage(pageNum);
81+
Assert.assertEquals(2, page.getAnnotsSize());
82+
Assert.assertEquals(2, page.getAnnotations().size());
83+
boolean foundMarkupAnnotation = false;
84+
for (PdfAnnotation annotation : page.getAnnotations()) {
85+
PdfDictionary annotationPageDict = annotation.getPageObject();
86+
if (annotationPageDict != null) {
87+
Assert.assertSame(page.getPdfObject(), annotationPageDict);
88+
}
89+
if (annotation instanceof PdfMarkupAnnotation) {
90+
foundMarkupAnnotation = true;
91+
PdfPopupAnnotation popup = ((PdfMarkupAnnotation) annotation).getPopup();
92+
Assert.assertTrue(MessageFormatUtil.format(
93+
"Popup reference must point to annotation present on the same page (# {0})", pageNum),
94+
page.containsAnnotation(popup));
95+
PdfDictionary parentAnnotation = popup.getParentObject();
96+
Assert.assertSame("Popup annotation parent must point to the annotation that specified it as Popup",
97+
annotation.getPdfObject(), parentAnnotation);
98+
}
99+
}
100+
Assert.assertTrue("Markup annotation expected to be present but not found", foundMarkupAnnotation);
101+
}
102+
outDocument.close();
103+
}
104+
105+
@Test
106+
// TODO remove expected exception and thus enable assertions when DEVSIX-3585 is implemented
107+
public void testCopyingPageWithAnnotationContainingIrtKey() throws IOException {
108+
junitExpectedException.expect(AssertionError.class);
109+
110+
String inFilePath = sourceFolder + "annotation-with-irt.pdf";
111+
String outFilePath = destinationFolder + "copy-annotation-with-irt.pdf";
112+
PdfDocument originalDocument = new PdfDocument(new PdfReader(inFilePath));
113+
PdfDocument outDocument = new PdfDocument(new PdfWriter(outFilePath));
114+
115+
originalDocument.copyPagesTo(1, 1, outDocument);
116+
// During the second copy call we have to rebuild/preserve all the annotation relationship (IRT in this case),
117+
// so that we don't end up with annotation on one page referring to an annotation on another page as its IRT
118+
// or as its parent
119+
originalDocument.copyPagesTo(1, 1, outDocument);
120+
121+
originalDocument.close();
122+
outDocument.close();
123+
124+
outDocument = new PdfDocument(new PdfReader(outFilePath));
125+
for (int pageNum = 1; pageNum <= outDocument.getNumberOfPages(); pageNum++) {
126+
PdfPage page = outDocument.getPage(pageNum);
127+
Assert.assertEquals(4, page.getAnnotsSize());
128+
Assert.assertEquals(4, page.getAnnotations().size());
129+
boolean foundMarkupAnnotation = false;
130+
for (PdfAnnotation annotation : page.getAnnotations()) {
131+
PdfDictionary annotationPageDict = annotation.getPageObject();
132+
if (annotationPageDict != null) {
133+
Assert.assertSame(page.getPdfObject(), annotationPageDict);
134+
}
135+
if (annotation instanceof PdfMarkupAnnotation) {
136+
foundMarkupAnnotation = true;
137+
PdfDictionary inReplyTo = ((PdfMarkupAnnotation) annotation).getInReplyToObject();
138+
Assert.assertTrue("IRT reference must point to annotation present on the same page",
139+
page.containsAnnotation(PdfAnnotation.makeAnnotation(inReplyTo)));
140+
}
141+
}
142+
Assert.assertTrue("Markup annotation expected to be present but not found", foundMarkupAnnotation);
143+
}
144+
outDocument.close();
145+
}
146+
147+
}

0 commit comments

Comments
 (0)