/** * Copyright 2006 by Know-Center, Graz, Austria * PDF-AS has been contracted by the E-Government Innovation Center EGIZ, a * joint initiative of the Federal Chancellery Austria and Graz University of * Technology. * * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by * the European Commission - subsequent versions of the EUPL (the "Licence"); * You may not use this work except in compliance with the Licence. * You may obtain a copy of the Licence at: * http://www.osor.eu/eupl/ * * Unless required by applicable law or agreed to in writing, software * distributed under the Licence is distributed on an "AS IS" basis, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the Licence for the specific language governing permissions and * limitations under the Licence. * * This product combines work with different licenses. See the "NOTICE" text * file for details on the various modules and licenses. * The "NOTICE" text file is part of the distribution. Any derivative works * that you distribute must include a readable copy of the "NOTICE" text file. */ package at.gv.egiz.pdfas.utils; import java.io.BufferedOutputStream; import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import org.apache.commons.io.IOUtils; import org.apache.commons.lang.BooleanUtils; import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.pdfbox.pdmodel.PDDocument; import at.gv.egiz.pdfas.api.io.DataSource; import at.gv.egiz.pdfas.exceptions.ErrorCode; import at.knowcenter.wag.egov.egiz.cfg.SettingsReader; import at.knowcenter.wag.egov.egiz.exceptions.PDFDocumentException; import com.lowagie.text.Document; import com.lowagie.text.pdf.BadPasswordException; import com.lowagie.text.pdf.PdfReader; /** * Collection of useful utilities. */ public class PDFASUtils { private static Log log = LogFactory.getLog(PDFASUtils.class); /** * The configuration key for PDF/A support. */ public static final String CFG_KEY_PDFA = "SIG_PDFA1B_VALID"; private PDFASUtils() { } /** * Verifies that a document could be opened with full permissions. * * @param pdfReader * The PdfReader. * @throws PDFDocumentException * Thrown if document has not been opened with full permissions. */ private static void checkReaderPermissions(PdfReader pdfReader) throws PDFDocumentException { if (pdfReader.isEncrypted()) { throw new PDFDocumentException(ErrorCode.DOCUMENT_IS_PROTECTED, "Document is encrypted."); } if (!pdfReader.isOpenedWithFullPermissions()) { throw new PDFDocumentException(ErrorCode.DOCUMENT_IS_PROTECTED, "Document is protected."); } } /** * Verifies that the document is not encrypted and/or protected. In case no restrictions have been applied to the * document a pdf reader is returned. * * @param dataSource * The document data source. * @throws PDFDocumentException * Thrown if the document could not be opened with full permissions. */ public static PdfReader createPdfReaderCheckingPermissions(DataSource dataSource) throws PDFDocumentException { return createPdfReaderCheckingPermissions(dataSource.createInputStream()); } /** * Verifies that the document is not encrypted and/or protected. In case no restrictions have been applied to the * document a pdf reader is returned. * * @param dataSource * The document data source. * @throws PDFDocumentException * Thrown if the document could not be opened with full permissions. */ public static PdfReader createPdfReaderCheckingPermissions(at.gv.egiz.pdfas.framework.input.DataSource dataSource) throws PDFDocumentException { return createPdfReaderCheckingPermissions(dataSource.createInputStream()); } /** * Verifies that the document is not encrypted and/or protected. In case no restrictions have been applied to the * document a pdf reader is returned. * * @param inputStream * The document data input stream. * @throws PDFDocumentException * Thrown if the document could not be opened with full permissions. */ public static PdfReader createPdfReaderCheckingPermissions(InputStream inputStream) throws PDFDocumentException { PdfReader reader = null; try { // try to parse document // If fully encrypted, PdfReader will fail; // It should throw a BadPasswordException, but unfortunately does not (throws an IOException instead, // internally catching BadPAsswordException; see comments below). reader = new PdfReader(inputStream); checkReaderPermissions(reader); return reader; } catch (BadPasswordException e) { // will never be reached with itext-2.1.5-rev3628-pdfas:v1.1 // just added for later versions... (see comments below) // itext-2.1.5-rev3628-pdfas:v1.2 correctly throws BadPasswordException throw new PDFDocumentException(ErrorCode.DOCUMENT_IS_PROTECTED, "Document is protected."); } catch (PDFDocumentException e) { throw e; } catch (Exception e) { final String EX_MSG_FOR_ENCRYPTED_DOCUMENT = "Bad user Password"; // Inspecting the exception message seems to be the only way when using itext-2.1.5-rev3628-pdfas:v1.1: // itext neither externally throws a BadPasswordException nor passed the cause..., // later versions do! // String "Bad user Password" is set in com.lowagie.text.pdf.BadPasswordException so this approach will // work as long as the underlying itext library is not beeing updated. if (StringUtils.containsIgnoreCase(e.getMessage(), EX_MSG_FOR_ENCRYPTED_DOCUMENT)) { throw new PDFDocumentException(ErrorCode.DOCUMENT_IS_PROTECTED, "Document is protected."); } throw new PDFDocumentException(ErrorCode.DOCUMENT_CANNOT_BE_READ, "Unable to parse document."); } finally { IOUtils.closeQuietly(inputStream); closeQuietly(reader); } } /** * Quietly closes a pdf reader. * * @param reader * The reader. */ public static void closeQuietly(PdfReader reader) { if (reader != null) { try { reader.close(); } catch (Throwable e) { // ignore } } } /** * Quietly closes a pdf document. * * @param pdDocument * The pdf document. */ public static void closeQuietly(PDDocument pdDocument) { if (pdDocument != null) { try { pdDocument.close(); } catch (Throwable e) { // ignore } } } /** * Quietly closes a pdf document. * * @param pdDocument * The pdf document. */ public static void closeQuietly(org.pdfbox.pdmodel.PDDocument pdDocument) { if (pdDocument != null) { try { pdDocument.close(); } catch (Throwable e) { // ignore } } } /** * Quietly closes a pdf document. * * @param pdDocument * The pdf document. */ public static void closeQuietly(Document doc) { if (doc != null) { try { doc.close(); } catch (Throwable e) { // ignore } } } public static boolean toFile(byte[] data, File file) throws IOException { return PDFASUtils.toFile(new ByteArrayInputStream(data), file); } public static boolean toFile(InputStream inputStream, File file) throws IOException { boolean result = false; BufferedOutputStream bufferedOutputStream = null; try { bufferedOutputStream = new BufferedOutputStream(new FileOutputStream(file)); ConfigUtils.writeInputStreamToOutputStream(inputStream, bufferedOutputStream); } finally { if (bufferedOutputStream != null) { try { bufferedOutputStream.close(); result = true; } catch (IOException e) { result = false; } } } return result; } /** * Returns {@code true} if the given {@code profileId} is PDF/A-1b enabled, {@code false} if not. * * @param profileId * The signature profile. * @return {@code true} if the given {@code profileId} is PDF/A-1b enabled, {@code false} if not. */ public static boolean isPdfAEnabled(String profileId) { if (profileId == null) { throw new NullPointerException("Profile identifier must not be null."); } if (StringUtils.isEmpty(profileId)) { throw new IllegalArgumentException("Profile identifier must not be empty."); } try { String pdfa = SettingsReader.getInstance().getSetting("sig_obj." + profileId + ".key." + CFG_KEY_PDFA, "default." + CFG_KEY_PDFA, "false"); return BooleanUtils.toBoolean(pdfa); } catch (Exception e) { log.error("Unable to read settings for PDF/A functionality.", e); return false; } } }