17
17
18
18
import android .content .Context ;
19
19
import android .graphics .Bitmap ;
20
- import android .util .Log ;
21
20
22
21
import androidx .test .platform .app .InstrumentationRegistry ;
23
22
24
23
import java .io .File ;
25
- import java .io .FileOutputStream ;
26
24
import java .io .IOException ;
27
- import java .io . InputStream ;
28
- import java .net . URL ;
25
+ import java .util . HashSet ;
26
+ import java .util . Set ;
29
27
30
28
import com .tom_roush .pdfbox .android .PDFBoxResourceLoader ;
29
+ import com .tom_roush .pdfbox .android .TestResourceGenerator ;
31
30
import com .tom_roush .pdfbox .cos .COSArray ;
32
31
import com .tom_roush .pdfbox .cos .COSBase ;
33
32
import com .tom_roush .pdfbox .cos .COSDictionary ;
34
33
import com .tom_roush .pdfbox .cos .COSName ;
35
34
import com .tom_roush .pdfbox .cos .COSObject ;
36
- import com .tom_roush .pdfbox .io .IOUtils ;
37
35
import com .tom_roush .pdfbox .io .MemoryUsageSetting ;
38
36
import com .tom_roush .pdfbox .pdmodel .PDDocument ;
39
37
import com .tom_roush .pdfbox .pdmodel .PDDocumentCatalog ;
45
43
import com .tom_roush .pdfbox .pdmodel .interactive .documentnavigation .destination .PDPageFitDestination ;
46
44
import com .tom_roush .pdfbox .rendering .PDFRenderer ;
47
45
48
- import junit .framework .TestCase ;
46
+ import org .junit .Before ;
47
+ import org .junit .Test ;
48
+
49
+ import static org .junit .Assert .assertEquals ;
50
+ import static org .junit .Assert .assertTrue ;
51
+ import static org .junit .Assume .assumeNotNull ;
49
52
50
53
/**
51
54
* Test suite for PDFMergerUtility.
52
55
*
53
56
* @author Maruan Sahyoun (PDF files)
54
57
* @author Tilman Hausherr (code)
55
58
*/
56
- public class PDFMergerUtilityTest extends TestCase
59
+ public class PDFMergerUtilityTest
57
60
{
58
61
final String SRCDIR = "pdfbox/input/merge" ;
59
62
String TARGETTESTDIR ;
60
- private File TARGETPDFDIR ;
63
+ private static File TARGETPDFDIR ;
61
64
final int DPI = 96 ;
62
65
private Context testContext ;
63
66
64
- @ Override
65
- protected void setUp () throws Exception
67
+ @ Before
68
+ public void setUp () throws Exception
66
69
{
67
- super .setUp ();
68
-
69
70
testContext = InstrumentationRegistry .getInstrumentation ().getContext ();
70
71
PDFBoxResourceLoader .init (testContext );
71
72
TARGETTESTDIR = testContext .getCacheDir () + "/pdfbox-test-output/merge/" ;
@@ -91,6 +92,7 @@ protected void setUp() throws Exception
91
92
*
92
93
* @throws IOException if something goes wrong.
93
94
*/
95
+ @ Test
94
96
public void testPDFMergerUtility () throws IOException
95
97
{
96
98
checkMergeIdentical ("PDFBox.GlobalResourceMergeTest.Doc01.decoded.pdf" ,
@@ -112,6 +114,7 @@ public void testPDFMergerUtility() throws IOException
112
114
*
113
115
* @throws IOException if something goes wrong.
114
116
*/
117
+ @ Test
115
118
public void testJpegCcitt () throws IOException
116
119
{
117
120
checkMergeIdentical ("jpegrgb.pdf" ,
@@ -127,6 +130,7 @@ public void testJpegCcitt() throws IOException
127
130
}
128
131
129
132
// see PDFBOX-2893
133
+ @ Test
130
134
public void testPDFMergerUtility2 () throws IOException
131
135
{
132
136
checkMergeIdentical ("PDFBox.GlobalResourceMergeTest.Doc01.pdf" ,
@@ -146,6 +150,7 @@ public void testPDFMergerUtility2() throws IOException
146
150
*
147
151
* @throws IOException
148
152
*/
153
+ @ Test
149
154
public void testPDFMergerOpenAction () throws IOException
150
155
{
151
156
PDDocument doc1 = new PDDocument ();
@@ -184,47 +189,117 @@ public void testPDFMergerOpenAction() throws IOException
184
189
*
185
190
* @throws IOException
186
191
*/
192
+ @ Test
187
193
public void testStructureTreeMerge () throws IOException
188
194
{
189
- File pdfFile = new File (TARGETPDFDIR , "PDFBOX-3999-GeneralForbearance.pdf" );
195
+ File inputPdf = TestResourceGenerator .downloadTestResource (TARGETPDFDIR , "PDFBOX-3999-GeneralForbearance.pdf" , "https://issues.apache.org/jira/secure/attachment/12896905/GeneralForbearance.pdf" );
196
+ assumeNotNull (inputPdf );
190
197
191
- if (!pdfFile .exists ())
192
- {
193
- try
194
- {
195
- Log .i ("PdfBox-Android" , "PDF not cached, Downloading PDF for PDFMergerUtility.testStructureTreeMerge" );
196
- InputStream pdfUrlStream = new URL (
197
- "https://issues.apache.org/jira/secure/attachment/12896905/GeneralForbearance.pdf" )
198
- .openStream ();
199
- IOUtils .copy (pdfUrlStream , new FileOutputStream (pdfFile ));
200
- }
201
- catch (Exception e )
202
- {
203
- Log .w ("PdfBox-Android" , "Unable to download test PDF. Skipping test PDFMergerUtility.testStructureTreeMerge" );
204
- return ;
205
- }
206
- }
198
+ PDFMergerUtility pdfMergerUtility = new PDFMergerUtility ();
199
+ PDDocument src = PDDocument .load (inputPdf );
200
+ PDDocument dst = PDDocument .load (inputPdf );
201
+ pdfMergerUtility .appendDocument (dst , src );
202
+ src .close ();
203
+ dst .save (new File (TARGETTESTDIR , "PDFBOX-3999-GeneralForbearance-merged.pdf" ));
204
+ dst .close ();
205
+
206
+ PDDocument doc = PDDocument .load (new File (TARGETTESTDIR , "PDFBOX-3999-GeneralForbearance-merged.pdf" ));
207
+ PDPageTree pageTree = doc .getPages ();
208
+
209
+ // check for orphan pages in the StructTreeRoot/K and StructTreeRoot/ParentTree trees.
210
+ PDStructureTreeRoot structureTreeRoot = doc .getDocumentCatalog ().getStructureTreeRoot ();
211
+ checkElement (pageTree , structureTreeRoot .getParentTree ().getCOSObject ());
212
+ checkElement (pageTree , structureTreeRoot .getK ());
213
+ }
214
+
215
+ /**
216
+ * PDFBOX-3999: check that no streams are kept from the source document by the destination
217
+ * document, despite orphan annotations remaining in the structure tree.
218
+ *
219
+ * @throws IOException
220
+ */
221
+ @ Test
222
+ public void testStructureTreeMerge2 () throws IOException
223
+ {
224
+ File inputPdf = TestResourceGenerator .downloadTestResource (TARGETPDFDIR , "PDFBOX-3999-GeneralForbearance.pdf" , "https://issues.apache.org/jira/secure/attachment/12896905/GeneralForbearance.pdf" );
225
+ assumeNotNull (inputPdf );
207
226
208
227
PDFMergerUtility pdfMergerUtility = new PDFMergerUtility ();
209
- PDDocument src = PDDocument .load (pdfFile );
210
- PDDocument dst = PDDocument .load (pdfFile );
228
+ PDDocument doc = PDDocument .load (inputPdf );
229
+ doc .getDocumentCatalog ().getAcroForm ().flatten ();
230
+ doc .save (new File (TARGETTESTDIR , "PDFBOX-3999-GeneralForbearance-flattened.pdf" ));
231
+
232
+ ElementCounter elementCounter = new ElementCounter ();
233
+ elementCounter .walk (doc .getDocumentCatalog ().getStructureTreeRoot ().getK ());
234
+ int singleCnt = elementCounter .cnt ;
235
+ int singleSetSize = elementCounter .set .size ();
236
+
237
+ doc .close ();
238
+
239
+ PDDocument src = PDDocument .load (new File (TARGETTESTDIR , "PDFBOX-3999-GeneralForbearance-flattened.pdf" ));
240
+ PDDocument dst = PDDocument .load (new File (TARGETTESTDIR , "PDFBOX-3999-GeneralForbearance-flattened.pdf" ));
211
241
pdfMergerUtility .appendDocument (dst , src );
242
+ // before solving PDFBOX-3999, the close() below brought
243
+ // IOException: COSStream has been closed and cannot be read.
212
244
src .close ();
213
- dst .save (new File (TARGETTESTDIR , "PDFBOX-3999-GovFormPreFlattened -merged.pdf" ));
245
+ dst .save (new File (TARGETTESTDIR , "PDFBOX-3999-GeneralForbearance-flattened -merged.pdf" ));
214
246
dst .close ();
215
247
216
- PDDocument doc = PDDocument .load (new File (TARGETTESTDIR , "PDFBOX-3999-GovFormPreFlattened -merged.pdf" ));
248
+ doc = PDDocument .load (new File (TARGETTESTDIR , "PDFBOX-3999-GeneralForbearance-flattened -merged.pdf" ));
217
249
PDPageTree pageTree = doc .getPages ();
218
250
219
251
// check for orphan pages in the StructTreeRoot/K and StructTreeRoot/ParentTree trees.
220
252
PDStructureTreeRoot structureTreeRoot = doc .getDocumentCatalog ().getStructureTreeRoot ();
221
253
checkElement (pageTree , structureTreeRoot .getParentTree ().getCOSObject ());
222
254
checkElement (pageTree , structureTreeRoot .getK ());
255
+
256
+ // Assume that the merged tree has double element count
257
+ elementCounter = new ElementCounter ();
258
+ elementCounter .walk (structureTreeRoot .getK ());
259
+ assertEquals (singleCnt * 2 , elementCounter .cnt );
260
+ assertEquals (singleSetSize * 2 , elementCounter .set .size ());
261
+
262
+ doc .close ();
263
+ }
264
+
265
+ private class ElementCounter
266
+ {
267
+ int cnt = 0 ;
268
+ Set <COSBase > set = new HashSet <COSBase >();
269
+
270
+ void walk (COSBase base )
271
+ {
272
+ if (base instanceof COSArray )
273
+ {
274
+ for (COSBase base2 : (COSArray ) base )
275
+ {
276
+ if (base2 instanceof COSObject )
277
+ {
278
+ base2 = ((COSObject ) base2 ).getObject ();
279
+ }
280
+ walk (base2 );
281
+ }
282
+ }
283
+ else if (base instanceof COSDictionary )
284
+ {
285
+ COSDictionary kdict = (COSDictionary ) base ;
286
+ if (kdict .containsKey (COSName .PG ))
287
+ {
288
+ ++cnt ;
289
+ set .add (kdict );
290
+ }
291
+ if (kdict .containsKey (COSName .K ))
292
+ {
293
+ walk (kdict .getDictionaryObject (COSName .K ));
294
+ }
295
+ }
296
+ }
223
297
}
224
298
225
299
// Each element can be an array, a dictionary or a number.
226
- // See PDF specification Table 37 – Entries in a number tree node dictionary
227
- // See PDF specification Table 322 – Entries in the structure tree root
300
+ // See PDF specification Table 37 - Entries in a number tree node dictionary
301
+ // See PDF specification Table 322 - Entries in the structure tree root
302
+ // See PDF specification Table 323 - Entries in a structure element dictionary
228
303
// example of file with /Kids: 000153.pdf 000208.pdf 000314.pdf 000359.pdf 000671.pdf
229
304
// from digitalcorpora site
230
305
private void checkElement (PDPageTree pageTree , COSBase base )
0 commit comments