]> source.dussan.org Git - xmlgraphics-fop.git/commitdiff
Bugzilla #54038: Added PDF/A-2 output option
authorVincent Hennebert <vhennebert@apache.org>
Mon, 22 Oct 2012 15:47:02 +0000 (15:47 +0000)
committerVincent Hennebert <vhennebert@apache.org>
Mon, 22 Oct 2012 15:47:02 +0000 (15:47 +0000)
git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@1400919 13f79535-47bb-0310-9956-ffa450edef68

src/java/org/apache/fop/pdf/PDFAMode.java
src/java/org/apache/fop/pdf/PDFLink.java
src/java/org/apache/fop/pdf/PDFMetadata.java
src/java/org/apache/fop/pdf/PDFProfile.java
src/java/org/apache/fop/render/pdf/AbstractImageAdapter.java
src/java/org/apache/fop/render/pdf/PDFRenderingUtil.java
src/java/org/apache/fop/svg/PDFGraphics2D.java
status.xml
test/java/org/apache/fop/pdf/PDFAModeTestCase.java [new file with mode: 0644]

index 899f9aa7d5ba6bd2cfd3eb4ce1bcb8582846849e..ab0acd35fe26bfea10c8559afccef36c76ad7171 100644 (file)
@@ -22,14 +22,24 @@ package org.apache.fop.pdf;
 /** Enum class for PDF/A modes. */
 public enum PDFAMode {
 
-    /** PDF/A disabled */
+    /** PDF/A disabled. */
     DISABLED("PDF/A disabled"),
-    /** PDF/A-1a enabled */
-    PDFA_1A("PDF/A-1a"),
-    /** PDF/A-1b enabled */
-    PDFA_1B("PDF/A-1b");
+    /** PDF/A-1a enabled. */
+    PDFA_1A(1, 'A'),
+    /** PDF/A-1b enabled. */
+    PDFA_1B(1, 'B'),
+    /** PDF/A-2a enabled. */
+    PDFA_2A(2, 'A'),
+    /** PDF/A-2b enabled. */
+    PDFA_2B(2, 'B'),
+    /** PDF/A-2u enabled. */
+    PDFA_2U(2, 'U');
 
-    private String name;
+    private final String name;
+
+    private final int part;
+
+    private final char level;
 
     /**
      * Constructor to add a new named item.
@@ -37,6 +47,14 @@ public enum PDFAMode {
      */
     private PDFAMode(String name) {
         this.name = name;
+        this.part = 0;
+        this.level = 0;
+    }
+
+    private PDFAMode(int part, char level) {
+        this.name = "PDF/A-" + part + Character.toLowerCase(level);
+        this.part = part;
+        this.level = level;
     }
 
     /** @return the name of the enum */
@@ -45,20 +63,51 @@ public enum PDFAMode {
     }
 
     /**
-     * Indicates whether this mode obeys the restrictions established by PDF/A-1a.
-     * @return true if this mode obeys the restrictions established by PDF/A-1a.
+     * Returns {@code true} if this enum corresponds to one of the available PDF/A modes.
+     *
+     * @return {@code true} if this is not DISABLED
+     */
+    public boolean isEnabled() {
+        return this != DISABLED;
+    }
+
+    /**
+     * Returns the part of the specification this enum corresponds to.
+     *
+     * @return 1 for PDF/A-1 (ISO 19005-1), 2 for PDF/A-2 (ISO 19005-2)
+     */
+    public int getPart() {
+        return part;
+    }
+
+    /**
+     * Returns {@code true} if this enum corresponds to PDF/A-1 (ISO 19005-1).
+     */
+    public boolean isPart1() {
+        return part == 1;
+    }
+
+    /**
+     * Returns {@code true} if this enum corresponds to PDF/A-2 (ISO 19005-2).
+     */
+    public boolean isPart2() {
+        return part == 1 || part == 2;
+    }
+
+    /**
+     * Returns the conformance level for this enum.
+     *
+     * @return 'A', 'B' or 'U'
      */
-    public boolean isPDFA1LevelA() {
-        return (this == PDFA_1A);
+    public char getConformanceLevel() {
+        return level;
     }
 
     /**
-     * Indicates whether this mode obeys the restrictions established by PDF/A-1b.
-     * @return true if this mode obeys the restrictions established by PDF/A-1b.
+     * Returns {@code true} if this enum corresponds to conformance level A.
      */
-    public boolean isPDFA1LevelB() {
-        return (this != DISABLED);
-        //PDF/A-1a is a superset of PDF/A-1b!
+    public boolean isLevelA() {
+        return level == 'A';
     }
 
     /**
@@ -67,13 +116,12 @@ public enum PDFAMode {
      * @return the PDFAMode enum object (DISABLED will be returned if no match is found)
      */
     public static PDFAMode getValueOf(String s) {
-        if (PDFA_1A.getName().equalsIgnoreCase(s)) {
-            return PDFA_1A;
-        } else if (PDFA_1B.getName().equalsIgnoreCase(s)) {
-            return PDFA_1B;
-        } else {
-            return DISABLED;
+        for (PDFAMode mode : values()) {
+            if (mode.name.equalsIgnoreCase(s)) {
+                return mode;
+            }
         }
+        return DISABLED;
     }
 
     /** {@inheritDoc} */
index ffec6f09c6fbdb4657947fdf5060035d1e3a7cf2..f35a15e57c49b74d55a803b362e588f65d093f9c 100644 (file)
@@ -85,7 +85,7 @@ public class PDFLink extends PDFObject {
     public String toPDFString() {
         getDocumentSafely().getProfile().verifyAnnotAllowed();
         String fFlag = "";
-        if (getDocumentSafely().getProfile().getPDFAMode().isPDFA1LevelB()) {
+        if (getDocumentSafely().getProfile().getPDFAMode().isEnabled()) {
             int f = 0;
             f |= 1 << (3 - 1); //Print, bit 3
             f |= 1 << (4 - 1); //NoZoom, bit 4
index 9b1b8165b2e806151eab2ecdfad387498e7e2435..203429147cb9976ebf632c8fcf87d181cb67069e 100644 (file)
@@ -102,7 +102,7 @@ public class PDFMetadata extends PDFStream {
     /** {@inheritDoc} */
     protected void populateStreamDict(Object lengthEntry) {
         final String filterEntry = getFilterList().buildFilterDictEntries();
-        if (getDocumentSafely().getProfile().getPDFAMode().isPDFA1LevelB()
+        if (getDocumentSafely().getProfile().getPDFAMode().isPart1()
                 && filterEntry != null && filterEntry.length() > 0) {
             throw new PDFConformanceException(
                     "The Filter key is prohibited when PDF/A-1 is active");
@@ -156,14 +156,10 @@ public class PDFMetadata extends PDFStream {
 
         //PDF/A identification
         PDFAMode pdfaMode = pdfDoc.getProfile().getPDFAMode();
-        if (pdfaMode.isPDFA1LevelB()) {
+        if (pdfaMode.isEnabled()) {
             PDFAAdapter pdfa = PDFAXMPSchema.getAdapter(meta);
-            pdfa.setPart(1);
-            if (pdfaMode == PDFAMode.PDFA_1A) {
-                pdfa.setConformance("A"); //PDF/A-1a
-            } else {
-                pdfa.setConformance("B"); //PDF/A-1b
-            }
+            pdfa.setPart(pdfaMode.getPart());
+            pdfa.setConformance(String.valueOf(pdfaMode.getConformanceLevel()));
         }
 
         //XMP Basic Schema
index 98005e469d6fd53a92728ba98d8e1ce1a3adcbfb..6e0cb412ee759380fb2d703043d8fefbc1bc73f1 100644 (file)
@@ -170,7 +170,7 @@ public class PDFProfile {
      */
     public void verifyTransparencyAllowed(String context) {
         final String err = "{0} does not allow the use of transparency. ({1})";
-        if (isPDFAActive()) {
+        if (pdfAMode.isPart1()) {
             throw new PDFConformanceException(MessageFormat.format(err,
                     new Object[] {getPDFAMode(), context}));
         }
@@ -183,7 +183,7 @@ public class PDFProfile {
     /** Checks if the right PDF version is set. */
     public void verifyPDFVersion() {
         final String err = "PDF version must be 1.4 for {0}";
-        if (getPDFAMode().isPDFA1LevelB()
+        if (getPDFAMode().isPart1()
                 && !Version.V1_4.equals(getDocument().getPDFVersion())) {
             throw new PDFConformanceException(format(err, getPDFAMode()));
         }
@@ -197,7 +197,7 @@ public class PDFProfile {
      * Checks a few things required for tagged PDF.
      */
     public void verifyTaggedPDF() {
-        if (getPDFAMode().isPDFA1LevelA()) {
+        if (getPDFAMode().isLevelA()) {
             final String err = "{0} requires the {1} dictionary entry to be set";
             PDFDictionary markInfo = getDocument().getRoot().getMarkInfo();
             if (markInfo == null) {
index 2840ea1e36137e9945592dbe310cc5dea6eca6b0..405d720c25b57e92ca9b605bebdb79d1a043819a 100644 (file)
@@ -95,7 +95,7 @@ public abstract class AbstractImageAdapter implements PDFImage {
         } else if (issRGB()) {
           pdfICCStream = setupsRGBColorProfile(doc);
         }
-        if (doc.getProfile().getPDFAMode().isPDFA1LevelB()) {
+        if (doc.getProfile().getPDFAMode().isPart1()) {
             if (pdfCS != null
                     && pdfCS.getColorSpace() != PDFDeviceColorSpace.DEVICE_RGB
                     && pdfCS.getColorSpace() != PDFDeviceColorSpace.DEVICE_GRAY
index ef57c215257d0ffbc8ee913cae2d249c0ea2f92f..c3b46794db8339531829c4b1e0991515d46f07c5 100644 (file)
@@ -57,7 +57,6 @@ import org.apache.fop.pdf.PDFICCStream;
 import org.apache.fop.pdf.PDFInfo;
 import org.apache.fop.pdf.PDFMetadata;
 import org.apache.fop.pdf.PDFNames;
-import org.apache.fop.pdf.PDFNumsArray;
 import org.apache.fop.pdf.PDFOutputIntent;
 import org.apache.fop.pdf.PDFPageLabels;
 import org.apache.fop.pdf.PDFReference;
@@ -109,8 +108,8 @@ class PDFRenderingUtil {
 
     private void initialize() {
         rendererConfig = PDFRendererOptionsConfig.DEFAULT.merge(createFromUserAgent(userAgent));
-        if (rendererConfig.getPDFAMode().isPDFA1LevelA()) {
-            //Enable accessibility if PDF/A-1a is enabled because it requires tagged PDF.
+        if (rendererConfig.getPDFAMode().isLevelA()) {
+            // PDF/A Level A requires tagged PDF
             userAgent.getRendererOptions().put(Accessibility.ACCESSIBILITY, Boolean.TRUE);
         }
     }
@@ -285,7 +284,7 @@ class PDFRenderingUtil {
             addPDFXOutputIntent();
         }
         PDFAMode pdfAMode = rendererConfig.getPDFAMode();
-        if (pdfAMode.isPDFA1LevelB()) {
+        if (pdfAMode.isEnabled()) {
             log.debug("PDF/A is active. Conformance Level: " + pdfAMode);
             addPDFA1OutputIntent();
         }
index 7517babf2b77b916f8837059982f3e6ed0b5eede..e914df0f690c5561fbe50e25cd18a2842a6f6b1e 100644 (file)
@@ -743,7 +743,7 @@ public class PDFGraphics2D extends AbstractGraphics2D implements NativeImageHand
         //TODO Handle this in PDFColorHandler by automatically converting the color.
         //This won't work properly anyway after the redesign of ColorExt
         if (col.getColorSpace().getType() == ColorSpace.TYPE_CMYK) {
-             if (pdfDoc.getProfile().getPDFAMode().isPDFA1LevelB()) {
+             if (pdfDoc.getProfile().getPDFAMode().isPart1()) {
                  //See PDF/A-1, ISO 19005:1:2005(E), 6.2.3.3
                  //FOP is currently restricted to DeviceRGB if PDF/A-1 is active.
                  throw new PDFConformanceException(
index 5a61dff21a692d08a7835c2a9259c36951242d42..f565f3eec1adca3153f671eab0a0138ba15ecc5f 100644 (file)
@@ -59,6 +59,9 @@
       documents. Example: the fix of marks layering will be such a case when it's done.
     -->
     <release version="FOP Trunk" date="TBD">
+      <action context="Renderers" dev="VH" type="add" fixes-bug="54038">
+        Added PDF/A-2 output option.
+      </action>
       <action context="Renderers" dev="VH" type="add" fixes-bug="54037">
         PDF output: Issue a warning when accessibility is enabled and language information is 
         missing.
diff --git a/test/java/org/apache/fop/pdf/PDFAModeTestCase.java b/test/java/org/apache/fop/pdf/PDFAModeTestCase.java
new file mode 100644 (file)
index 0000000..352e071
--- /dev/null
@@ -0,0 +1,145 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.pdf;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+public class PDFAModeTestCase {
+
+    private static class PDFAModeChecker {
+
+        private final PDFAMode mode;
+
+        PDFAModeChecker(PDFAMode mode) {
+            this.mode = mode;
+        }
+
+        PDFAModeChecker isEnabled() {
+            assertTrue(mode.isEnabled());
+            return this;
+        }
+
+        PDFAModeChecker isDisabled() {
+            assertFalse(mode.isEnabled());
+            return this;
+        }
+
+        PDFAModeChecker isPart1() {
+            assertEquals(1, mode.getPart());
+            assertTrue(mode.isPart1());
+            return this;
+        }
+
+        PDFAModeChecker isNotPart1() {
+            assertFalse(mode.getPart() == 1);
+            assertFalse(mode.isPart1());
+            return this;
+        }
+
+        PDFAModeChecker isPart2() {
+            assertTrue(mode.getPart() == 1 || mode.getPart() == 2);
+            assertTrue(mode.isPart2());
+            return this;
+        }
+
+        PDFAModeChecker isNotPart2() {
+            assertFalse(mode.getPart() == 2);
+            assertFalse(mode.isPart2());
+            return this;
+        }
+
+        PDFAModeChecker hasConformanceLevel(char level) {
+            assertEquals(level, mode.getConformanceLevel());
+            return this;
+        }
+
+        PDFAModeChecker isLevelA() {
+            assertEquals('A', mode.getConformanceLevel());
+            assertTrue(mode.isLevelA());
+            return this;
+        }
+
+        PDFAModeChecker isNotLevelA() {
+            assertFalse(mode.getConformanceLevel() == 'A');
+            assertFalse(mode.isLevelA());
+            return this;
+        }
+    }
+
+    @Test
+    public void checkDisabled() {
+        new PDFAModeChecker(PDFAMode.DISABLED)
+                .isDisabled()
+                .isNotPart1()
+                .isNotPart2()
+                .isNotLevelA();
+    }
+
+    @Test
+    public void checkPDFA_1a() {
+        new PDFAModeChecker(PDFAMode.PDFA_1A)
+                .isEnabled()
+                .isPart1()
+                .isPart2()
+                .isLevelA();
+    }
+
+    @Test
+    public void checkPDFA_1b() {
+        new PDFAModeChecker(PDFAMode.PDFA_1B)
+                .isEnabled()
+                .isPart1()
+                .isPart2()
+                .isNotLevelA();
+    }
+
+    @Test
+    public void checkPDFA_2a() {
+        new PDFAModeChecker(PDFAMode.PDFA_2A)
+                .isEnabled()
+                .isNotPart1()
+                .isPart2()
+                .isLevelA();
+    }
+
+    @Test
+    public void checkPDFA_2b() {
+        new PDFAModeChecker(PDFAMode.PDFA_2B)
+                .isEnabled()
+                .isNotPart1()
+                .isPart2()
+                .isNotLevelA();
+    }
+
+    @Test
+    public void checkPDFA_2u() {
+        new PDFAModeChecker(PDFAMode.PDFA_2U)
+                .isEnabled()
+                .isNotPart1()
+                .isPart2()
+                .isNotLevelA()
+                .hasConformanceLevel('U');
+    }
+
+}