/* * 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 java.text.MessageFormat; /** * This class allows tracks the enabled PDF profiles (PDF/A and PDF/X) and provides methods to * the libarary and its users to enable the generation of PDFs conforming to the enabled PDF * profiles. * <p> * Some profile from PDF/X and PDF/A can be active simultaneously (example: PDF/A-1 and * PDF/X-3:2003). */ public class PDFProfile { /** * Indicates the PDF/A mode currently active. Defaults to "no restrictions", i.e. * PDF/A not active. */ protected PDFAMode pdfAMode = PDFAMode.DISABLED; protected PDFUAMode pdfUAMode = PDFUAMode.DISABLED; /** * Indicates the PDF/X mode currently active. Defaults to "no restrictions", i.e. * PDF/X not active. */ protected PDFXMode pdfXMode = PDFXMode.DISABLED; protected PDFVTMode pdfVTMode = PDFVTMode.DISABLED; private PDFDocument doc; /** * Main constructor * @param doc the PDF document */ public PDFProfile(PDFDocument doc) { this.doc = doc; } /** * Validates if the requested profile combination is compatible. */ protected void validateProfileCombination() { if (pdfAMode != PDFAMode.DISABLED) { if (pdfAMode == PDFAMode.PDFA_1B) { if (pdfXMode != PDFXMode.DISABLED && pdfXMode != PDFXMode.PDFX_3_2003 && pdfXMode != PDFXMode.PDFX_4) { throw new PDFConformanceException( pdfAMode + " and " + pdfXMode + " are not compatible!"); } } } if (pdfVTMode != PDFVTMode.DISABLED && pdfXMode != PDFXMode.PDFX_4) { throw new PDFConformanceException(pdfVTMode.name() + " requires " + PDFXMode.PDFX_4.getName() + " enabled"); } } /** @return the PDFDocument this profile is attached to */ public PDFDocument getDocument() { return this.doc; } /** @return the PDF/A mode */ public PDFAMode getPDFAMode() { return this.pdfAMode; } public PDFUAMode getPDFUAMode() { return this.pdfUAMode; } /** @return true if any PDF/A mode is active */ public boolean isPDFAActive() { return getPDFAMode() != PDFAMode.DISABLED; } /** * Sets the PDF/A mode * @param mode the PDF/A mode */ public void setPDFAMode(PDFAMode mode) { if (mode == null) { mode = PDFAMode.DISABLED; } this.pdfAMode = mode; validateProfileCombination(); } public void setPDFUAMode(PDFUAMode mode) { if (mode == null) { mode = PDFUAMode.DISABLED; } this.pdfUAMode = mode; validateProfileCombination(); } /** @return the PDF/X mode */ public PDFXMode getPDFXMode() { return this.pdfXMode; } public PDFVTMode getPDFVTMode() { return this.pdfVTMode; } /** @return true if any PDF/X mode is active */ public boolean isPDFXActive() { return getPDFXMode() != PDFXMode.DISABLED; } public boolean isPDFVTActive() { return getPDFVTMode() != PDFVTMode.DISABLED; } /** * Sets the PDF/X mode * @param mode the PDF/X mode */ public void setPDFXMode(PDFXMode mode) { if (mode == null) { mode = PDFXMode.DISABLED; } this.pdfXMode = mode; validateProfileCombination(); } /** * Sets the PDF/X mode * @param mode the PDF/X mode */ public void setPDFVTMode(PDFVTMode mode) { if (mode == null) { mode = PDFVTMode.DISABLED; } this.pdfVTMode = mode; validateProfileCombination(); } /** {@inheritDoc} */ public String toString() { StringBuffer sb = new StringBuffer(); if (isPDFAActive() && isPDFXActive()) { sb.append("[").append(getPDFAMode()).append(",").append(getPDFXMode()).append("]"); } else if (isPDFAActive()) { sb.append(getPDFAMode()); } else if (isPDFXActive()) { sb.append(getPDFXMode()); } else if (getPDFUAMode().isEnabled()) { sb.append(getPDFUAMode()); } else { sb.append(super.toString()); } return sb.toString(); } //---------=== Info and validation methods ===--------- private String format(String pattern, Object[] args) { return MessageFormat.format(pattern, args); } private String format(String pattern, Object arg) { return format(pattern, new Object[] {arg}); } /** Checks if encryption is allowed. */ public void verifyEncryptionAllowed() { final String err = "{0} doesn't allow encrypted PDFs"; if (isPDFAActive()) { throw new PDFConformanceException(format(err, getPDFAMode())); } if (isPDFXActive()) { throw new PDFConformanceException(format(err, getPDFXMode())); } } /** Checks if PostScript XObjects are allowed. */ public void verifyPSXObjectsAllowed() { final String err = "PostScript XObjects are prohibited when {0}" + " is active. Convert EPS graphics to another format."; if (isPDFAActive()) { throw new PDFConformanceException(format(err, getPDFAMode())); } if (isPDFXActive()) { throw new PDFConformanceException(format(err, getPDFXMode())); } } /** * Checks if the use of transparency is allowed. * @param context Context information for the user to identify the problem spot */ public void verifyTransparencyAllowed(String context) { Object profile = isTransparencyAllowed(); if (profile != null) { throw new TransparencyDisallowedException(profile, context); } } /** * Returns {@code null} if transparency is allowed, otherwise returns the profile that * prevents it. * * @return {@code null}, or an object whose {@code toString} method returns the name * of the profile that disallows transparency */ public Object isTransparencyAllowed() { if (pdfAMode.isPart1()) { return getPDFAMode(); } if (getPDFXMode() == PDFXMode.PDFX_3_2003) { return getPDFXMode(); } return null; } /** Checks if the right PDF version is set. */ public void verifyPDFVersion() { String err = "PDF version must be 1.4 for {0}"; if (getPDFAMode().isPart1() && !Version.V1_4.equals(getDocument().getPDFVersion())) { throw new PDFConformanceException(format(err, getPDFAMode())); } if (getPDFXMode() == PDFXMode.PDFX_3_2003 && !Version.V1_4.equals(getDocument().getPDFVersion())) { throw new PDFConformanceException(format(err, getPDFXMode())); } } /** * Checks a few things required for tagged PDF. */ public void verifyTaggedPDF() { if (getPDFAMode().isLevelA() || getPDFUAMode().isEnabled()) { final String err = "{0} requires the {1} dictionary entry to be set"; String mode = getPDFAMode().toString(); if (getPDFUAMode().isEnabled()) { mode = getPDFUAMode().toString(); } PDFDictionary markInfo = getDocument().getRoot().getMarkInfo(); if (markInfo == null) { throw new PDFConformanceException(format( "{0} requires that the accessibility option in the configuration file be enabled", mode)); } if (!Boolean.TRUE.equals(markInfo.get("Marked"))) { throw new PDFConformanceException(format(err, new Object[] {mode, "Marked"})); } if (getDocument().getRoot().getStructTreeRoot() == null) { throw new PDFConformanceException(format(err, new Object[] {mode, "StructTreeRoot"})); } if (getDocument().getRoot().getLanguage() == null) { throw new PDFConformanceException(format(err, new Object[] {mode, "Lang"})); } } } /** @return true if the ID entry must be present in the trailer. */ public boolean isIDEntryRequired() { return isPDFAActive() || isPDFXActive(); } /** @return true if all fonts need to be embedded. */ public boolean isFontEmbeddingRequired() { return isPDFAActive() || isPDFXActive() || getPDFUAMode().isEnabled(); } /** Checks if a title may be absent. */ public void verifyTitleAbsent() { final String err = "{0} requires the title to be set."; if (getPDFUAMode().isEnabled()) { throw new PDFConformanceException(format(err, getPDFUAMode())); } if (isPDFXActive()) { throw new PDFConformanceException(format(err, getPDFXMode())); } } /** @return true if the ModDate Info entry must be present. */ public boolean isModDateRequired() { return getPDFXMode() != PDFXMode.DISABLED; } /** @return true if the Trapped Info entry must be present. */ public boolean isTrappedEntryRequired() { return getPDFXMode() != PDFXMode.DISABLED; } /** @return true if annotations are allowed */ public boolean isAnnotationAllowed() { return !isPDFXActive(); } /** Checks if annotations are allowed. */ public void verifyAnnotAllowed() { if (!isAnnotationAllowed()) { final String err = "{0} does not allow annotations inside the printable area."; //Note: this rule is simplified. Refer to the standard for details. throw new PDFConformanceException(format(err, getPDFXMode())); } } /** Checks if Actions are allowed. */ public void verifyActionAllowed() { if (isPDFXActive()) { final String err = "{0} does not allow Actions."; throw new PDFConformanceException(format(err, getPDFXMode())); } } /** Checks if embedded files are allowed. */ public void verifyEmbeddedFilesAllowed() { final String err = "{0} does not allow embedded files."; if (isPDFAActive() && getPDFAMode().getPart() < 3) { throw new PDFConformanceException(format(err, getPDFAMode())); } if (isPDFXActive()) { //Implicit since file specs are forbidden throw new PDFConformanceException(format(err, getPDFXMode())); } } }