From: Vincent Hennebert Date: Mon, 22 Oct 2012 15:47:02 +0000 (+0000) Subject: Bugzilla #54038: Added PDF/A-2 output option X-Git-Tag: fop-2_0~277 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=f17dbea7f8210e18ef5e9f103a684637ac36a916;p=xmlgraphics-fop.git Bugzilla #54038: Added PDF/A-2 output option git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@1400919 13f79535-47bb-0310-9956-ffa450edef68 --- diff --git a/src/java/org/apache/fop/pdf/PDFAMode.java b/src/java/org/apache/fop/pdf/PDFAMode.java index 899f9aa7d..ab0acd35f 100644 --- a/src/java/org/apache/fop/pdf/PDFAMode.java +++ b/src/java/org/apache/fop/pdf/PDFAMode.java @@ -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} */ diff --git a/src/java/org/apache/fop/pdf/PDFLink.java b/src/java/org/apache/fop/pdf/PDFLink.java index ffec6f09c..f35a15e57 100644 --- a/src/java/org/apache/fop/pdf/PDFLink.java +++ b/src/java/org/apache/fop/pdf/PDFLink.java @@ -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 diff --git a/src/java/org/apache/fop/pdf/PDFMetadata.java b/src/java/org/apache/fop/pdf/PDFMetadata.java index 9b1b8165b..203429147 100644 --- a/src/java/org/apache/fop/pdf/PDFMetadata.java +++ b/src/java/org/apache/fop/pdf/PDFMetadata.java @@ -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 diff --git a/src/java/org/apache/fop/pdf/PDFProfile.java b/src/java/org/apache/fop/pdf/PDFProfile.java index 98005e469..6e0cb412e 100644 --- a/src/java/org/apache/fop/pdf/PDFProfile.java +++ b/src/java/org/apache/fop/pdf/PDFProfile.java @@ -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) { diff --git a/src/java/org/apache/fop/render/pdf/AbstractImageAdapter.java b/src/java/org/apache/fop/render/pdf/AbstractImageAdapter.java index 2840ea1e3..405d720c2 100644 --- a/src/java/org/apache/fop/render/pdf/AbstractImageAdapter.java +++ b/src/java/org/apache/fop/render/pdf/AbstractImageAdapter.java @@ -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 diff --git a/src/java/org/apache/fop/render/pdf/PDFRenderingUtil.java b/src/java/org/apache/fop/render/pdf/PDFRenderingUtil.java index ef57c2152..c3b46794d 100644 --- a/src/java/org/apache/fop/render/pdf/PDFRenderingUtil.java +++ b/src/java/org/apache/fop/render/pdf/PDFRenderingUtil.java @@ -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(); } diff --git a/src/java/org/apache/fop/svg/PDFGraphics2D.java b/src/java/org/apache/fop/svg/PDFGraphics2D.java index 7517babf2..e914df0f6 100644 --- a/src/java/org/apache/fop/svg/PDFGraphics2D.java +++ b/src/java/org/apache/fop/svg/PDFGraphics2D.java @@ -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( diff --git a/status.xml b/status.xml index 5a61dff21..f565f3eec 100644 --- a/status.xml +++ b/status.xml @@ -59,6 +59,9 @@ documents. Example: the fix of marks layering will be such a case when it's done. --> + + Added PDF/A-2 output option. + 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 index 000000000..352e0710e --- /dev/null +++ b/test/java/org/apache/fop/pdf/PDFAModeTestCase.java @@ -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'); + } + +}