-
Notifications
You must be signed in to change notification settings - Fork 18
/
StampNoChange.java
226 lines (215 loc) · 8.44 KB
/
StampNoChange.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
package mkl.testarea.itext7.stamp;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.GeneralSecurityException;
import org.junit.BeforeClass;
import org.junit.Test;
import com.itextpdf.kernel.pdf.PdfDictionary;
import com.itextpdf.kernel.pdf.PdfDocument;
import com.itextpdf.kernel.pdf.PdfReader;
import com.itextpdf.kernel.pdf.PdfWriter;
import com.itextpdf.kernel.pdf.StampingProperties;
import com.itextpdf.kernel.pdf.tagging.PdfStructTreeRoot;
import com.itextpdf.signatures.IExternalSignatureContainer;
import com.itextpdf.signatures.PdfSigner;
/**
* @author mkl
*/
public class StampNoChange
{
final static File RESULT_FOLDER = new File("target/test-outputs", "stamp");
@BeforeClass
public static void setUpBeforeClass() throws Exception
{
RESULT_FOLDER.mkdirs();
}
/**
* <a href="http://stackoverflow.com/questions/39937615/itext-7-pdfdictionary-released-prematurely">
* iText 7: PdfDictionary released prematurely
* </a>
* <br/>
* <a href="https://mega.nz/#!bh90VI4S!bbjRXOm3EAc2BA-OQPsJKZ5Sei9Xfu9JQl72M0YnYg0">
* prematureReleaseExample.pdf
* </a>
* <p>
* Indeed, this simple no-change stamping fails with a {@link NullPointerException}
* due to a structure tree node already been flushed. The cause is that a few nodes
* appear multiple times in the structure tree of the document at hand.
* </p>
* <p>
* iText can be hardened against this problem by adding an `isFlushed` test to
* {@link PdfStructTreeRoot#flushAllKids(IPdfStructElem)}:
* </p>
* <pre>
* private void flushAllKids(IPdfStructElem elem) {
* for (IPdfStructElem kid : elem.getKids()) {
* if (kid instanceof PdfStructElem) {
* if (!((PdfStructElem) kid).isFlushed())
* {
* flushAllKids(kid);
* ((PdfStructElem) kid).flush();
* }
* }
* }
* }
* </pre>
*/
@Test
public void testPrematureReleaseExample() throws IOException
{
File target = new File(RESULT_FOLDER, "prematureReleaseExample-asis.pdf");
try ( InputStream resource = getClass().getResourceAsStream("prematureReleaseExample.pdf");
OutputStream dest = new FileOutputStream(target))
{
PdfReader reader = new PdfReader(resource);
PdfWriter writer = new PdfWriter(dest);
PdfDocument doc = new PdfDocument(reader, writer);
doc.close();
}
}
/**
* <a href="https://stackoverflow.com/questions/47281240/append-mode-requires-a-document-without-errors-even-if-recovery-is-possible">
* Append mode requires a document without errors, even if recovery is possible
* </a>
* <br/>
* <a href="https://www.dropbox.com/s/i7eeamw9xouf76l/word.pdf?dl=0">
* word.pdf
* </a>
* <p>
* This test reproduces the issue observed by the OP: A first stamping
* seems to work but the second one throws an error. The cause is that
* the first stamping already created a broken result which makes the
* second one stumble.
* </p>
* @see #testAppendWordSigning()
* @see #testNoAppendWord()
*/
@Test
public void testAppendWord() throws IOException
{
File target = new File(RESULT_FOLDER, "word-append.pdf");
try ( InputStream resource = getClass().getResourceAsStream("word.pdf");
OutputStream dest = new FileOutputStream(target))
{
PdfReader reader = new PdfReader(resource);
PdfWriter writer = new PdfWriter(dest);
PdfDocument doc = new PdfDocument(reader, writer, new StampingProperties().useAppendMode());
doc.close();
}
File secondTarget = new File(RESULT_FOLDER, "word-append-again.pdf");
try ( OutputStream dest = new FileOutputStream(secondTarget))
{
PdfReader reader = new PdfReader(target);
PdfWriter writer = new PdfWriter(dest);
PdfDocument doc = new PdfDocument(reader, writer, new StampingProperties().useAppendMode());
doc.close();
}
}
/**
* <a href="https://stackoverflow.com/questions/47281240/append-mode-requires-a-document-without-errors-even-if-recovery-is-possible">
* Append mode requires a document without errors, even if recovery is possible
* </a>
* <br/>
* <a href="https://www.dropbox.com/s/i7eeamw9xouf76l/word.pdf?dl=0">
* word.pdf
* </a>
* <p>
* This test shows that the issue is not bound to the signing use case
* during which the OP observed it: Signing simply produces the same
* fault in the result document as arbitrary stamping does in the test
* {@link #testAppendWord()}.
* </p>
*/
@Test
public void testAppendWordSigning() throws IOException, GeneralSecurityException
{
File target = new File(RESULT_FOLDER, "word-signed.pdf");
try ( InputStream resource = getClass().getResourceAsStream("word.pdf");
OutputStream dest = new FileOutputStream(target))
{
PdfReader reader = new PdfReader(resource);
PdfSigner signer = new PdfSigner(reader, dest, new StampingProperties().useAppendMode());
signer.signExternalContainer(new IExternalSignatureContainer() {
@Override
public byte[] sign(InputStream data) throws GeneralSecurityException {
return "dummy signature".getBytes();
}
@Override
public void modifySigningDictionary(PdfDictionary signDic) {
}
}, 4096);
}
}
/**
* <a href="https://stackoverflow.com/questions/47281240/append-mode-requires-a-document-without-errors-even-if-recovery-is-possible">
* Append mode requires a document without errors, even if recovery is possible
* </a>
* <br/>
* <a href="https://www.dropbox.com/s/i7eeamw9xouf76l/word.pdf?dl=0">
* word.pdf
* </a>
* <p>
* This test shows that the issue is related to the append
* mode: Without appending everything works just fine.
* </p>
* @see #testAppendWord()
*/
@Test
public void testNoAppendWord() throws IOException
{
File target = new File(RESULT_FOLDER, "word-stamp.pdf");
try ( InputStream resource = getClass().getResourceAsStream("word.pdf");
OutputStream dest = new FileOutputStream(target))
{
PdfReader reader = new PdfReader(resource);
PdfWriter writer = new PdfWriter(dest);
PdfDocument doc = new PdfDocument(reader, writer);
doc.close();
}
File secondTarget = new File(RESULT_FOLDER, "word-stamp-again.pdf");
try ( OutputStream dest = new FileOutputStream(secondTarget))
{
PdfReader reader = new PdfReader(target);
PdfWriter writer = new PdfWriter(dest);
PdfDocument doc = new PdfDocument(reader, writer);
doc.close();
}
}
/**
* <a href="https://stackoverflow.com/questions/58630959/error-when-reading-pdf-file-generated-by-ms-office">
* Error when reading PDF file generated by MS Office
* </a>
* <br/>
* <a href="https://drive.google.com/open?id=1fnwtXfEGg6BIeVuAi-l28Ol_dxbCd12F">
* TEST.pdf
* </a>
* <p>
* This test shows how to append to a PDF even though iText has
* found cross reference issues in it. Beware, the result file
* still has cross reference issues, so it processing it further
* may have unexpected results.
* </p>
*/
@Test
public void testAppendTest() throws IOException
{
File target = new File(RESULT_FOLDER, "TEST-append.pdf");
try ( InputStream resource = getClass().getResourceAsStream("TEST.pdf");
OutputStream dest = new FileOutputStream(target))
{
@SuppressWarnings("serial")
PdfReader reader = new PdfReader(resource) {
@Override
public boolean hasRebuiltXref() {
return false;
}
};
PdfWriter writer = new PdfWriter(dest);
PdfDocument doc = new PdfDocument(reader, writer, new StampingProperties().useAppendMode());
doc.close();
}
}
}