From 1e2c13561799a767f31a6ace90b619b665835f02 Mon Sep 17 00:00:00 2001 From: Simon Steiner Date: Tue, 29 Oct 2024 15:39:22 +0000 Subject: [PATCH] FOP-3215: Allow object-streams with signing and encryption --- .../java/org/apache/fop/pdf/PDFDocument.java | 3 -- .../org/apache/fop/pdf/PDFEncryptionJCE.java | 3 ++ .../main/java/org/apache/fop/pdf/PDFRoot.java | 4 ++ .../java/org/apache/fop/pdf/PDFSignature.java | 6 ++- .../fop/pdf/PDFObjectStreamTestCase.java | 19 ++++++-- .../apache/fop/pdf/PDFSigningTestCase.java | 46 +++++++++++++------ 6 files changed, 59 insertions(+), 22 deletions(-) diff --git a/fop-core/src/main/java/org/apache/fop/pdf/PDFDocument.java b/fop-core/src/main/java/org/apache/fop/pdf/PDFDocument.java index acc41032f..8f4ce7347 100644 --- a/fop-core/src/main/java/org/apache/fop/pdf/PDFDocument.java +++ b/fop-core/src/main/java/org/apache/fop/pdf/PDFDocument.java @@ -1202,9 +1202,6 @@ public class PDFDocument { if (objectStreamsEnabled && linearizationEnabled) { throw new UnsupportedOperationException("Linearization and use-object-streams can't be both enabled"); } - if (objectStreamsEnabled && isEncryptionActive()) { - throw new UnsupportedOperationException("Encryption and use-object-streams can't be both enabled"); - } return objectStreamsEnabled || (accessibilityEnabled && versionController.getPDFVersion().compareTo(Version.V1_5) >= 0 && !isLinearizationEnabled()); } diff --git a/fop-core/src/main/java/org/apache/fop/pdf/PDFEncryptionJCE.java b/fop-core/src/main/java/org/apache/fop/pdf/PDFEncryptionJCE.java index ff2aac68f..edebbb23a 100644 --- a/fop-core/src/main/java/org/apache/fop/pdf/PDFEncryptionJCE.java +++ b/fop-core/src/main/java/org/apache/fop/pdf/PDFEncryptionJCE.java @@ -837,4 +837,7 @@ public final class PDFEncryptionJCE extends PDFObject implements PDFEncryption { return pdfVersion; } + public boolean supportsObjectStream() { + return false; + } } diff --git a/fop-core/src/main/java/org/apache/fop/pdf/PDFRoot.java b/fop-core/src/main/java/org/apache/fop/pdf/PDFRoot.java index 7b6038467..45b0ee182 100644 --- a/fop-core/src/main/java/org/apache/fop/pdf/PDFRoot.java +++ b/fop-core/src/main/java/org/apache/fop/pdf/PDFRoot.java @@ -348,4 +348,8 @@ public class PDFRoot extends PDFDictionary { af.add(fileSpec); fileSpec.put("AFRelationship", new PDFName("Data")); } + + public boolean supportsObjectStream() { + return !getDocument().isEncryptionActive(); + } } diff --git a/fop-core/src/main/java/org/apache/fop/pdf/PDFSignature.java b/fop-core/src/main/java/org/apache/fop/pdf/PDFSignature.java index 1ff785553..f5384be53 100644 --- a/fop-core/src/main/java/org/apache/fop/pdf/PDFSignature.java +++ b/fop-core/src/main/java/org/apache/fop/pdf/PDFSignature.java @@ -120,7 +120,11 @@ public class PDFSignature { startOfDocMDP = countingOutputStream.getByteCount(); return super.output(stream); } - throw new IOException("Disable pdf linearization and use-object-streams"); + throw new IOException("Disable pdf linearization"); + } + + public boolean supportsObjectStream() { + return false; } } diff --git a/fop-core/src/test/java/org/apache/fop/pdf/PDFObjectStreamTestCase.java b/fop-core/src/test/java/org/apache/fop/pdf/PDFObjectStreamTestCase.java index 702c320a8..537d0216e 100644 --- a/fop-core/src/test/java/org/apache/fop/pdf/PDFObjectStreamTestCase.java +++ b/fop-core/src/test/java/org/apache/fop/pdf/PDFObjectStreamTestCase.java @@ -35,8 +35,22 @@ import org.apache.fop.render.pdf.PDFContentGenerator; public class PDFObjectStreamTestCase { @Test public void testObjectStreamsEnabled() throws IOException { - TimeZone.setDefault(TimeZone.getTimeZone("UTC")); PDFDocument doc = new PDFDocument(""); + String out = buildObjectStreamsPDF(doc); + Assert.assertTrue(out.contains("<<\n /Type /ObjStm\n /N 3\n /First 15\n /Length 260\n>>\n" + + "stream\n8 0\n9 52\n4 121\n<<\n/Producer")); + } + + @Test + public void testObjectStreamsWithEncryption() throws IOException { + PDFDocument doc = new PDFDocument(""); + doc.setEncryption(new PDFEncryptionParams()); + String out = buildObjectStreamsPDF(doc); + Assert.assertTrue(out.contains("<<\n /Type /ObjStm\n /N 3\n /First 16\n /Length 282\n>>\nstream")); + } + + private String buildObjectStreamsPDF(PDFDocument doc) throws IOException { + TimeZone.setDefault(TimeZone.getTimeZone("UTC")); Map> filterMap = new HashMap<>(); List filterList = new ArrayList<>(); filterList.add("null"); @@ -55,7 +69,6 @@ public class PDFObjectStreamTestCase { gen.flushPDFDoc(); doc.outputTrailer(out); Assert.assertTrue(out.toString().contains("/Subtype /Image")); - Assert.assertTrue(out.toString().contains("<<\n /Type /ObjStm\n /N 3\n /First 15\n /Length 260\n>>\n" - + "stream\n8 0\n9 52\n4 121\n<<\n/Producer")); + return out.toString(); } } diff --git a/fop-core/src/test/java/org/apache/fop/pdf/PDFSigningTestCase.java b/fop-core/src/test/java/org/apache/fop/pdf/PDFSigningTestCase.java index fbd11cf79..3e46aa34d 100644 --- a/fop-core/src/test/java/org/apache/fop/pdf/PDFSigningTestCase.java +++ b/fop-core/src/test/java/org/apache/fop/pdf/PDFSigningTestCase.java @@ -41,9 +41,28 @@ import org.apache.fop.fo.pagination.LayoutMasterSetTestCase; public class PDFSigningTestCase { @Test - public void textFO() throws Exception { + public void testFO() throws Exception { ByteArrayOutputStream out = new ByteArrayOutputStream(); - foToOutput(out, MimeConstants.MIME_PDF); + foToOutput(out, false); + String endStr = checkOutput(out); + Assert.assertTrue(endStr.contains("/FT /Sig\n" + + " /Type /Annot\n" + + " /Subtype /Widget\n" + + " /F 132\n" + + " /T (Signature1)\n" + + " /TU (Signature1)\n" + + " /Rect [0 0 0 0]")); + } + + @Test + public void testWithObjectStreams() throws Exception { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + foToOutput(out, true); + String endStr = checkOutput(out); + Assert.assertFalse(endStr.contains("/Subtype /Widget")); + } + + private String checkOutput(ByteArrayOutputStream out) throws Exception { StringTokenizer byteRange = new StringTokenizer(out.toString().split("/ByteRange ")[1]); byteRange.nextToken(); int startOfContents = Integer.parseInt(byteRange.nextToken()); @@ -62,31 +81,28 @@ public class PDFSigningTestCase { String endStr = new String(end); Assert.assertTrue(endStr.contains( "/ByteRange [0 " + startOfContents + " " + endOfContents + " " + sizeOfEnd + "]")); - Assert.assertTrue(endStr.contains("/FT /Sig\n" - + " /Type /Annot\n" - + " /Subtype /Widget\n" - + " /F 132\n" - + " /T (Signature1)\n" - + " /TU (Signature1)\n" - + " /Rect [0 0 0 0]")); + return endStr; } - private void foToOutput(ByteArrayOutputStream out, String mimeFopIf) throws Exception { - FopFactory fopFactory = getFopFactory(); + private void foToOutput(ByteArrayOutputStream out, boolean objectStreams) throws Exception { + FopFactory fopFactory = getFopFactory(objectStreams); FOUserAgent userAgent = fopFactory.newFOUserAgent(); - Fop fop = fopFactory.newFop(mimeFopIf, userAgent, out); + Fop fop = fopFactory.newFop(MimeConstants.MIME_PDF, userAgent, out); Transformer transformer = TransformerFactory.newInstance().newTransformer(); Source src = new StreamSource(LayoutMasterSetTestCase.class.getResourceAsStream("side-regions.fo")); Result res = new SAXResult(fop.getDefaultHandler()); transformer.transform(src, res); } - private FopFactory getFopFactory() throws Exception { + private FopFactory getFopFactory(boolean objectStreams) throws Exception { String pkcs = PDFSigningTestCase.class.getResource("keystore.pkcs12").toString(); String fopxconf = "\n" + " \n" - + " \n" - + " \n" + + " \n"; + if (objectStreams) { + fopxconf += "true"; + } + fopxconf += " \n" + " " + pkcs + "\n" + " \n" + " \n" -- 2.39.5