From 55dcc5fbfd9dc80cc2d71cfdcbe6e8b01bc5907f Mon Sep 17 00:00:00 2001 From: Joerg Pietschmann Date: Wed, 5 Mar 2003 18:58:16 +0000 Subject: [PATCH] Added PDF encryption. Submitted by: Patrick C. Lankswert git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/branches/fop-0_20_2-maintain@196016 13f79535-47bb-0310-9956-ffa450edef68 --- build.xml | 13 +- .../org/apache/fop/pdf/PDFEncryption.java | 156 +++++++ .../org/apache/fop/pdf/PDFEncryption.java | 421 ++++++++++++++++++ .../apache/fop/apps/CommandLineOptions.java | 123 +++-- src/org/apache/fop/pdf/PDFDocument.java | 42 ++ src/org/apache/fop/pdf/PDFXObject.java | 10 +- .../apache/fop/render/pdf/PDFRenderer.java | 45 ++ 7 files changed, 777 insertions(+), 33 deletions(-) create mode 100755 src/java-1.3/org/apache/fop/pdf/PDFEncryption.java create mode 100755 src/java-1.4/org/apache/fop/pdf/PDFEncryption.java diff --git a/build.xml b/build.xml index 5764bda2a..ca6af48fb 100644 --- a/build.xml +++ b/build.xml @@ -189,7 +189,7 @@ Sometimes ant gives out this warnings, but the build is finished without any pro - + @@ -302,11 +302,17 @@ Sometimes ant gives out this warnings, but the build is finished without any pro + + + + + + @@ -371,8 +377,9 @@ Sometimes ant gives out this warnings, but the build is finished without any pro - + diff --git a/src/java-1.3/org/apache/fop/pdf/PDFEncryption.java b/src/java-1.3/org/apache/fop/pdf/PDFEncryption.java new file mode 100755 index 000000000..52e0c9fb2 --- /dev/null +++ b/src/java-1.3/org/apache/fop/pdf/PDFEncryption.java @@ -0,0 +1,156 @@ +/* + * $Id$ + * ============================================================================ + * The Apache Software License, Version 1.1 + * ============================================================================ + * + * Copyright (C) 1999-2003 The Apache Software Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modifica- + * tion, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The end-user documentation included with the redistribution, if any, must + * include the following acknowledgment: "This product includes software + * developed by the Apache Software Foundation (http://www.apache.org/)." + * Alternately, this acknowledgment may appear in the software itself, if + * and wherever such third-party acknowledgments normally appear. + * + * 4. The names "FOP" and "Apache Software Foundation" must not be used to + * endorse or promote products derived from this software without prior + * written permission. For written permission, please contact + * apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache", nor may + * "Apache" appear in their name, without prior written permission of the + * Apache Software Foundation. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * APACHE SOFTWARE FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLU- + * DING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ============================================================================ + * + * This software consists of voluntary contributions made by many individuals + * on behalf of the Apache Software Foundation and was originally created by + * James Tauber . For more information on the Apache + * Software Foundation, please see . + */ +package org.apache.fop.pdf; + +import java.util.Hashtable; + +/** + * class representing a /Filter /Standard object. + * + */ +public class PDFEncryption extends PDFObject { + /** + * create a /Filter /Standard object. + * + * @param number the object's number + */ + public PDFEncryption(int number) { + throw new IllegalStateException("PDF encryption not available."); + } + + /** This method allows the setting of the user password + * @param value The string to use as the user password. It may be blank but not null. + */ + public void setUserPassword(String value) { + } + + /** Sets the owner password for the PDF + * @param value The owner password + */ + public void setOwnerPassword(String value) { + } + + /** Set whether the document will allow printing. + * @param value The new permision value + */ + public void setAllowPrint(boolean value) { + } + + /** Set whether the document will allow the content to be extracted + * @param value The new permission value + */ + public void setAllowCopyContent(boolean value) { + } + + /** Set whether the document will allow content editting + * @param value The new permission value + */ + public void setAllowEditContent(boolean value) { + } + + /** Set whether the document will allow annotation modificcations + * @param value The new permission value + */ + public void setAllowEditAnnotation(boolean value) { + } + + /** Returns the document file ID + * @return The file ID + */ +// public byte [] getFileID() { +// return null; +// } + + /** This method returns the indexed file ID + * @param index The index to access the file ID + * @return The file ID + */ + public String getFileID(int index) { + return null; + } + + /** This method initializes the encryption algorithms and values + */ + public void init() { + } + + /** This method encrypts the passed data using the generated keys. + * @param data The data to be encrypted + * @param number The block number + * @param generation The block generation + * @return The encrypted data + */ +// public byte [] encryptData(byte [] data, int number, int generation) { +// return null; +// } + + /** Creates PDFFilter for the encryption object + * @param number The object number + * @param generation The objects generation + * @return The resulting filter + */ + public PDFFilter makeFilter(int number, int generation) { + return null; + } + + /** + * represent the object in PDF + * + * @return the PDF + */ + public byte[] toPDF() throws IllegalStateException { + throw new IllegalStateException("PDF Encryption not available."); + } + + static public boolean encryptionAvailable() { + return false; + } +} diff --git a/src/java-1.4/org/apache/fop/pdf/PDFEncryption.java b/src/java-1.4/org/apache/fop/pdf/PDFEncryption.java new file mode 100755 index 000000000..261b28c58 --- /dev/null +++ b/src/java-1.4/org/apache/fop/pdf/PDFEncryption.java @@ -0,0 +1,421 @@ +/* + * $Id$ + * ============================================================================ + * The Apache Software License, Version 1.1 + * ============================================================================ + * + * Copyright (C) 1999-2003 The Apache Software Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modifica- + * tion, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The end-user documentation included with the redistribution, if any, must + * include the following acknowledgment: "This product includes software + * developed by the Apache Software Foundation (http://www.apache.org/)." + * Alternately, this acknowledgment may appear in the software itself, if + * and wherever such third-party acknowledgments normally appear. + * + * 4. The names "FOP" and "Apache Software Foundation" must not be used to + * endorse or promote products derived from this software without prior + * written permission. For written permission, please contact + * apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache", nor may + * "Apache" appear in their name, without prior written permission of the + * Apache Software Foundation. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * APACHE SOFTWARE FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLU- + * DING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ============================================================================ + * + * This software consists of voluntary contributions made by many individuals + * on behalf of the Apache Software Foundation and was originally created by + * James Tauber . For more information on the Apache + * Software Foundation, please see . + */ +package org.apache.fop.pdf; + +// Java +import java.io.PrintWriter; +import java.io.UnsupportedEncodingException; +import java.util.Enumeration; +import java.util.Vector; +import java.util.Hashtable; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.InvalidKeyException; +import javax.crypto.Cipher; +import javax.crypto.spec.SecretKeySpec; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.BadPaddingException; +import javax.crypto.NoSuchPaddingException; +import java.util.Random; + +/** + * class representing a /Filter /Standard object. + * + */ +public class PDFEncryption extends PDFObject { + private class EncryptionFilter extends PDFFilter { + PDFEncryption encryption; + int number; + int generation; + + /** The constructor for the internal PDFEncryption filter + * @param encryption The encryption object to use + * @param number The number of the object to be encrypted + * @param generation The generation of the object to be encrypted + */ + public EncryptionFilter(PDFEncryption encryption, + int number, int generation) { + super(); + this.encryption = encryption; + this.number = number; + this.generation = generation; + } + + /** return a PDF string representation of the filter. In this + * case no filter name is passed. + * @return The filter name, blank in this case + */ + public String getName() { + return ""; + } + + /** return a parameter dictionary for this filter, or null + * @return The parameter dictionary. In this case, null. + */ + public String getDecodeParms() { + return null; + } + + /** encode the given data with the filter + * @param data The data to be encrypted + * @return The encrypted data + */ + public byte[] encode(byte[] data) { + return encryption.encryptData(data,number,generation); + } + } + + final static char [] pad = { 0x28, 0xBF, 0x4E, 0x5E, 0x4E, 0x75, 0x8A, 0x41, + 0x64, 0x00, 0x4E, 0x56, 0xFF, 0xFA, 0x01, 0x08, + 0x2E, 0x2E, 0x00, 0xB6, 0xD0, 0x68, 0x3E, 0x80, + 0x2F, 0x0C, 0xA9, 0xFE, 0x64, 0x53, 0x69, 0x7A }; + final static char[] digits = {'0','1','2','3','4','5','6','7', + '8','9','A','B','C','D','E','F'}; + + /** Value of PRINT permission + */ + public final static int PERMISSION_PRINT = 4; + /** Value of content editting permission + */ + public final static int PERMISSION_EDIT_CONTENT = 8; + /** Value of content extraction permission + */ + public final static int PERMISSION_COPY_CONTENT = 16; + /** Value of annotation editting permission + */ + public final static int PERMISSION_EDIT_ANNOTATIONS = 32; + + // Encryption tools + MessageDigest digest = null; + Cipher cipher = null; + Random random = new Random(); + // Control attributes + String userPassword = ""; + String ownerPassword = ""; + boolean allowPrint = true; + boolean allowCopyContent = true; + boolean allowEditContent = true; + boolean allowEditAnnotations = true; + // Output attributes + byte [] fileID = null; + byte [] encryptionKey = null; + String dictionary = null; + + /** + * create a /Filter /Standard object. + * + * @param number the object's number + */ + public PDFEncryption(int number) { + /* generic creation of object */ + super(number); + try { + digest = MessageDigest.getInstance("MD5"); + cipher = Cipher.getInstance("RC4"); + } catch (NoSuchAlgorithmException e) { + throw new IllegalStateException(e.getMessage()); + } catch (NoSuchPaddingException e) { + throw new IllegalStateException(e.getMessage()); + } + } + + /** This method allows the setting of the user password + * @param value The string to use as the user password. It may be blank but not null. + */ + public void setUserPassword(String value) { + this.userPassword = value; + } + + /** Returns the current user password + * @return The user password + */ + public String getUserPassword () { + return this.userPassword; + } + + /** Sets the owner password for the PDF + * @param value The owner password + */ + public void setOwnerPassword(String value) { + this.ownerPassword = value; + } + + /** Returns the owner password for the PDF + * @return The owner password + */ + public String getOwnerPassword() { + return this.ownerPassword; + } + + /** Set whether the document will allow printing. + * @param value The new permision value + */ + public void setAllowPrint(boolean value) { + this.allowPrint = value; + } + /** Set whether the document will allow the content to be extracted + * @param value The new permission value + */ + public void setAllowCopyContent(boolean value) { + this.allowCopyContent = value; + } + /** Set whether the document will allow content editting + * @param value The new permission value + */ + public void setAllowEditContent(boolean value) { + this.allowEditContent = value; + } + /** Set whether the document will allow annotation modificcations + * @param value The new permission value + */ + public void setAllowEditAnnotation(boolean value) { + this.allowEditAnnotations = value; + } + + // Internal procedures + + private byte [] prepPassword(String password) { + byte [] obuffer = new byte[32]; + byte [] pbuffer = password.getBytes(); + + int i = 0; + int j = 0; + + while(i < obuffer.length && i < pbuffer.length) { + obuffer[i] = pbuffer[i]; i++; + } + while(i < obuffer.length) { + obuffer[i++] = (byte) pad[j++]; + } + + return obuffer; + } + + private String toHex(byte [] value) { + StringBuffer buffer = new StringBuffer(); + + for(int i=0; i < value.length; i++) { + buffer.append(digits[(value[i] >>> 4) & 0x0F]); + buffer.append(digits[value[i] & 0x0F]); + } + + return buffer.toString(); + } + + /** Returns the document file ID + * @return The file ID + */ + public byte [] getFileID() { + if (fileID == null) { + fileID = new byte[16]; + random.nextBytes(fileID); + } + + return fileID; + } + + /** This method returns the indexed file ID + * @param index The index to access the file ID + * @return The file ID + */ + public String getFileID(int index) { + if (index == 1) { + return toHex(getFileID()); + } + + byte [] id = new byte[16]; + random.nextBytes(id); + return toHex(id); + } + + private byte [] encryptWithKey(byte [] data, byte [] key) { + try { + SecretKeySpec keyspec = new SecretKeySpec(key, "RC4"); + cipher.init(Cipher.ENCRYPT_MODE,keyspec); + return cipher.doFinal(data); + } catch (IllegalBlockSizeException e) { + throw new IllegalStateException(e.getMessage()); + } catch (BadPaddingException e) { + throw new IllegalStateException(e.getMessage()); + } catch (InvalidKeyException e) { + throw new IllegalStateException(e.getMessage()); + } + } + + private byte [] encryptWithHash(byte [] data, byte [] hash, int size) { + hash = digest.digest(hash); + + byte [] key = new byte[size]; + + for(int i = 0; i < size; i++) { + key[i] = hash[i]; + } + + return encryptWithKey(data,key); + } + + /** This method initializes the encryption algorithms and values + */ + public void init() { + // Generate the owner value + byte [] oValue; + if (ownerPassword.length() > 0) { + oValue = encryptWithHash(prepPassword(userPassword),prepPassword(ownerPassword),5); + } else { + oValue = encryptWithHash(prepPassword(userPassword),prepPassword(userPassword),5); + } + + // Generate permissions value + int permissions = -4; + + if (!allowPrint) { + permissions -= PERMISSION_PRINT; + } + if(!allowCopyContent) { + permissions -= PERMISSION_COPY_CONTENT; + } + if(!allowEditContent) { + permissions -= PERMISSION_EDIT_CONTENT; + } + if(!allowEditAnnotations) { + permissions -= PERMISSION_EDIT_ANNOTATIONS; + } + + // Create the encrption key + digest.update(prepPassword(userPassword)); + digest.update(oValue); + digest.update((byte) (permissions >>> 0)); + digest.update((byte) (permissions >>> 8)); + digest.update((byte) (permissions >>> 16)); + digest.update((byte) (permissions >>> 24)); + digest.update(getFileID()); + + byte [] hash = digest.digest(); + this.encryptionKey = new byte[5]; + + for(int i = 0; i < 5; i++) { + this.encryptionKey[i] = hash[i]; + } + + // Create the user value + byte [] uValue = encryptWithKey(prepPassword(""),this.encryptionKey); + + // Create the dictionary + this.dictionary = this.number + " " + this.generation + + " obj\n<< /Filter /Standard\n" + + "/V 1" + + "/R 2" + + "/Length 40" + + "/P " +permissions+"\n" + + "/O <"+toHex(oValue)+">\n" + + "/U <"+toHex(uValue)+">\n" + + ">>\n" + + "endobj\n"; + } + + /** This method encrypts the passed data using the generated keys. + * @param data The data to be encrypted + * @param number The block number + * @param generation The block generation + * @return The encrypted data + */ + public byte [] encryptData(byte [] data, int number, int generation) { + if (this.encryptionKey == null) { + throw new IllegalStateException("PDF Encryption has not been initialized"); + } + + byte [] hash = new byte[this.encryptionKey.length+5]; + + int i = 0; + + while(i < this.encryptionKey.length) { + hash[i] = this.encryptionKey[i]; i++; + } + + hash[i++] = (byte) (number >>> 0); + hash[i++] = (byte) (number >>> 8); + hash[i++] = (byte) (number >>> 16); + hash[i++] = (byte) (generation >>> 0); + hash[i++] = (byte) (generation >>> 8);; + + return encryptWithHash(data,hash,hash.length); + } + + /** Creates PDFFilter for the encryption object + * @param number The object number + * @param generation The objects generation + * @return The resulting filter + */ + public PDFFilter makeFilter(int number, int generation) { + return new EncryptionFilter(this, number, generation); + } + + /** + * represent the object in PDF + * + * @return the PDF + */ + public byte[] toPDF() throws IllegalStateException { + if (this.dictionary == null) { + throw new IllegalStateException("PDF Encryption has not been initialized"); + } + + try { + return this.dictionary.getBytes(PDFDocument.ENCODING); + } catch (UnsupportedEncodingException ue) { + return this.dictionary.getBytes(); + } + } + + static public boolean encryptionAvailable() { + return true; + } +} diff --git a/src/org/apache/fop/apps/CommandLineOptions.java b/src/org/apache/fop/apps/CommandLineOptions.java index 4bc03622b..6cf7ecc71 100644 --- a/src/org/apache/fop/apps/CommandLineOptions.java +++ b/src/org/apache/fop/apps/CommandLineOptions.java @@ -147,6 +147,26 @@ public class CommandLineOptions { } + private boolean pdfEncryptionAvailable = false; + private boolean pdfEncryptionChecked = false; + private boolean encryptionAvailable() { + if (!pdfEncryptionChecked) { + try { + Class c = Class.forName("javax.crypto.Cipher"); + pdfEncryptionAvailable + = org.apache.fop.pdf.PDFEncryption.encryptionAvailable(); + } + catch(ClassNotFoundException e) { + pdfEncryptionAvailable = false; + } + pdfEncryptionChecked = true; + if (!pdfEncryptionAvailable) { + log.warn("PDF encryption not available."); + } + } + return pdfEncryptionAvailable; + } + /** * parses the commandline arguments * @return true if parse was successful and procesing can continue, false if processing should stop @@ -219,6 +239,44 @@ public class CommandLineOptions { outfile = new File(args[i + 1]); i++; } + } else if (args[i].equals("-o")) { + if ((i + 1 == args.length) || (args[i + 1].charAt(0) == '-')) { + if (encryptionAvailable()) { + rendererOptions.put("ownerPassword", ""); + } + } else { + if (encryptionAvailable()) { + rendererOptions.put("ownerPassword", args[i + 1]); + } + i++; + } + } else if (args[i].equals("-u")) { + if ((i + 1 == args.length) || (args[i + 1].charAt(0) == '-')) { + if (encryptionAvailable()) { + rendererOptions.put("userPassword", ""); + } + } else { + if (encryptionAvailable()) { + rendererOptions.put("userPassword", args[i + 1]); + } + i++; + } + } else if (args[i].equals("-noprint")) { + if (encryptionAvailable()) { + rendererOptions.put("allowPrint", "FALSE"); + } + } else if (args[i].equals("-nocopy")) { + if (encryptionAvailable()) { + rendererOptions.put("allowCopyContent", "FALSE"); + } + } else if (args[i].equals("-noedit")) { + if (encryptionAvailable()) { + rendererOptions.put("allowEditContent", "FALSE"); + } + } else if (args[i].equals("-noannotations")) { + if (encryptionAvailable()) { + rendererOptions.put("allowEditAnnotations", "FALSE"); + } } else if (args[i].equals("-mif")) { setOutputMode(MIF_OUTPUT); if ((i + 1 == args.length) @@ -534,38 +592,45 @@ public class CommandLineOptions { */ public static void printUsage() { System.err.println("\nUSAGE\nFop [options] [-fo|-xml] infile [-xsl file] [-awt|-pdf|-mif|-pcl|-ps|-txt|-at|-print] \n" - + " [OPTIONS] \n" - + " -d debug mode \n" - + " -x dump configuration settings \n" - + " -q quiet mode \n" - + " -c cfg.xml use additional configuration file cfg.xml\n" - + " -l lang the language to use for user information \n" - + " -s for area tree XML, down to block areas only\n\n" - + " [INPUT] \n" - + " infile xsl:fo input file (the same as the next) \n" - + " -fo infile xsl:fo input file \n" - + " -xml infile xml input file, must be used together with -xsl \n" - + " -xsl stylesheet xslt stylesheet \n \n" - + " [OUTPUT] \n" - + " outfile input will be rendered as pdf file into outfile \n" - + " -pdf outfile input will be rendered as pdf file (outfile req'd) \n" - + " -awt input will be displayed on screen \n" + + " [OPTIONS]\n" + + " -d debug mode\n" + + " -x dump configuration settings\n" + + " -q quiet mode\n" + + " -c cfg.xml use additional configuration file cfg.xml\n" + + " -l lang the language to use for user information\n" + + " -s (-at output) omit tree below block areas\n" + + " -"+TXTRenderer.encodingOptionName+" (-txt output encoding use the encoding for the output file.\n" + + " The encoding must be a valid java encoding.\n" + + " -o [password] pdf file will be encrypted with option owner password\n" + + " -u [password] pdf file will be encrypted with option user password\n" + + " -noprint pdf file will be encrypted without printing permission\n" + + " -nocopy pdf file will be encrypted without copy content permission\n" + + " -noedit pdf file will be encrypted without edit content permission\n" + + " -noannotations pdf file will be encrypted without edit annotation permission\n" + + "\n [INPUT]\n" + + " infile xsl:fo input file (the same as the next)\n" + + " -fo infile xsl:fo input file\n" + + " -xml infile xml input file, must be used together with -xsl\n" + + " -xsl stylesheet xslt stylesheet\n" + + "\n [OUTPUT]\n" + + " outfile input will be rendered as pdf file into outfile\n" + + " -pdf outfile input will be rendered as pdf file (outfile req'd)\n" + + " -awt input will be displayed on screen\n" + " -mif outfile input will be rendered as mif file (outfile req'd)\n" - + " -pcl outfile input will be rendered as pcl file (outfile req'd) \n" - + " -ps outfile input will be rendered as PostScript file (outfile req'd) \n" - + " -txt outfile input will be rendered as text file (outfile req'd) \n" - + " -"+TXTRenderer.encodingOptionName+" encoding use the encoding for the output file.\n" - + " the encoding must be a valid java encoding.\n" - + " -svg outfile input will be rendered as an svg slides file (outfile req'd) \n" - + " -at outfile representation of area tree as XML (outfile req'd) \n" - + " -print input file will be rendered and sent to the printer \n" - + " see options with \"-print help\" \n\n" - + " [Examples]\n" + " Fop foo.fo foo.pdf \n" + + " -pcl outfile input will be rendered as pcl file (outfile req'd)\n" + + " -ps outfile input will be rendered as PostScript file (outfile req'd)\n" + + " -txt outfile input will be rendered as text file (outfile req'd)\n" + + " -svg outfile input will be rendered as an svg slides file (outfile req'd)\n" + + " -at outfile representation of area tree as XML (outfile req'd)\n" + + " -print input file will be rendered and sent to the printer\n" + + " see print specific options with \"-print help\"\n" + + "\n [Examples]\n" + + " Fop foo.fo foo.pdf\n" + " Fop -fo foo.fo -pdf foo.pdf (does the same as the previous line)\n" + " Fop -xsl foo.xsl -xml foo.xml -pdf foo.pdf\n" + " Fop foo.fo -mif foo.mif\n" - + " Fop foo.fo -print or Fop -print foo.fo \n" - + " Fop foo.fo -awt \n"); + + " Fop foo.fo -print or Fop -print foo.fo\n" + + " Fop foo.fo -awt\n"); } /** @@ -573,7 +638,7 @@ public class CommandLineOptions { */ public void printUsagePrintOutput() { System.err.println("USAGE: -print [-Dstart=i] [-Dend=i] [-Dcopies=i] [-Deven=true|false] " - + " org.apache.fop.apps.Fop (..) -print \n" + + " org.apache.fop.apps.Fop (..) -print\n" + "Example:\n" + "java -Dstart=1 -Dend=2 org.apache.Fop.apps.Fop infile.fo -print "); } diff --git a/src/org/apache/fop/pdf/PDFDocument.java b/src/org/apache/fop/pdf/PDFDocument.java index 3eca8bce5..d21603c1c 100644 --- a/src/org/apache/fop/pdf/PDFDocument.java +++ b/src/org/apache/fop/pdf/PDFDocument.java @@ -159,6 +159,11 @@ public class PDFDocument { */ protected IDReferences idReferences; + /** + * the documents encryption, if exists + */ + protected PDFEncryption encryption; + /** * the colorspace (0=RGB, 1=CMYK) */ @@ -237,6 +242,30 @@ public class PDFDocument { this.info.setProducer(producer); } + /** + * set the encryption object + * + * @param ownerPassword The owner password for the pdf file + * @param userPassword The user password for the pdf file + * @param allowPrint Indicates whether the printing permission will be set + * @param allowCopyContent Indicates whether the content extracting permission will be set + * @param allowEditContent Indicates whether the edit content permission will be set + * @param allowEditAnnotations Indicates whether the edit annotations permission will be set + */ + public void setEncryption(String ownerPassword, String userPassword, + boolean allowPrint, boolean allowCopyContent, + boolean allowEditContent, boolean allowEditAnnotations) { + this.encryption = new PDFEncryption(++this.objectcount); + this.encryption.setOwnerPassword(ownerPassword); + this.encryption.setUserPassword(userPassword); + this.encryption.setAllowPrint(allowPrint); + this.encryption.setAllowCopyContent(allowCopyContent); + this.encryption.setAllowEditContent(allowEditContent); + this.encryption.setAllowEditAnnotation(allowEditAnnotations); + this.encryption.init(); + addTrailerObject(this.encryption); + } + /** * Make a /Catalog (Root) object. This object is written in * the trailer. @@ -1170,6 +1199,9 @@ public class PDFDocument { */ PDFStream obj = new PDFStream(++this.objectcount); obj.addDefaultFilters(); + if (this.encryption != null) { + obj.addFilter(this.encryption.makeFilter(obj.number,obj.generation)); + } this.objects.add(obj); return obj; @@ -1317,6 +1349,15 @@ public class PDFDocument { by the table's length */ this.position += outputXref(stream); + // Determine existance of encryption dictionary + String encryptEntry = ""; + + if (this.encryption != null) { + encryptEntry = + "/Encrypt " + this.encryption.number + " " + this.encryption.generation + " R\n"+ + "/ID[<"+this.encryption.getFileID(1)+"><"+this.encryption.getFileID(2)+">]\n"; + } + /* construct the trailer */ String pdf = "trailer\n" + @@ -1324,6 +1365,7 @@ public class PDFDocument { "/Size " + (this.objectcount + 1) + "\n" + "/Root " + this.root.number + " " + this.root.generation + " R\n" + "/Info " + this.info.number + " " + this.info.generation + " R\n" + + encryptEntry + ">>\n" + "startxref\n" + this.xref + "\n" + diff --git a/src/org/apache/fop/pdf/PDFXObject.java b/src/org/apache/fop/pdf/PDFXObject.java index c9098f6dd..92098c5de 100644 --- a/src/org/apache/fop/pdf/PDFXObject.java +++ b/src/org/apache/fop/pdf/PDFXObject.java @@ -108,6 +108,9 @@ public class PDFXObject extends PDFObject { pdfICCStream = pdfDoc.makePDFICCStream(); pdfICCStream.setColorSpace(jpegimage.getColorSpace()); pdfICCStream.addDefaultFilters(); + if (pdfDoc.encryption != null) { + pdfICCStream.addFilter(pdfDoc.encryption.makeFilter(pdfICCStream.number, pdfICCStream.generation)); + } } } } catch (Exception e) { @@ -185,6 +188,9 @@ public class PDFXObject extends PDFObject { imgStream.setData(imgData); //imgStream.addFilter(new FlateFilter()); imgStream.addDefaultFilters(); + if (pdfDoc.encryption != null) { + imgStream.addFilter(pdfDoc.encryption.makeFilter(this.number,this.generation)); + } String dictEntries = imgStream.applyFilters(); @@ -241,7 +247,9 @@ public class PDFXObject extends PDFObject { } else { imgStream.addDefaultFilters(); } - + if (pdfDoc.encryption != null) { + imgStream.addFilter(pdfDoc.encryption.makeFilter(this.number,this.generation)); + } String dictEntries = imgStream.applyFilters(); diff --git a/src/org/apache/fop/render/pdf/PDFRenderer.java b/src/org/apache/fop/render/pdf/PDFRenderer.java index 097495ade..4b2ff2c4e 100644 --- a/src/org/apache/fop/render/pdf/PDFRenderer.java +++ b/src/org/apache/fop/render/pdf/PDFRenderer.java @@ -170,6 +170,51 @@ public class PDFRenderer extends PrintRenderer { */ public void setOptions(java.util.Map options) { this.options = options; + + // Process encryption options, if any exist + boolean encrypt = false; + String oPassword = ""; + String uPassword = ""; + boolean allowPrint = true; + boolean allowCopyContent = true; + boolean allowEditContent = true; + boolean allowEditAnnotations = true; + String option; + + option = (String) options.get("ownerPassword"); + if (option != null) { + encrypt = true; + oPassword = option; + } + option = (String) options.get("userPassword"); + if (option != null) { + encrypt = true; + uPassword = option; + } + option = (String) options.get("allowPrint"); + if (option != null) { + encrypt = true; + allowPrint = option.equals("TRUE"); + } + option = (String) options.get("allowCopyContent"); + if (option != null) { + encrypt = true; + allowCopyContent = option.equals("TRUE"); + } + option = (String) options.get("allowEditContent"); + if (option != null) { + encrypt = true; + allowEditContent = option.equals("TRUE"); + } + option = (String) options.get("allowEditAnnotations"); + if (option != null) { + encrypt = true; + allowEditAnnotations = option.equals("TRUE"); + } + if (encrypt) { + this.pdfDoc.setEncryption(oPassword,uPassword,allowPrint,allowCopyContent, + allowEditContent, allowEditAnnotations); + } } /** -- 2.39.5