FOP-3215: Allow object-streams with signing and encryption
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 acc4103..8f4ce73 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 @@
         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 ff2aac6..edebbb2 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 @@
         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 7b60384..45b0ee1 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 @@
         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 1ff7855..f5384be 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 @@
                 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 702c320..537d021 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 @@
 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<String, List<String>> filterMap = new HashMap<>();
         List<String> filterList = new ArrayList<>();
         filterList.add("null");
@@ -55,7 +69,6 @@
         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 fbd11cf..3e46aa3 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 @@
 
 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 @@
         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 = "<fop version=\"1.0\">\n"
                 + "  <renderers>\n"
-                + "    <renderer mime=\"application/pdf\">\n"
-                + "    <sign-params>\n"
+                + "    <renderer mime=\"application/pdf\">\n";
+        if (objectStreams) {
+            fopxconf += "<use-object-streams>true</use-object-streams>";
+        }
+        fopxconf += "    <sign-params>\n"
                 + "      <keystore>" + pkcs + "</keystore>\n"
                 + "    </sign-params>\n"
                 + "    </renderer>\n"