You are viewing a plain text version of this content. The canonical link for it is here.
Posted to fop-commits@xmlgraphics.apache.org by vh...@apache.org on 2012/04/05 18:20:17 UTC
svn commit: r1309921 [27/42] - in
/xmlgraphics/fop/branches/Temp_TrueTypeInPostScript: ./ examples/embedding/
examples/embedding/java/embedding/ examples/embedding/java/embedding/atxml/
examples/embedding/java/embedding/tools/ examples/plan/src/org/apa...
Modified: xmlgraphics/fop/branches/Temp_TrueTypeInPostScript/src/java/org/apache/fop/pdf/PDFDocument.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/branches/Temp_TrueTypeInPostScript/src/java/org/apache/fop/pdf/PDFDocument.java?rev=1309921&r1=1309920&r2=1309921&view=diff
==============================================================================
--- xmlgraphics/fop/branches/Temp_TrueTypeInPostScript/src/java/org/apache/fop/pdf/PDFDocument.java (original)
+++ xmlgraphics/fop/branches/Temp_TrueTypeInPostScript/src/java/org/apache/fop/pdf/PDFDocument.java Thu Apr 5 16:19:19 2012
@@ -24,12 +24,9 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
-import java.io.Writer;
-import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
-import java.text.DateFormat;
-import java.text.SimpleDateFormat;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
@@ -41,6 +38,10 @@ import java.util.Map;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+import org.apache.fop.pdf.xref.CrossReferenceStream;
+import org.apache.fop.pdf.xref.CrossReferenceTable;
+import org.apache.fop.pdf.xref.TrailerDictionary;
+
/* image support modified from work of BoBoGi */
/* font support based on work by Takayuki Takeuchi */
@@ -67,40 +68,31 @@ import org.apache.commons.logging.LogFac
*/
public class PDFDocument {
- private static final Integer LOCATION_PLACEHOLDER = new Integer(0);
-
- /** Integer constant to represent PDF 1.3 */
- public static final int PDF_VERSION_1_3 = 3;
-
- /** Integer constant to represent PDF 1.4 */
- public static final int PDF_VERSION_1_4 = 4;
-
/** the encoding to use when converting strings to PDF commands */
public static final String ENCODING = "ISO-8859-1";
/** the counter for object numbering */
- protected int objectcount = 0;
+ protected int objectcount;
/** the logger instance */
private Log log = LogFactory.getLog("org.apache.fop.pdf");
/** the current character position */
- private int position = 0;
-
- /** character position of xref table */
- private int xref;
+ private long position;
/** the character position of each object */
- private List location = new ArrayList();
+ private List<Long> indirectObjectOffsets = new ArrayList<Long>();
+
+ private Collection<PDFStructElem> structureTreeElements;
/** List of objects to write in the trailer */
- private List trailerObjects = new ArrayList();
+ private List<PDFObject> trailerObjects = new ArrayList<PDFObject>();
/** the objects themselves */
- private List objects = new LinkedList();
+ private List<PDFObject> objects = new LinkedList<PDFObject>();
- /** Indicates what PDF version is active */
- private int pdfVersion = PDF_VERSION_1_4;
+ /** Controls the PDF version of this document */
+ private VersionController versionController;
/** Indicates which PDF profiles are active (PDF/A, PDF/X etc.) */
private PDFProfile pdfProfile = new PDFProfile(this);
@@ -109,7 +101,7 @@ public class PDFDocument {
private PDFRoot root;
/** The root outline object */
- private PDFOutline outlineRoot = null;
+ private PDFOutline outlineRoot;
/** The /Pages object (mark-fop@inomial.com) */
private PDFPages pages;
@@ -128,63 +120,46 @@ public class PDFDocument {
= new PDFDeviceColorSpace(PDFDeviceColorSpace.DEVICE_RGB);
/** the counter for Pattern name numbering (e.g. 'Pattern1') */
- private int patternCount = 0;
+ private int patternCount;
/** the counter for Shading name numbering */
- private int shadingCount = 0;
+ private int shadingCount;
/** the counter for XObject numbering */
- private int xObjectCount = 0;
+ private int xObjectCount;
- /** the {@link PDFXObject}s map */
/* TODO: Should be modified (works only for image subtype) */
- private Map xObjectsMap = new HashMap();
+ private Map<String, PDFXObject> xObjectsMap = new HashMap<String, PDFXObject>();
- /** The {@link PDFFont} map */
- private Map fontMap = new HashMap();
+ private Map<String, PDFFont> fontMap = new HashMap<String, PDFFont>();
- /** The {@link PDFFilter} map */
- private Map filterMap = new HashMap();
+ private Map<String, List<String>> filterMap = new HashMap<String, List<String>>();
- /** List of {@link PDFGState}s. */
- private List gstates = new ArrayList();
+ private List<PDFGState> gstates = new ArrayList<PDFGState>();
- /** List of {@link PDFFunction}s. */
- private List functions = new ArrayList();
+ private List<PDFFunction> functions = new ArrayList<PDFFunction>();
- /** List of {@link PDFShading}s. */
- private List shadings = new ArrayList();
+ private List<PDFShading> shadings = new ArrayList<PDFShading>();
- /** List of {@link PDFPattern}s. */
- private List patterns = new ArrayList();
+ private List<PDFPattern> patterns = new ArrayList<PDFPattern>();
- /** List of {@link PDFLink}s. */
- private List links = new ArrayList();
+ private List<PDFLink> links = new ArrayList<PDFLink>();
- /** List of {@link PDFDestination}s. */
- private List destinations;
+ private List<PDFDestination> destinations;
- /** List of {@link PDFFileSpec}s. */
- private List filespecs = new ArrayList();
+ private List<PDFFileSpec> filespecs = new ArrayList<PDFFileSpec>();
- /** List of {@link PDFGoToRemote}s. */
- private List gotoremotes = new ArrayList();
+ private List<PDFGoToRemote> gotoremotes = new ArrayList<PDFGoToRemote>();
- /** List of {@link PDFGoTo}s. */
- private List gotos = new ArrayList();
+ private List<PDFGoTo> gotos = new ArrayList<PDFGoTo>();
- /** List of {@link PDFLaunch}es. */
- private List launches = new ArrayList();
-
- /**
- * The PDFDests object for the name dictionary.
- * Note: This object is not a list.
- */
- private PDFDests dests;
+ private List<PDFLaunch> launches = new ArrayList<PDFLaunch>();
private PDFFactory factory;
- private boolean encodingOnTheFly = true;
+ private FileIDGenerator fileIDGenerator;
+
+ private boolean accessibilityEnabled;
/**
* Creates an empty PDF document.
@@ -199,6 +174,24 @@ public class PDFDocument {
* @param prod the name of the producer of this pdf document
*/
public PDFDocument(String prod) {
+ this(prod, null);
+ versionController = VersionController.getDynamicVersionController(Version.V1_4, this);
+ }
+
+ /**
+ * Creates an empty PDF document.
+ *
+ * The constructor creates a /Root and /Pages object to
+ * track the document but does not write these objects until
+ * the trailer is written. Note that the object ID of the
+ * pages object is determined now, and the xref table is
+ * updated later. This allows Pages to refer to their
+ * Parent before we write it out.
+ *
+ * @param prod the name of the producer of this pdf document
+ * @param versionController the version controller of this PDF document
+ */
+ public PDFDocument(String prod, VersionController versionController) {
this.factory = new PDFFactory(this);
@@ -213,26 +206,32 @@ public class PDFDocument {
// Make the /Info record
this.info = getFactory().makeInfo(prod);
+
+ this.versionController = versionController;
+ }
+
+ /**
+ * Returns the current PDF version.
+ *
+ * @return returns the PDF version
+ */
+ public Version getPDFVersion() {
+ return versionController.getPDFVersion();
}
/**
- * @return the integer representing the active PDF version
- * (one of PDFDocument.PDF_VERSION_*)
+ * Sets the PDF version of this document.
+ *
+ * @param version the PDF version
+ * @throws IllegalStateException if the version of this PDF is not allowed to change.
*/
- public int getPDFVersion() {
- return this.pdfVersion;
+ public void setPDFVersion(Version version) {
+ versionController.setPDFVersion(version);
}
- /** @return the String representing the active PDF version */
+ /** @return the String representing the current PDF version */
public String getPDFVersionString() {
- switch (getPDFVersion()) {
- case PDF_VERSION_1_3:
- return "1.3";
- case PDF_VERSION_1_4:
- return "1.4";
- default:
- throw new IllegalStateException("Unsupported PDF version selected");
- }
+ return versionController.getPDFVersion().toString();
}
/** @return the PDF profile currently active. */
@@ -250,17 +249,6 @@ public class PDFDocument {
}
/**
- * Indicates whether stream encoding on-the-fly is enabled. If enabled
- * stream can be serialized without the need for a buffer to merely
- * calculate the stream length.
- *
- * @return <code>true</code> if on-the-fly encoding is enabled
- */
- public boolean isEncodingOnTheFly() {
- return this.encodingOnTheFly;
- }
-
- /**
* Converts text to a byte array for writing to a PDF file.
*
* @param text text to convert/encode
@@ -275,20 +263,17 @@ public class PDFDocument {
}
/**
- * Creates and returns a Writer object wrapping the given OutputStream. The Writer is
- * buffered to reduce the number of calls to the encoding converter so don't forget
- * to <code>flush()</code> the Writer after use or before writing directly to the
- * underlying OutputStream.
- *
- * @param out the OutputStream to write to
- * @return the requested Writer
- */
- public static Writer getWriterFor(OutputStream out) {
- try {
- return new java.io.BufferedWriter(new java.io.OutputStreamWriter(out, ENCODING));
- } catch (UnsupportedEncodingException uee) {
- throw new Error("JVM doesn't support " + ENCODING + " encoding!");
- }
+ * Flushes the given text buffer to an output stream with the right encoding and resets
+ * the text buffer. This is used to efficiently switch between outputting text and binary
+ * content.
+ * @param textBuffer the text buffer
+ * @param out the output stream to flush the text content to
+ * @throws IOException if an I/O error occurs while writing to the output stream
+ */
+ public static void flushTextBuffer(StringBuilder textBuffer, OutputStream out)
+ throws IOException {
+ out.write(encode(textBuffer.toString()));
+ textBuffer.setLength(0);
}
/**
@@ -323,7 +308,7 @@ public class PDFDocument {
*
* @param map the map of filter lists for each stream type
*/
- public void setFilterMap(Map map) {
+ public void setFilterMap(Map<String, List<String>> map) {
this.filterMap = map;
}
@@ -332,7 +317,7 @@ public class PDFDocument {
*
* @return the map of filters being used
*/
- public Map getFilterMap() {
+ public Map<String, List<String>> getFilterMap() {
return this.filterMap;
}
@@ -355,22 +340,34 @@ public class PDFDocument {
}
/**
- * Makes sure a Lang entry has been set on the document catalog, setting it
- * to a default value if necessary. When accessibility is enabled the
- * language must be specified for any text element in the document.
- */
- public void enforceLanguageOnRoot() {
- if (root.getLanguage() == null) {
- String fallbackLanguage;
- if (getProfile().getPDFAMode().isPDFA1LevelA()) {
- //According to Annex B of ISO-19005-1:2005(E), section B.2
- fallbackLanguage = "x-unknown";
- } else {
- //No language has been set on the first page-sequence, so fall back to "en".
- fallbackLanguage = "en";
- }
- root.setLanguage(fallbackLanguage);
- }
+ * Creates and returns a StructTreeRoot object.
+ *
+ * @param parentTree the value of the ParenTree entry
+ * @return the structure tree root
+ */
+ public PDFStructTreeRoot makeStructTreeRoot(PDFParentTree parentTree) {
+ PDFStructTreeRoot structTreeRoot = new PDFStructTreeRoot(parentTree);
+ assignObjectNumber(structTreeRoot);
+ addTrailerObject(structTreeRoot);
+ root.setStructTreeRoot(structTreeRoot);
+ structureTreeElements = new ArrayList<PDFStructElem>();
+ return structTreeRoot;
+ }
+
+ /**
+ * Creates and returns a structure element.
+ *
+ * @param structureType the structure type of the new element (value for the
+ * S entry)
+ * @param parent the parent of the new structure element in the structure
+ * hierarchy
+ * @return a dictionary of type StructElem
+ */
+ public PDFStructElem makeStructureElement(PDFName structureType, PDFObject parent) {
+ PDFStructElem structElem = new PDFStructElem(parent, structureType);
+ assignObjectNumber(structElem);
+ structureTreeElements.add(structElem);
+ return structElem;
}
/**
@@ -445,39 +442,39 @@ public class PDFDocument {
//Add object to special lists where necessary
if (obj instanceof PDFFunction) {
- this.functions.add(obj);
+ this.functions.add((PDFFunction) obj);
}
if (obj instanceof PDFShading) {
final String shadingName = "Sh" + (++this.shadingCount);
((PDFShading)obj).setName(shadingName);
- this.shadings.add(obj);
+ this.shadings.add((PDFShading) obj);
}
if (obj instanceof PDFPattern) {
final String patternName = "Pa" + (++this.patternCount);
((PDFPattern)obj).setName(patternName);
- this.patterns.add(obj);
+ this.patterns.add((PDFPattern) obj);
}
if (obj instanceof PDFFont) {
final PDFFont font = (PDFFont)obj;
this.fontMap.put(font.getName(), font);
}
if (obj instanceof PDFGState) {
- this.gstates.add(obj);
+ this.gstates.add((PDFGState) obj);
}
if (obj instanceof PDFPage) {
this.pages.notifyKidRegistered((PDFPage)obj);
}
if (obj instanceof PDFLaunch) {
- this.launches.add(obj);
+ this.launches.add((PDFLaunch) obj);
}
if (obj instanceof PDFLink) {
- this.links.add(obj);
+ this.links.add((PDFLink) obj);
}
if (obj instanceof PDFFileSpec) {
- this.filespecs.add(obj);
+ this.filespecs.add((PDFFileSpec) obj);
}
if (obj instanceof PDFGoToRemote) {
- this.gotoremotes.add(obj);
+ this.gotoremotes.add((PDFGoToRemote) obj);
}
}
@@ -491,7 +488,7 @@ public class PDFDocument {
this.trailerObjects.add(obj);
if (obj instanceof PDFGoTo) {
- this.gotos.add(obj);
+ this.gotos.add((PDFGoTo) obj);
}
}
@@ -513,10 +510,10 @@ public class PDFDocument {
*/
public void setEncryption(PDFEncryptionParams params) {
getProfile().verifyEncryptionAllowed();
- this.encryption = PDFEncryptionManager.newInstance(++this.objectcount, params);
+ fileIDGenerator = FileIDGenerator.getRandomFileIDGenerator();
+ this.encryption = PDFEncryptionManager.newInstance(++this.objectcount, params, this);
if (this.encryption != null) {
PDFObject pdfObject = (PDFObject)this.encryption;
- pdfObject.setDocument(this);
addTrailerObject(pdfObject);
} else {
log.warn(
@@ -543,9 +540,8 @@ public class PDFDocument {
return this.encryption;
}
- private Object findPDFObject(List list, PDFObject compare) {
- for (Iterator iter = list.iterator(); iter.hasNext();) {
- PDFObject obj = (PDFObject) iter.next();
+ private Object findPDFObject(List<? extends PDFObject> list, PDFObject compare) {
+ for (PDFObject obj : list) {
if (compare.contentEquals(obj)) {
return obj;
}
@@ -595,7 +591,7 @@ public class PDFDocument {
* @return PDFFont the requested font, null if it wasn't found
*/
protected PDFFont findFont(String fontname) {
- return (PDFFont)this.fontMap.get(fontname);
+ return this.fontMap.get(fontname);
}
/**
@@ -607,7 +603,7 @@ public class PDFDocument {
protected PDFDestination findDestination(PDFDestination compare) {
int index = getDestinationList().indexOf(compare);
if (index >= 0) {
- return (PDFDestination)getDestinationList().get(index);
+ return getDestinationList().get(index);
} else {
return null;
}
@@ -672,9 +668,9 @@ public class PDFDocument {
*/
protected PDFGState findGState(PDFGState wanted, PDFGState current) {
PDFGState poss;
- Iterator iter = this.gstates.iterator();
+ Iterator<PDFGState> iter = this.gstates.iterator();
while (iter.hasNext()) {
- PDFGState avail = (PDFGState)iter.next();
+ PDFGState avail = iter.next();
poss = new PDFGState();
poss.addValues(current);
poss.addValues(avail);
@@ -718,7 +714,7 @@ public class PDFDocument {
*
* @return the map of fonts used in this document
*/
- public Map getFontMap() {
+ public Map<String, PDFFont> getFontMap() {
return this.fontMap;
}
@@ -747,6 +743,7 @@ public class PDFDocument {
* @return the image or PDFXObject for the key if found
* @deprecated Use getXObject instead (so forms are treated in the same way)
*/
+ @Deprecated
public PDFImageXObject getImage(String key) {
return (PDFImageXObject)this.xObjectsMap.get(key);
}
@@ -758,16 +755,7 @@ public class PDFDocument {
* @return the PDFXObject for the key if found
*/
public PDFXObject getXObject(String key) {
- return (PDFXObject)this.xObjectsMap.get(key);
- }
-
- /**
- * Gets the PDFDests object (which represents the /Dests entry).
- *
- * @return the PDFDests object (which represents the /Dests entry).
- */
- public PDFDests getDests() {
- return this.dests;
+ return this.xObjectsMap.get(key);
}
/**
@@ -776,7 +764,7 @@ public class PDFDocument {
*/
public void addDestination(PDFDestination destination) {
if (this.destinations == null) {
- this.destinations = new ArrayList();
+ this.destinations = new ArrayList<PDFDestination>();
}
this.destinations.add(destination);
}
@@ -786,11 +774,11 @@ public class PDFDocument {
*
* @return the list of named destinations.
*/
- public List getDestinationList() {
+ public List<PDFDestination> getDestinationList() {
if (hasDestinations()) {
return this.destinations;
} else {
- return Collections.EMPTY_LIST;
+ return Collections.emptyList();
}
}
@@ -905,17 +893,8 @@ public class PDFDocument {
return this.resources;
}
- /**
- * Ensure there is room in the locations xref for the number of
- * objects that have been created.
- * @param objidx the object's index
- * @param position the position
- */
- private void setLocation(int objidx, int position) {
- while (this.location.size() <= objidx) {
- this.location.add(LOCATION_PLACEHOLDER);
- }
- this.location.set(objidx, new Integer(position));
+ public void enableAccessibility(boolean enableAccessibility) {
+ this.accessibilityEnabled = enableAccessibility;
}
/**
@@ -929,23 +908,50 @@ public class PDFDocument {
//LinkedList) allows for output() methods to create and register objects
//on the fly even during serialization.
while (this.objects.size() > 0) {
- /* Retrieve first */
- PDFObject object = (PDFObject)this.objects.remove(0);
- /*
- * add the position of this object to the list of object
- * locations
- */
- setLocation(object.getObjectNumber() - 1, this.position);
-
- /*
- * output the object and increment the character position
- * by the object's length
- */
- this.position += object.output(stream);
+ PDFObject object = this.objects.remove(0);
+ streamIndirectObject(object, stream);
+ }
+ }
+
+ private void streamIndirectObject(PDFObject o, OutputStream stream) throws IOException {
+ recordObjectOffset(o);
+ this.position += outputIndirectObject(o, stream);
+ }
+
+ private void streamIndirectObjects(Collection<? extends PDFObject> objects, OutputStream stream)
+ throws IOException {
+ for (PDFObject o : objects) {
+ streamIndirectObject(o, stream);
+ }
+ }
+
+ private void recordObjectOffset(PDFObject object) {
+ int index = object.getObjectNumber() - 1;
+ while (indirectObjectOffsets.size() <= index) {
+ indirectObjectOffsets.add(null);
}
+ indirectObjectOffsets.set(index, position);
+ }
- //Clear all objects written to the file
- //this.objects.clear();
+ /**
+ * Outputs the given object, wrapped by obj/endobj, to the given stream.
+ *
+ * @param object an indirect object, as described in Section 3.2.9 of the PDF 1.5
+ * Reference.
+ * @param stream the stream to which the object must be output
+ * @throws IllegalArgumentException if the object is not an indirect object
+ */
+ public static int outputIndirectObject(PDFObject object, OutputStream stream)
+ throws IOException {
+ if (!object.hasObjectNumber()) {
+ throw new IllegalArgumentException("Not an indirect object");
+ }
+ byte[] obj = encode(object.getObjectID());
+ stream.write(obj);
+ int length = object.output(stream);
+ byte[] endobj = encode("\nendobj\n");
+ stream.write(endobj);
+ return obj.length + length + endobj.length;
}
/**
@@ -978,27 +984,6 @@ public class PDFDocument {
this.position += bin.length;
}
- /** @return the "ID" entry for the file trailer */
- protected String getIDEntry() {
- try {
- MessageDigest digest = MessageDigest.getInstance("MD5");
- DateFormat df = new SimpleDateFormat("yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'SSS");
- digest.update(encode(df.format(new Date())));
- //Ignoring the filename here for simplicity even though it's recommended by the PDF spec
- digest.update(encode(String.valueOf(this.position)));
- digest.update(getInfo().toPDF());
- byte[] res = digest.digest();
- String s = PDFText.toHex(res);
- return "/ID [" + s + " " + s + "]";
- } catch (NoSuchAlgorithmException e) {
- if (getProfile().isIDEntryRequired()) {
- throw new UnsupportedOperationException("MD5 not available: " + e.getMessage());
- } else {
- return ""; //Entry is optional if PDF/A or PDF/X are not active
- }
- }
- }
-
/**
* Write the trailer
*
@@ -1006,83 +991,116 @@ public class PDFDocument {
* @throws IOException if there is an exception writing to the output stream
*/
public void outputTrailer(OutputStream stream) throws IOException {
+ createDestinations();
+ output(stream);
+ outputTrailerObjectsAndXref(stream);
+ }
+
+ private void createDestinations() {
if (hasDestinations()) {
Collections.sort(this.destinations, new DestinationComparator());
- this.dests = getFactory().makeDests(this.destinations);
+ PDFDests dests = getFactory().makeDests(this.destinations);
if (this.root.getNames() == null) {
this.root.setNames(getFactory().makeNames());
}
this.root.getNames().setDests(dests);
}
- output(stream);
- for (int count = 0; count < this.trailerObjects.size(); count++) {
- PDFObject o = (PDFObject)this.trailerObjects.get(count);
- setLocation(o.getObjectNumber() - 1, this.position);
- this.position += o.output(stream);
- }
- /* output the xref table and increment the character position
- by the table's length */
- this.position += outputXref(stream);
-
- /* construct the trailer */
- StringBuffer pdf = new StringBuffer(128);
- pdf.append("trailer\n<<\n/Size ")
- .append(this.objectcount + 1)
- .append("\n/Root ")
- .append(this.root.referencePDF())
- .append("\n/Info ")
- .append(this.info.referencePDF())
- .append('\n');
+ }
- if (this.isEncryptionActive()) {
- pdf.append(this.encryption.getTrailerEntry());
- } else {
- pdf.append(this.getIDEntry());
+ private void outputTrailerObjectsAndXref(OutputStream stream) throws IOException {
+ TrailerOutputHelper trailerOutputHelper = mayCompressStructureTreeElements()
+ ? new CompressedTrailerOutputHelper()
+ : new UncompressedTrailerOutputHelper();
+ if (structureTreeElements != null) {
+ trailerOutputHelper.outputStructureTreeElements(stream);
+ }
+ streamIndirectObjects(trailerObjects, stream);
+ TrailerDictionary trailerDictionary = createTrailerDictionary();
+ long startxref = trailerOutputHelper.outputCrossReferenceObject(stream, trailerDictionary);
+ String trailer = "startxref\n" + startxref + "\n%%EOF\n";
+ stream.write(encode(trailer));
+ }
+
+ private boolean mayCompressStructureTreeElements() {
+ return accessibilityEnabled
+ && versionController.getPDFVersion().compareTo(Version.V1_5) >= 0;
+ }
+
+ private TrailerDictionary createTrailerDictionary() {
+ FileIDGenerator gen = getFileIDGenerator();
+ TrailerDictionary trailerDictionary = new TrailerDictionary(this)
+ .setRoot(root)
+ .setInfo(info)
+ .setFileID(gen.getOriginalFileID(), gen.getUpdatedFileID());
+ if (isEncryptionActive()) {
+ trailerDictionary.setEncryption(encryption);
}
+ return trailerDictionary;
+ }
+
+ private interface TrailerOutputHelper {
- pdf.append("\n>>\nstartxref\n")
- .append(this.xref)
- .append("\n%%EOF\n");
+ void outputStructureTreeElements(OutputStream stream) throws IOException;
- /* write the trailer */
- stream.write(encode(pdf.toString()));
+ /**
+ * @return the offset of the cross-reference object (the value of startxref)
+ */
+ long outputCrossReferenceObject(OutputStream stream, TrailerDictionary trailerDictionary)
+ throws IOException;
}
- /**
- * Write the xref table
- *
- * @param stream the OutputStream to write the xref table to
- * @return the number of characters written
- * @throws IOException in case of an error writing the result to
- * the parameter stream
- */
- private int outputXref(OutputStream stream) throws IOException {
+ private class UncompressedTrailerOutputHelper implements TrailerOutputHelper {
- /* remember position of xref table */
- this.xref = this.position;
+ public void outputStructureTreeElements(OutputStream stream)
+ throws IOException {
+ streamIndirectObjects(structureTreeElements, stream);
+ }
- /* construct initial part of xref */
- StringBuffer pdf = new StringBuffer(128);
- pdf.append("xref\n0 ");
- pdf.append(this.objectcount + 1);
- pdf.append("\n0000000000 65535 f \n");
+ public long outputCrossReferenceObject(OutputStream stream,
+ TrailerDictionary trailerDictionary) throws IOException {
+ new CrossReferenceTable(trailerDictionary, position,
+ indirectObjectOffsets).output(stream);
+ return position;
+ }
+ }
- String s, loc;
- for (int count = 0; count < this.location.size(); count++) {
- final String padding = "0000000000";
- s = this.location.get(count).toString();
+ private class CompressedTrailerOutputHelper implements TrailerOutputHelper {
- /* contruct xref entry for object */
- loc = padding.substring(s.length()) + s;
+ private ObjectStreamManager structureTreeObjectStreams;
- /* append to xref table */
- pdf = pdf.append(loc).append(" 00000 n \n");
+ public void outputStructureTreeElements(OutputStream stream)
+ throws IOException {
+ assert structureTreeElements.size() > 0;
+ structureTreeObjectStreams = new ObjectStreamManager(PDFDocument.this);
+ for (PDFStructElem structElem : structureTreeElements) {
+ structureTreeObjectStreams.add(structElem);
+ }
}
- /* write the xref table and return the character length */
- byte[] pdfBytes = encode(pdf.toString());
- stream.write(pdfBytes);
- return pdfBytes.length;
+ public long outputCrossReferenceObject(OutputStream stream,
+ TrailerDictionary trailerDictionary) throws IOException {
+ // Outputting the object streams should not have created new indirect objects
+ assert objects.isEmpty();
+ new CrossReferenceStream(PDFDocument.this, ++objectcount, trailerDictionary, position,
+ indirectObjectOffsets,
+ structureTreeObjectStreams.getCompressedObjectReferences())
+ .output(stream);
+ return position;
+ }
}
+ long getCurrentFileSize() {
+ return position;
+ }
+
+ FileIDGenerator getFileIDGenerator() {
+ if (fileIDGenerator == null) {
+ try {
+ fileIDGenerator = FileIDGenerator.getDigestFileIDGenerator(this);
+ } catch (NoSuchAlgorithmException e) {
+ fileIDGenerator = FileIDGenerator.getRandomFileIDGenerator();
+ }
+ }
+ return fileIDGenerator;
+ }
}
Modified: xmlgraphics/fop/branches/Temp_TrueTypeInPostScript/src/java/org/apache/fop/pdf/PDFEmbeddedFile.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/branches/Temp_TrueTypeInPostScript/src/java/org/apache/fop/pdf/PDFEmbeddedFile.java?rev=1309921&r1=1309920&r2=1309921&view=diff
==============================================================================
--- xmlgraphics/fop/branches/Temp_TrueTypeInPostScript/src/java/org/apache/fop/pdf/PDFEmbeddedFile.java (original)
+++ xmlgraphics/fop/branches/Temp_TrueTypeInPostScript/src/java/org/apache/fop/pdf/PDFEmbeddedFile.java Thu Apr 5 16:19:19 2012
@@ -34,7 +34,7 @@ public class PDFEmbeddedFile extends PDF
super();
put("Type", new PDFName("EmbeddedFile"));
PDFDictionary params = new PDFDictionary();
- params.put("CreationDate", params.formatDateTime(new Date()));
+ params.put("CreationDate", PDFInfo.formatDateTime(new Date()));
put("Params", params);
}
Modified: xmlgraphics/fop/branches/Temp_TrueTypeInPostScript/src/java/org/apache/fop/pdf/PDFEmbeddedFiles.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/branches/Temp_TrueTypeInPostScript/src/java/org/apache/fop/pdf/PDFEmbeddedFiles.java?rev=1309921&r1=1309920&r2=1309921&view=diff
==============================================================================
--- xmlgraphics/fop/branches/Temp_TrueTypeInPostScript/src/java/org/apache/fop/pdf/PDFEmbeddedFiles.java (original)
+++ xmlgraphics/fop/branches/Temp_TrueTypeInPostScript/src/java/org/apache/fop/pdf/PDFEmbeddedFiles.java Thu Apr 5 16:19:19 2012
@@ -21,7 +21,6 @@ package org.apache.fop.pdf;
import java.io.IOException;
import java.io.OutputStream;
-import java.io.Writer;
import java.util.Iterator;
import java.util.Map;
import java.util.SortedMap;
@@ -39,9 +38,9 @@ public class PDFEmbeddedFiles extends PD
}
/** {@inheritDoc} */
- protected void writeDictionary(OutputStream out, Writer writer) throws IOException {
+ protected void writeDictionary(OutputStream out, StringBuilder textBuffer) throws IOException {
sortNames(); //Sort the names before writing them out
- super.writeDictionary(out, writer);
+ super.writeDictionary(out, textBuffer);
}
private void sortNames() {
Modified: xmlgraphics/fop/branches/Temp_TrueTypeInPostScript/src/java/org/apache/fop/pdf/PDFEncryption.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/branches/Temp_TrueTypeInPostScript/src/java/org/apache/fop/pdf/PDFEncryption.java?rev=1309921&r1=1309920&r2=1309921&view=diff
==============================================================================
--- xmlgraphics/fop/branches/Temp_TrueTypeInPostScript/src/java/org/apache/fop/pdf/PDFEncryption.java (original)
+++ xmlgraphics/fop/branches/Temp_TrueTypeInPostScript/src/java/org/apache/fop/pdf/PDFEncryption.java Thu Apr 5 16:19:19 2012
@@ -25,18 +25,6 @@ package org.apache.fop.pdf;
public interface PDFEncryption {
/**
- * Returns the encryption parameters.
- * @return the encryption parameters
- */
- PDFEncryptionParams getParams();
-
- /**
- * Sets the encryption parameters.
- * @param params The parameterss to set
- */
- void setParams(PDFEncryptionParams params);
-
- /**
* Adds a PDFFilter to the PDFStream object
* @param stream the stream to add an encryption filter to
*/
@@ -52,8 +40,10 @@ public interface PDFEncryption {
byte[] encrypt(byte[] data, PDFObject refObj);
/**
- * Returns the trailer entry for encryption.
- * @return the trailer entry
+ * Returns the /Encrypt entry in the file trailer dictionary.
+ *
+ * @return the string "/Encrypt n g R\n" where n and g are the number and generation
+ * of the document's encryption dictionary
*/
String getTrailerEntry();
}
Modified: xmlgraphics/fop/branches/Temp_TrueTypeInPostScript/src/java/org/apache/fop/pdf/PDFEncryptionJCE.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/branches/Temp_TrueTypeInPostScript/src/java/org/apache/fop/pdf/PDFEncryptionJCE.java?rev=1309921&r1=1309920&r2=1309921&view=diff
==============================================================================
--- xmlgraphics/fop/branches/Temp_TrueTypeInPostScript/src/java/org/apache/fop/pdf/PDFEncryptionJCE.java (original)
+++ xmlgraphics/fop/branches/Temp_TrueTypeInPostScript/src/java/org/apache/fop/pdf/PDFEncryptionJCE.java Thu Apr 5 16:19:19 2012
@@ -19,13 +19,12 @@
package org.apache.fop.pdf;
-// Java
import java.io.IOException;
import java.io.OutputStream;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
-import java.util.Random;
+import java.util.Arrays;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
@@ -35,310 +34,426 @@ import javax.crypto.NoSuchPaddingExcepti
import javax.crypto.spec.SecretKeySpec;
/**
- * class representing a /Filter /Standard object.
- *
+ * An implementation of the Standard Security Handler.
*/
-public class PDFEncryptionJCE extends PDFObject implements PDFEncryption {
+public final class PDFEncryptionJCE extends PDFObject implements PDFEncryption {
- private class EncryptionFilter extends PDFFilter {
- private PDFEncryptionJCE encryption;
- private int number;
- private int generation;
+ private final MessageDigest digest;
- /**
- * The constructor for the internal PDFEncryptionJCE 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(PDFEncryptionJCE encryption,
- int number, int generation) {
- super();
- this.encryption = encryption;
- this.number = number;
- this.generation = generation;
- log.debug("new encryption filter for number "
- + number + " and generation " + generation);
+ private byte[] encryptionKey;
+
+ private String encryptionDictionary;
+
+ private class EncryptionInitializer {
+
+ private final PDFEncryptionParams encryptionParams;
+
+ private int encryptionLength;
+
+ private int version;
+
+ private int revision;
+
+ EncryptionInitializer(PDFEncryptionParams params) {
+ this.encryptionParams = new PDFEncryptionParams(params);
+ }
+
+ void init() {
+ encryptionLength = encryptionParams.getEncryptionLengthInBits();
+ determineEncryptionAlgorithm();
+ int permissions = Permission.computePermissions(encryptionParams);
+ EncryptionSettings encryptionSettings = new EncryptionSettings(
+ encryptionLength, permissions,
+ encryptionParams.getUserPassword(), encryptionParams.getOwnerPassword());
+ InitializationEngine initializationEngine = (revision == 2)
+ ? new Rev2Engine(encryptionSettings)
+ : new Rev3Engine(encryptionSettings);
+ initializationEngine.run();
+ encryptionDictionary = createEncryptionDictionary(permissions,
+ initializationEngine.oValue,
+ initializationEngine.uValue);
}
+ private void determineEncryptionAlgorithm() {
+ if (isVersion1Revision2Algorithm()) {
+ version = 1;
+ revision = 2;
+ } else {
+ version = 2;
+ revision = 3;
+ }
+ }
+
+ private boolean isVersion1Revision2Algorithm() {
+ return encryptionLength == 40
+ && encryptionParams.isAllowFillInForms()
+ && encryptionParams.isAllowAccessContent()
+ && encryptionParams.isAllowAssembleDocument()
+ && encryptionParams.isAllowPrintHq();
+ }
+
+ private String createEncryptionDictionary(final int permissions, final byte[] oValue,
+ final byte[] uValue) {
+ return "<< /Filter /Standard\n"
+ + "/V " + version + "\n"
+ + "/R " + revision + "\n"
+ + "/Length " + encryptionLength + "\n"
+ + "/P " + permissions + "\n"
+ + "/O " + PDFText.toHex(oValue) + "\n"
+ + "/U " + PDFText.toHex(uValue) + "\n"
+ + ">>";
+ }
+
+ }
+
+ private static enum Permission {
+
+ PRINT(3),
+ EDIT_CONTENT(4),
+ COPY_CONTENT(5),
+ EDIT_ANNOTATIONS(6),
+ FILL_IN_FORMS(9),
+ ACCESS_CONTENT(10),
+ ASSEMBLE_DOCUMENT(11),
+ PRINT_HQ(12);
+
+ private final int mask;
+
/**
- * Return a PDF string representation of the filter. In this
- * case no filter name is passed.
- * @return The filter name, blank in this case
+ * Creates a new permission.
+ *
+ * @param bit bit position for this permission, 1-based to match the PDF Reference
*/
- public String getName() {
- return "";
+ private Permission(int bit) {
+ mask = 1 << (bit - 1);
+ }
+
+ private int removeFrom(int permissions) {
+ return permissions - mask;
+ }
+
+ static int computePermissions(PDFEncryptionParams encryptionParams) {
+ int permissions = -4;
+
+ if (!encryptionParams.isAllowPrint()) {
+ permissions = PRINT.removeFrom(permissions);
+ }
+ if (!encryptionParams.isAllowCopyContent()) {
+ permissions = COPY_CONTENT.removeFrom(permissions);
+ }
+ if (!encryptionParams.isAllowEditContent()) {
+ permissions = EDIT_CONTENT.removeFrom(permissions);
+ }
+ if (!encryptionParams.isAllowEditAnnotations()) {
+ permissions = EDIT_ANNOTATIONS.removeFrom(permissions);
+ }
+ if (!encryptionParams.isAllowFillInForms()) {
+ permissions = FILL_IN_FORMS.removeFrom(permissions);
+ }
+ if (!encryptionParams.isAllowAccessContent()) {
+ permissions = ACCESS_CONTENT.removeFrom(permissions);
+ }
+ if (!encryptionParams.isAllowAssembleDocument()) {
+ permissions = ASSEMBLE_DOCUMENT.removeFrom(permissions);
+ }
+ if (!encryptionParams.isAllowPrintHq()) {
+ permissions = PRINT_HQ.removeFrom(permissions);
+ }
+ return permissions;
+ }
+ }
+
+ private static final class EncryptionSettings {
+
+ final int encryptionLength; // CSOK: VisibilityModifier
+
+ final int permissions; // CSOK: VisibilityModifier
+
+ final String userPassword; // CSOK: VisibilityModifier
+
+ final String ownerPassword; // CSOK: VisibilityModifier
+
+ EncryptionSettings(int encryptionLength, int permissions,
+ String userPassword, String ownerPassword) {
+ this.encryptionLength = encryptionLength;
+ this.permissions = permissions;
+ this.userPassword = userPassword;
+ this.ownerPassword = ownerPassword;
+ }
+
+ }
+
+ private abstract class InitializationEngine {
+
+ /** Padding for passwords. */
+ protected final byte[] padding = new byte[] {
+ (byte) 0x28, (byte) 0xBF, (byte) 0x4E, (byte) 0x5E,
+ (byte) 0x4E, (byte) 0x75, (byte) 0x8A, (byte) 0x41,
+ (byte) 0x64, (byte) 0x00, (byte) 0x4E, (byte) 0x56,
+ (byte) 0xFF, (byte) 0xFA, (byte) 0x01, (byte) 0x08,
+ (byte) 0x2E, (byte) 0x2E, (byte) 0x00, (byte) 0xB6,
+ (byte) 0xD0, (byte) 0x68, (byte) 0x3E, (byte) 0x80,
+ (byte) 0x2F, (byte) 0x0C, (byte) 0xA9, (byte) 0xFE,
+ (byte) 0x64, (byte) 0x53, (byte) 0x69, (byte) 0x7A};
+
+ protected final int encryptionLengthInBytes;
+
+ private final int permissions;
+
+ private byte[] oValue;
+
+ private byte[] uValue;
+
+ private final byte[] preparedUserPassword;
+
+ protected final String ownerPassword;
+
+ InitializationEngine(EncryptionSettings encryptionSettings) {
+ this.encryptionLengthInBytes = encryptionSettings.encryptionLength / 8;
+ this.permissions = encryptionSettings.permissions;
+ this.preparedUserPassword = preparePassword(encryptionSettings.userPassword);
+ this.ownerPassword = encryptionSettings.ownerPassword;
+ }
+
+ void run() {
+ oValue = computeOValue();
+ createEncryptionKey();
+ uValue = computeUValue();
}
/**
- * Return a parameter dictionary for this filter, or null
- * @return The parameter dictionary. In this case, null.
+ * Applies Algorithm 3.3 Page 79 of the PDF 1.4 Reference.
+ *
+ * @return the O value
*/
- public PDFObject getDecodeParms() {
- return null;
+ private byte[] computeOValue() {
+ // Step 1
+ byte[] md5Input = prepareMD5Input();
+ // Step 2
+ digest.reset();
+ byte[] hash = digest.digest(md5Input);
+ // Step 3
+ hash = computeOValueStep3(hash);
+ // Step 4
+ byte[] key = new byte[encryptionLengthInBytes];
+ System.arraycopy(hash, 0, key, 0, encryptionLengthInBytes);
+ // Steps 5, 6
+ byte[] encryptionResult = encryptWithKey(key, preparedUserPassword);
+ // Step 7
+ encryptionResult = computeOValueStep7(key, encryptionResult);
+ // Step 8
+ return encryptionResult;
}
/**
- * {@inheritDoc}
+ * Applies Algorithm 3.2 Page 78 of the PDF 1.4 Reference.
*/
- public OutputStream applyFilter(OutputStream out) throws IOException {
- return new CipherOutputStream(out,
- encryption.initCipher(number, generation));
+ private void createEncryptionKey() {
+ // Steps 1, 2
+ digest.reset();
+ digest.update(preparedUserPassword);
+ // Step 3
+ digest.update(oValue);
+ // Step 4
+ digest.update((byte) (permissions >>> 0));
+ digest.update((byte) (permissions >>> 8));
+ digest.update((byte) (permissions >>> 16));
+ digest.update((byte) (permissions >>> 24));
+ // Step 5
+ digest.update(getDocumentSafely().getFileIDGenerator().getOriginalFileID());
+ byte[] hash = digest.digest();
+ // Step 6
+ hash = createEncryptionKeyStep6(hash);
+ // Step 7
+ encryptionKey = new byte[encryptionLengthInBytes];
+ System.arraycopy(hash, 0, encryptionKey, 0, encryptionLengthInBytes);
}
- }
+ protected abstract byte[] computeUValue();
- private static final 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};
-
- /** Value of PRINT permission */
- public static final int PERMISSION_PRINT = 4;
- /** Value of content editting permission */
- public static final int PERMISSION_EDIT_CONTENT = 8;
- /** Value of content extraction permission */
- public static final int PERMISSION_COPY_CONTENT = 16;
- /** Value of annotation editting permission */
- public static final int PERMISSION_EDIT_ANNOTATIONS = 32;
-
- // Encryption tools
- private MessageDigest digest = null;
- //private Cipher cipher = null;
- private Random random = new Random();
- // Control attributes
- private PDFEncryptionParams params;
- // Output attributes
- private byte[] fileID = null;
- private byte[] encryptionKey = null;
- private String dictionary = null;
+ /**
+ * Adds padding to the password as directed in page 78 of the PDF 1.4 Reference.
+ *
+ * @param password the password
+ * @return the password with additional padding if necessary
+ */
+ private byte[] preparePassword(String password) {
+ int finalLength = 32;
+ byte[] preparedPassword = new byte[finalLength];
+ byte[] passwordBytes = password.getBytes();
+ System.arraycopy(passwordBytes, 0, preparedPassword, 0, passwordBytes.length);
+ System.arraycopy(padding, 0, preparedPassword, passwordBytes.length,
+ finalLength - passwordBytes.length);
+ return preparedPassword;
+ }
- /**
- * Create a /Filter /Standard object.
- *
- * @param objnum the object's number
- */
- public PDFEncryptionJCE(int objnum) {
- /* generic creation of object */
- super();
- setObjectNumber(objnum);
- try {
- digest = MessageDigest.getInstance("MD5");
- //cipher = Cipher.getInstance("RC4");
- } catch (NoSuchAlgorithmException e) {
- throw new UnsupportedOperationException(e.getMessage());
- /*} catch (NoSuchPaddingException e) {
- throw new UnsupportedOperationException(e.getMessage());*/
+ private byte[] prepareMD5Input() {
+ if (ownerPassword.length() != 0) {
+ return preparePassword(ownerPassword);
+ } else {
+ return preparedUserPassword;
+ }
}
- }
- /**
- * Local factory method.
- * @param objnum PDF object number for the encryption object
- * @param params PDF encryption parameters
- * @return PDFEncryption the newly created PDFEncryption object
- */
- public static PDFEncryption make(int objnum, PDFEncryptionParams params) {
- PDFEncryptionJCE impl = new PDFEncryptionJCE(objnum);
- impl.setParams(params);
- impl.init();
- return impl;
- }
+ protected abstract byte[] computeOValueStep3(byte[] hash);
+ protected abstract byte[] computeOValueStep7(byte[] key, byte[] encryptionResult);
- /**
- * Returns the encryption parameters.
- * @return the encryption parameters
- */
- public PDFEncryptionParams getParams() {
- return this.params;
- }
+ protected abstract byte[] createEncryptionKeyStep6(byte[] hash);
- /**
- * Sets the encryption parameters.
- * @param params The parameterss to set
- */
- public void setParams(PDFEncryptionParams params) {
- this.params = params;
}
- // Internal procedures
+ private class Rev2Engine extends InitializationEngine {
- private byte[] prepPassword(String password) {
- byte[] obuffer = new byte[32];
- byte[] pbuffer = password.getBytes();
+ Rev2Engine(EncryptionSettings encryptionSettings) {
+ super(encryptionSettings);
+ }
- int i = 0;
- int j = 0;
+ @Override
+ protected byte[] computeOValueStep3(byte[] hash) {
+ return hash;
+ }
+
+ @Override
+ protected byte[] computeOValueStep7(byte[] key, byte[] encryptionResult) {
+ return encryptionResult;
+ }
- while (i < obuffer.length && i < pbuffer.length) {
- obuffer[i] = pbuffer[i];
- i++;
+ @Override
+ protected byte[] createEncryptionKeyStep6(byte[] hash) {
+ return hash;
}
- while (i < obuffer.length) {
- obuffer[i++] = (byte) PAD[j++];
+
+ @Override
+ protected byte[] computeUValue() {
+ return encryptWithKey(encryptionKey, padding);
}
- return obuffer;
}
- /**
- * Returns the document file ID
- * @return The file ID
- */
- public byte[] getFileID() {
- if (fileID == null) {
- fileID = new byte[16];
- random.nextBytes(fileID);
+ private class Rev3Engine extends InitializationEngine {
+
+ Rev3Engine(EncryptionSettings encryptionSettings) {
+ super(encryptionSettings);
}
- return fileID;
- }
+ @Override
+ protected byte[] computeOValueStep3(byte[] hash) {
+ for (int i = 0; i < 50; i++) {
+ hash = digest.digest(hash);
+ }
+ return hash;
+ }
- /**
- * 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 PDFText.toHex(getFileID());
+ @Override
+ protected byte[] computeOValueStep7(byte[] key, byte[] encryptionResult) {
+ return xorKeyAndEncrypt19Times(key, encryptionResult);
}
- byte[] id = new byte[16];
- random.nextBytes(id);
- return PDFText.toHex(id);
- }
+ @Override
+ protected byte[] createEncryptionKeyStep6(byte[] hash) {
+ for (int i = 0; i < 50; i++) {
+ digest.update(hash, 0, encryptionLengthInBytes);
+ hash = digest.digest();
+ }
+ return hash;
+ }
- private byte[] encryptWithKey(byte[] data, byte[] key) {
- try {
- final Cipher c = initCipher(key);
- return c.doFinal(data);
- } catch (IllegalBlockSizeException e) {
- throw new IllegalStateException(e.getMessage());
- } catch (BadPaddingException e) {
- throw new IllegalStateException(e.getMessage());
+ @Override
+ protected byte[] computeUValue() {
+ // Step 1 is encryptionKey
+ // Step 2
+ digest.reset();
+ digest.update(padding);
+ // Step 3
+ digest.update(getDocumentSafely().getFileIDGenerator().getOriginalFileID());
+ // Step 4
+ byte[] encryptionResult = encryptWithKey(encryptionKey, digest.digest());
+ // Step 5
+ encryptionResult = xorKeyAndEncrypt19Times(encryptionKey, encryptionResult);
+ // Step 6
+ byte[] uValue = new byte[32];
+ System.arraycopy(encryptionResult, 0, uValue, 0, 16);
+ // Add the arbitrary padding
+ Arrays.fill(uValue, 16, 32, (byte) 0);
+ return uValue;
}
- }
- private Cipher initCipher(byte[] key) {
- try {
- Cipher c = Cipher.getInstance("RC4");
- SecretKeySpec keyspec = new SecretKeySpec(key, "RC4");
- c.init(Cipher.ENCRYPT_MODE, keyspec);
- return c;
- } catch (InvalidKeyException e) {
- throw new IllegalStateException(e.getMessage());
- } catch (NoSuchAlgorithmException e) {
- throw new UnsupportedOperationException(e.getMessage());
- } catch (NoSuchPaddingException e) {
- throw new UnsupportedOperationException(e.getMessage());
+ private byte[] xorKeyAndEncrypt19Times(byte[] key, byte[] input) {
+ byte[] result = input;
+ byte[] encryptionKey = new byte[key.length];
+ for (int i = 1; i <= 19; i++) {
+ for (int j = 0; j < key.length; j++) {
+ encryptionKey[j] = (byte) (key[j] ^ i);
+ }
+ result = encryptWithKey(encryptionKey, result);
+ }
+ return result;
}
- }
- private Cipher initCipher(int number, int generation) {
- byte[] hash = calcHash(number, generation);
- int size = hash.length;
- hash = digest.digest(hash);
- byte[] key = calcKey(hash, size);
- return initCipher(key);
}
- private byte[] encryptWithHash(byte[] data, byte[] hash, int size) {
- hash = digest.digest(hash);
+ private class EncryptionFilter extends PDFFilter {
- byte[] key = calcKey(hash, size);
+ private int streamNumber;
- return encryptWithKey(data, key);
- }
+ private int streamGeneration;
- private byte[] calcKey(byte[] hash, int size) {
- byte[] key = new byte[size];
+ EncryptionFilter(int streamNumber, int streamGeneration) {
+ this.streamNumber = streamNumber;
+ this.streamGeneration = streamGeneration;
+ }
- for (int i = 0; i < size; i++) {
- key[i] = hash[i];
+ /**
+ * Returns a PDF string representation of this filter.
+ *
+ * @return the empty string
+ */
+ public String getName() {
+ return "";
}
- return key;
+
+ /**
+ * Returns a parameter dictionary for this filter.
+ *
+ * @return null, this filter has no parameters
+ */
+ public PDFObject getDecodeParms() {
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ public OutputStream applyFilter(OutputStream out) throws IOException {
+ byte[] key = createEncryptionKey(streamNumber, streamGeneration);
+ Cipher cipher = initCipher(key);
+ return new CipherOutputStream(out, cipher);
+ }
+
}
- /**
- * This method initializes the encryption algorithms and values
- */
- public void init() {
- // Generate the owner value
- byte[] oValue;
- if (params.getOwnerPassword().length() > 0) {
- oValue = encryptWithHash(
- prepPassword(params.getUserPassword()),
- prepPassword(params.getOwnerPassword()), 5);
- } else {
- oValue = encryptWithHash(
- prepPassword(params.getUserPassword()),
- prepPassword(params.getUserPassword()), 5);
- }
-
- // Generate permissions value
- int permissions = -4;
-
- if (!params.isAllowPrint()) {
- permissions -= PERMISSION_PRINT;
- }
- if (!params.isAllowCopyContent()) {
- permissions -= PERMISSION_COPY_CONTENT;
- }
- if (!params.isAllowEditContent()) {
- permissions -= PERMISSION_EDIT_CONTENT;
- }
- if (!params.isAllowEditAnnotations()) {
- permissions -= PERMISSION_EDIT_ANNOTATIONS;
- }
-
- // Create the encrption key
- digest.update(prepPassword(params.getUserPassword()));
- 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 = getObjectID()
- + "<< /Filter /Standard\n"
- + "/V 1\n"
- + "/R 2\n"
- + "/Length 40\n"
- + "/P " + permissions + "\n"
- + "/O " + PDFText.toHex(oValue) + "\n"
- + "/U " + PDFText.toHex(uValue) + "\n"
- + ">>\n"
- + "endobj\n";
+ private PDFEncryptionJCE(int objectNumber, PDFEncryptionParams params, PDFDocument pdf) {
+ setObjectNumber(objectNumber);
+ try {
+ digest = MessageDigest.getInstance("MD5");
+ } catch (NoSuchAlgorithmException e) {
+ throw new UnsupportedOperationException(e.getMessage());
+ }
+ setDocument(pdf);
+ EncryptionInitializer encryptionInitializer = new EncryptionInitializer(params);
+ encryptionInitializer.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) {
- if (this.encryptionKey == null) {
- throw new IllegalStateException("PDF Encryption has not been initialized");
- }
- byte[] hash = calcHash(number, generation);
- return encryptWithHash(data, hash, hash.length);
+ * Creates and returns an encryption object.
+ *
+ * @param objectNumber the object number for the encryption dictionary
+ * @param params the encryption parameters
+ * @param pdf the PDF document to be encrypted
+ * @return the newly created encryption object
+ */
+ public static PDFEncryption make(
+ int objectNumber, PDFEncryptionParams params, PDFDocument pdf) {
+ return new PDFEncryptionJCE(objectNumber, params, pdf);
}
/** {@inheritDoc} */
@@ -350,63 +465,88 @@ public class PDFEncryptionJCE extends PD
if (o == null) {
throw new IllegalStateException("No object number could be obtained for a PDF object");
}
- return encryptData(data, o.getObjectNumber(), o.getGeneration());
- }
-
- private byte[] calcHash(int number, int generation) {
- 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 hash;
- }
-
- /**
- * 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);
+ byte[] key = createEncryptionKey(o.getObjectNumber(), o.getGeneration());
+ return encryptWithKey(key, data);
}
- /**
- * Adds a PDFFilter to the PDFStream object
- * @param stream the stream to add an encryption filter to
- */
+ /** {@inheritDoc} */
public void applyFilter(AbstractPDFStream stream) {
stream.getFilterList().addFilter(
- this.makeFilter(stream.getObjectNumber(), stream.getGeneration()));
+ new EncryptionFilter(stream.getObjectNumber(), stream.getGeneration()));
}
/**
- * Represent the object in PDF
+ * Prepares the encryption dictionary for output to a PDF file.
*
- * @return the PDF
+ * @return the encryption dictionary as a byte array
*/
public byte[] toPDF() {
- if (this.dictionary == null) {
- throw new IllegalStateException("PDF Encryption has not been initialized");
+ assert encryptionDictionary != null;
+ return encode(this.encryptionDictionary);
+ }
+
+ /** {@inheritDoc} */
+ public String getTrailerEntry() {
+ return "/Encrypt " + getObjectNumber() + " " + getGeneration() + " R\n";
+ }
+
+ private static byte[] encryptWithKey(byte[] key, byte[] data) {
+ try {
+ final Cipher c = initCipher(key);
+ return c.doFinal(data);
+ } catch (IllegalBlockSizeException e) {
+ throw new IllegalStateException(e.getMessage());
+ } catch (BadPaddingException e) {
+ throw new IllegalStateException(e.getMessage());
}
+ }
- return encode(this.dictionary);
+ private static Cipher initCipher(byte[] key) {
+ try {
+ Cipher c = Cipher.getInstance("RC4");
+ SecretKeySpec keyspec = new SecretKeySpec(key, "RC4");
+ c.init(Cipher.ENCRYPT_MODE, keyspec);
+ return c;
+ } catch (InvalidKeyException e) {
+ throw new IllegalStateException(e);
+ } catch (NoSuchAlgorithmException e) {
+ throw new UnsupportedOperationException(e);
+ } catch (NoSuchPaddingException e) {
+ throw new UnsupportedOperationException(e);
+ }
}
/**
- * {@inheritDoc}
- */
- public String getTrailerEntry() {
- return "/Encrypt " + getObjectNumber() + " "
- + getGeneration() + " R\n"
- + "/ID[" + getFileID(1) + getFileID(2) + "]\n";
+ * Applies Algorithm 3.1 from the PDF 1.4 Reference.
+ *
+ * @param objectNumber the object number
+ * @param generationNumber the generation number
+ * @return the key to use for encryption
+ */
+ private byte[] createEncryptionKey(int objectNumber, int generationNumber) {
+ // Step 1 passed in
+ // Step 2
+ byte[] md5Input = prepareMD5Input(objectNumber, generationNumber);
+ // Step 3
+ digest.reset();
+ byte[] hash = digest.digest(md5Input);
+ // Step 4
+ int keyLength = Math.min(16, md5Input.length);
+ byte[] key = new byte[keyLength];
+ System.arraycopy(hash, 0, key, 0, keyLength);
+ return key;
}
+
+ private byte[] prepareMD5Input(int objectNumber, int generationNumber) {
+ byte[] md5Input = new byte[encryptionKey.length + 5];
+ System.arraycopy(encryptionKey, 0, md5Input, 0, encryptionKey.length);
+ int i = encryptionKey.length;
+ md5Input[i++] = (byte) (objectNumber >>> 0);
+ md5Input[i++] = (byte) (objectNumber >>> 8);
+ md5Input[i++] = (byte) (objectNumber >>> 16);
+ md5Input[i++] = (byte) (generationNumber >>> 0);
+ md5Input[i++] = (byte) (generationNumber >>> 8);
+ return md5Input;
+ }
+
}
Modified: xmlgraphics/fop/branches/Temp_TrueTypeInPostScript/src/java/org/apache/fop/pdf/PDFEncryptionManager.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/branches/Temp_TrueTypeInPostScript/src/java/org/apache/fop/pdf/PDFEncryptionManager.java?rev=1309921&r1=1309920&r2=1309921&view=diff
==============================================================================
--- xmlgraphics/fop/branches/Temp_TrueTypeInPostScript/src/java/org/apache/fop/pdf/PDFEncryptionManager.java (original)
+++ xmlgraphics/fop/branches/Temp_TrueTypeInPostScript/src/java/org/apache/fop/pdf/PDFEncryptionManager.java Thu Apr 5 16:19:19 2012
@@ -109,16 +109,18 @@ public final class PDFEncryptionManager
* Creates a new PDFEncryption instance if PDF encryption is available.
* @param objnum PDF object number
* @param params PDF encryption parameters
+ * @param pdf the PDF document to encrypt
* @return PDFEncryption the newly created instance, null if PDF encryption
* is unavailable.
*/
- public static PDFEncryption newInstance(int objnum, PDFEncryptionParams params) {
+ public static PDFEncryption newInstance(int objnum, PDFEncryptionParams params,
+ PDFDocument pdf) {
try {
- Class clazz = Class.forName("org.apache.fop.pdf.PDFEncryptionJCE");
+ Class<?> clazz = Class.forName("org.apache.fop.pdf.PDFEncryptionJCE");
Method makeMethod = clazz.getMethod("make",
- new Class[] {int.class, PDFEncryptionParams.class});
+ new Class[] {int.class, PDFEncryptionParams.class, PDFDocument.class});
Object obj = makeMethod.invoke(null,
- new Object[] {new Integer(objnum), params});
+ new Object[] {new Integer(objnum), params, pdf});
return (PDFEncryption)obj;
} catch (ClassNotFoundException e) {
if (checkAvailableAlgorithms()) {
Modified: xmlgraphics/fop/branches/Temp_TrueTypeInPostScript/src/java/org/apache/fop/pdf/PDFEncryptionParams.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/branches/Temp_TrueTypeInPostScript/src/java/org/apache/fop/pdf/PDFEncryptionParams.java?rev=1309921&r1=1309920&r2=1309921&view=diff
==============================================================================
--- xmlgraphics/fop/branches/Temp_TrueTypeInPostScript/src/java/org/apache/fop/pdf/PDFEncryptionParams.java (original)
+++ xmlgraphics/fop/branches/Temp_TrueTypeInPostScript/src/java/org/apache/fop/pdf/PDFEncryptionParams.java Thu Apr 5 16:19:19 2012
@@ -26,10 +26,17 @@ public class PDFEncryptionParams {
private String userPassword = ""; //May not be null
private String ownerPassword = ""; //May not be null
+
private boolean allowPrint = true;
private boolean allowCopyContent = true;
private boolean allowEditContent = true;
private boolean allowEditAnnotations = true;
+ private boolean allowFillInForms = true;
+ private boolean allowAccessContent = true;
+ private boolean allowAssembleDocument = true;
+ private boolean allowPrintHq = true;
+
+ private int encryptionLengthInBits = 40;
/**
* Creates a new instance.
@@ -61,6 +68,25 @@ public class PDFEncryptionParams {
}
/**
+ * Creates a copy of the given encryption parameters.
+ *
+ * @param source source encryption parameters
+ */
+ public PDFEncryptionParams(PDFEncryptionParams source) {
+ setUserPassword(source.getUserPassword());
+ setOwnerPassword(source.getOwnerPassword());
+ setAllowPrint(source.isAllowPrint());
+ setAllowCopyContent(source.isAllowCopyContent());
+ setAllowEditContent(source.isAllowEditContent());
+ setAllowEditAnnotations(source.isAllowEditAnnotations());
+ setAllowAssembleDocument(source.isAllowAssembleDocument());
+ setAllowAccessContent(source.isAllowAccessContent());
+ setAllowFillInForms(source.isAllowFillInForms());
+ setAllowPrintHq(source.isAllowPrintHq());
+ setEncryptionLengthInBits(source.getEncryptionLengthInBits());
+ }
+
+ /**
* Indicates whether copying content is allowed.
* @return true if copying is allowed
*/
@@ -93,6 +119,38 @@ public class PDFEncryptionParams {
}
/**
+ * Indicates whether revision 3 filling in forms is allowed.
+ * @return true if revision 3 filling in forms is allowed
+ */
+ public boolean isAllowFillInForms() {
+ return allowFillInForms;
+ }
+
+ /**
+ * Indicates whether revision 3 extracting text and graphics is allowed.
+ * @return true if revision 3 extracting text and graphics is allowed
+ */
+ public boolean isAllowAccessContent() {
+ return allowAccessContent;
+ }
+
+ /**
+ * Indicates whether revision 3 assembling document is allowed.
+ * @return true if revision 3 assembling document is allowed
+ */
+ public boolean isAllowAssembleDocument() {
+ return allowAssembleDocument;
+ }
+
+ /**
+ * Indicates whether revision 3 printing to high quality is allowed.
+ * @return true if revision 3 printing to high quality is allowed
+ */
+ public boolean isAllowPrintHq() {
+ return allowPrintHq;
+ }
+
+ /**
* Returns the owner password.
* @return the owner password, an empty string if no password applies
*/
@@ -133,7 +191,7 @@ public class PDFEncryptionParams {
}
/**
- * Sets the persmission for printing.
+ * Sets the permission for printing.
* @param allowPrint true if printing is allowed
*/
public void setAllowPrint(boolean allowPrint) {
@@ -141,6 +199,38 @@ public class PDFEncryptionParams {
}
/**
+ * Sets whether revision 3 filling in forms is allowed.
+ * @param allowFillInForms true if revision 3 filling in forms is allowed.
+ */
+ public void setAllowFillInForms(boolean allowFillInForms) {
+ this.allowFillInForms = allowFillInForms;
+ }
+
+ /**
+ * Sets whether revision 3 extracting text and graphics is allowed.
+ * @param allowAccessContent true if revision 3 extracting text and graphics is allowed
+ */
+ public void setAllowAccessContent(boolean allowAccessContent) {
+ this.allowAccessContent = allowAccessContent;
+ }
+
+ /**
+ * Sets whether revision 3 assembling document is allowed.
+ * @param allowAssembleDocument true if revision 3 assembling document is allowed
+ */
+ public void setAllowAssembleDocument(boolean allowAssembleDocument) {
+ this.allowAssembleDocument = allowAssembleDocument;
+ }
+
+ /**
+ * Sets whether revision 3 printing to high quality is allowed.
+ * @param allowPrintHq true if revision 3 printing to high quality is allowed
+ */
+ public void setAllowPrintHq(boolean allowPrintHq) {
+ this.allowPrintHq = allowPrintHq;
+ }
+
+ /**
* Sets the owner password.
* @param ownerPassword The owner password to set, null or an empty String
* if no password is applicable
@@ -166,4 +256,21 @@ public class PDFEncryptionParams {
}
}
+ /**
+ * Returns the encryption length.
+ * @return the encryption length
+ */
+ public int getEncryptionLengthInBits() {
+ return encryptionLengthInBits;
+ }
+
+ /**
+ * Sets the encryption length.
+ *
+ * @param encryptionLength the encryption length
+ */
+ public void setEncryptionLengthInBits(int encryptionLength) {
+ this.encryptionLengthInBits = encryptionLength;
+ }
+
}
Modified: xmlgraphics/fop/branches/Temp_TrueTypeInPostScript/src/java/org/apache/fop/pdf/PDFFactory.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/branches/Temp_TrueTypeInPostScript/src/java/org/apache/fop/pdf/PDFFactory.java?rev=1309921&r1=1309920&r2=1309921&view=diff
==============================================================================
--- xmlgraphics/fop/branches/Temp_TrueTypeInPostScript/src/java/org/apache/fop/pdf/PDFFactory.java (original)
+++ xmlgraphics/fop/branches/Temp_TrueTypeInPostScript/src/java/org/apache/fop/pdf/PDFFactory.java Thu Apr 5 16:19:19 2012
@@ -20,13 +20,16 @@
package org.apache.fop.pdf;
// Java
+import java.awt.Color;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
+import java.text.DecimalFormat;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.BitSet;
import java.util.Iterator;
import java.util.List;
@@ -39,6 +42,11 @@ import org.apache.commons.io.IOUtils;
import org.apache.commons.io.output.ByteArrayOutputStream;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+
+import org.apache.xmlgraphics.java2d.color.ColorUtil;
+import org.apache.xmlgraphics.java2d.color.NamedColorSpace;
+import org.apache.xmlgraphics.xmp.Metadata;
+
import org.apache.fop.fonts.CIDFont;
import org.apache.fop.fonts.CIDSubset;
import org.apache.fop.fonts.CodePointMapping;
@@ -70,6 +78,8 @@ public class PDFFactory {
private Log log = LogFactory.getLog(PDFFactory.class);
+ private int subsetFontCounter = -1;
+
/**
* Creates a new PDFFactory.
* @param document the parent PDFDocument needed to register the generated
@@ -777,23 +787,23 @@ public class PDFFactory {
for (currentPosition = 0; currentPosition < lastPosition;
currentPosition++) { // for every consecutive color pair
- PDFColor currentColor = (PDFColor)theColors.get(currentPosition);
- PDFColor nextColor = (PDFColor)theColors.get(currentPosition
- + 1);
- // colorspace must be consistant
- if (getDocument().getColorSpace() != currentColor.getColorSpace()) {
- currentColor.setColorSpace(
- getDocument().getColorSpace());
- }
+ Color currentColor = (Color)theColors.get(currentPosition);
+ Color nextColor = (Color)theColors.get(currentPosition + 1);
- if (getDocument().getColorSpace()
- != nextColor.getColorSpace()) {
- nextColor.setColorSpace(
- getDocument().getColorSpace());
+ // colorspace must be consistent, so we simply convert to sRGB where necessary
+ if (!currentColor.getColorSpace().isCS_sRGB()) {
+ //Convert to sRGB
+ currentColor = ColorUtil.toSRGBColor(currentColor);
+ theColors.set(currentPosition, currentColor);
+ }
+ if (!nextColor.getColorSpace().isCS_sRGB()) {
+ //Convert to sRGB
+ nextColor = ColorUtil.toSRGBColor(nextColor);
+ theColors.set(currentPosition + 1, nextColor);
}
- theCzero = currentColor.getVector();
- theCone = nextColor.getVector();
+ theCzero = toColorVector(currentColor);
+ theCone = toColorVector(nextColor);
myfunc = makeFunction(2, null, null, theCzero, theCone,
interpolation);
@@ -841,6 +851,15 @@ public class PDFFactory {
return (myPattern);
}
+ private List toColorVector(Color nextColor) {
+ List vector = new java.util.ArrayList();
+ float[] comps = nextColor.getColorComponents(null);
+ for (int i = 0, c = comps.length; i < c; i++) {
+ vector.add(new Double(comps[i]));
+ }
+ return vector;
+ }
+
/* ============= named destinations and the name dictionary ============ */
/**
@@ -895,35 +914,6 @@ public class PDFFactory {
}
/**
- * Creates and returns a StructTreeRoot object. Used for accessibility.
- * @param parentTree the value of the ParenTree entry
- * @return structure Tree Root element
- */
- public PDFStructTreeRoot makeStructTreeRoot(PDFParentTree parentTree) {
- PDFStructTreeRoot structTreeRoot = new PDFStructTreeRoot(parentTree);
- getDocument().assignObjectNumber(structTreeRoot);
- getDocument().addTrailerObject(structTreeRoot);
- getDocument().getRoot().setStructTreeRoot(structTreeRoot);
- return structTreeRoot;
- }
-
- /**
- * Creates and returns a StructElem object.
- *
- * @param structureType the structure type of the new element (value for the
- * S entry)
- * @param parent the parent of the new structure element in the structure
- * hierarchy
- * @return the newly created element
- */
- public PDFStructElem makeStructureElement(PDFName structureType, PDFObject parent) {
- PDFStructElem structElem = new PDFStructElem(parent, structureType);
- getDocument().assignObjectNumber(structElem);
- getDocument().addTrailerObject(structElem);
- return structElem;
- }
-
- /**
* Make a the head object of the name dictionary (the /Dests object).
*
* @param destinationList a list of PDFDestination instances
@@ -1362,10 +1352,15 @@ public class PDFFactory {
} else {
FontType fonttype = metrics.getFontType();
- PDFFontDescriptor pdfdesc = makeFontDescriptor(descriptor);
+ String fontPrefix = descriptor.isSubsetEmbedded() ? createSubsetFontPrefix() : "";
+
+ String subsetFontName = fontPrefix + basefont;
+
+ PDFFontDescriptor pdfdesc = makeFontDescriptor(descriptor, fontPrefix);
PDFFont font = null;
- font = PDFFont.createFont(fontname, fonttype, basefont, null);
+
+ font = PDFFont.createFont(fontname, fonttype, subsetFontName, null);
getDocument().registerObject(font);
if (fonttype == FontType.TYPE0) {
@@ -1380,8 +1375,7 @@ public class PDFFactory {
= new PDFCIDSystemInfo(cidMetrics.getRegistry(),
cidMetrics.getOrdering(),
cidMetrics.getSupplement());
- PDFCIDFont cidFont
- = new PDFCIDFont(basefont,
+ PDFCIDFont cidFont = new PDFCIDFont(subsetFontName,
cidMetrics.getCIDType(),
cidMetrics.getDefaultWidth(),
getSubsetWidths(cidMetrics), sysInfo,
@@ -1535,18 +1529,35 @@ public class PDFFactory {
return warray;
}
+ private String createSubsetFontPrefix() {
+ subsetFontCounter++;
+ DecimalFormat counterFormat = new DecimalFormat("00000");
+ String counterString = counterFormat.format(subsetFontCounter);
+
+ // Subset prefix as described in chapter 5.5.3 of PDF 1.4
+ StringBuffer sb = new StringBuffer("E");
+
+ for (char c : counterString.toCharArray()) {
+ // translate numbers to uppercase characters
+ sb.append((char) (c + ('A' - '0')));
+ }
+ sb.append("+");
+ return sb.toString();
+ }
+
/**
* make a /FontDescriptor object
*
* @param desc the font descriptor
+ * @param fontPrefix the String with which to prefix the font name
* @return the new PDF font descriptor
*/
- public PDFFontDescriptor makeFontDescriptor(FontDescriptor desc) {
+ private PDFFontDescriptor makeFontDescriptor(FontDescriptor desc, String fontPrefix) {
PDFFontDescriptor descriptor = null;
if (desc.getFontType() == FontType.TYPE0) {
// CID Font
- descriptor = new PDFCIDFontDescriptor(desc.getEmbedFontName(),
+ descriptor = new PDFCIDFontDescriptor(fontPrefix + desc.getEmbedFontName(),
desc.getFontBBox(),
desc.getCapHeight(),
desc.getFlags(),
@@ -1780,6 +1791,38 @@ public class PDFFactory {
}
/**
+ * Create a new Separation color space.
+ * @param res the resource context (may be null)
+ * @param ncs the named color space to map to a separation color space
+ * @return the newly created Separation color space
+ */
+ public PDFSeparationColorSpace makeSeparationColorSpace(PDFResourceContext res,
+ NamedColorSpace ncs) {
+ String colorName = ncs.getColorName();
+ final Double zero = new Double(0d);
+ final Double one = new Double(1d);
+ List theDomain = Arrays.asList(new Double[] {zero, one});
+ List theRange = Arrays.asList(new Double[] {zero, one, zero, one, zero, one});
+ List theCZero = Arrays.asList(new Double[] {one, one, one});
+ List theCOne = new ArrayList();
+ float[] comps = ncs.getRGBColor().getColorComponents(null);
+ for (int i = 0, c = comps.length; i < c; i++) {
+ theCOne.add(new Double(comps[i]));
+ }
+ PDFFunction tintFunction = makeFunction(2, theDomain, theRange,
+ theCZero, theCOne, 1.0d);
+ PDFSeparationColorSpace cs = new PDFSeparationColorSpace(colorName, tintFunction);
+ getDocument().registerObject(cs);
+ if (res != null) {
+ res.getPDFResources().addColorSpace(cs);
+ } else {
+ getDocument().getResources().addColorSpace(cs);
+ }
+
+ return cs;
+ }
+
+ /**
* Make an Array object (ex. Widths array for a font).
*
* @param values the int array values
Modified: xmlgraphics/fop/branches/Temp_TrueTypeInPostScript/src/java/org/apache/fop/pdf/PDFFilter.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/branches/Temp_TrueTypeInPostScript/src/java/org/apache/fop/pdf/PDFFilter.java?rev=1309921&r1=1309920&r2=1309921&view=diff
==============================================================================
--- xmlgraphics/fop/branches/Temp_TrueTypeInPostScript/src/java/org/apache/fop/pdf/PDFFilter.java (original)
+++ xmlgraphics/fop/branches/Temp_TrueTypeInPostScript/src/java/org/apache/fop/pdf/PDFFilter.java Thu Apr 5 16:19:19 2012
@@ -19,15 +19,15 @@
package org.apache.fop.pdf;
-import java.io.OutputStream;
import java.io.IOException;
+import java.io.OutputStream;
/**
- * PDF Filter class.
- * This represents a PDF filter object.
- * Filter implementations should extend this class.
+ * <p>PDF Filter class.
+ * This class represents a PDF filter object.
+ * Filter implementations should extend this class.</p>
*
- * @author Eric SCHAEFFER, Kelly A. Campbell
+ * <p>This work was authored by Eric Schaeffer and Kelly A. Campbell.</p>
*/
public abstract class PDFFilter {
/*
Modified: xmlgraphics/fop/branches/Temp_TrueTypeInPostScript/src/java/org/apache/fop/pdf/PDFFilterException.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/branches/Temp_TrueTypeInPostScript/src/java/org/apache/fop/pdf/PDFFilterException.java?rev=1309921&r1=1309920&r2=1309921&view=diff
==============================================================================
--- xmlgraphics/fop/branches/Temp_TrueTypeInPostScript/src/java/org/apache/fop/pdf/PDFFilterException.java (original)
+++ xmlgraphics/fop/branches/Temp_TrueTypeInPostScript/src/java/org/apache/fop/pdf/PDFFilterException.java Thu Apr 5 16:19:19 2012
@@ -20,10 +20,10 @@
package org.apache.fop.pdf;
/**
- * PDF Filter exception.
- * This is used for exceptions relating to use a PDF filter.
+ * <p>PDF Filter exception.
+ * This is used for exceptions relating to use a PDF filter.</p>
*
- * @author Eric SCHAEFFER
+ * <p>This work was authored by Eric Schaeffer.</p>
*/
public class PDFFilterException extends Exception {
/**
Modified: xmlgraphics/fop/branches/Temp_TrueTypeInPostScript/src/java/org/apache/fop/pdf/PDFFilterList.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/branches/Temp_TrueTypeInPostScript/src/java/org/apache/fop/pdf/PDFFilterList.java?rev=1309921&r1=1309920&r2=1309921&view=diff
==============================================================================
--- xmlgraphics/fop/branches/Temp_TrueTypeInPostScript/src/java/org/apache/fop/pdf/PDFFilterList.java (original)
+++ xmlgraphics/fop/branches/Temp_TrueTypeInPostScript/src/java/org/apache/fop/pdf/PDFFilterList.java Thu Apr 5 16:19:19 2012
@@ -21,6 +21,7 @@ package org.apache.fop.pdf;
import java.io.IOException;
import java.io.OutputStream;
+import java.util.Collections;
import java.util.List;
import java.util.Map;
@@ -47,7 +48,7 @@ public class PDFFilterList {
/** Key for the filter used for metadata */
public static final String METADATA_FILTER = "metadata";
- private List filters = new java.util.ArrayList();
+ private List<PDFFilter> filters = new java.util.ArrayList<PDFFilter>();
private boolean ignoreASCIIFilters = false;
@@ -197,6 +198,10 @@ public class PDFFilterList {
}
}
+ List<PDFFilter> getFilters() {
+ return Collections.unmodifiableList(filters);
+ }
+
/**
* Apply the filters to the data
* in the order given and return the /Filter and /DecodeParms
@@ -206,7 +211,7 @@ public class PDFFilterList {
* @return a String representing the filter list
*/
protected String buildFilterDictEntries() {
- if (filters != null && filters.size() > 0) {
+ if (filters.size() > 0) {
List names = new java.util.ArrayList();
List parms = new java.util.ArrayList();
@@ -229,7 +234,7 @@ public class PDFFilterList {
* @param dict the PDFDictionary to set the entries on
*/
protected void putFilterDictEntries(PDFDictionary dict) {
- if (filters != null && filters.size() > 0) {
+ if (filters.size() > 0) {
List names = new java.util.ArrayList();
List parms = new java.util.ArrayList();
@@ -358,7 +363,7 @@ public class PDFFilterList {
*/
public OutputStream applyFilters(OutputStream stream) throws IOException {
OutputStream out = stream;
- if (filters != null && !isDisableAllFilters()) {
+ if (!isDisableAllFilters()) {
for (int count = filters.size() - 1; count >= 0; count--) {
PDFFilter filter = (PDFFilter)filters.get(count);
out = filter.applyFilter(out);
Modified: xmlgraphics/fop/branches/Temp_TrueTypeInPostScript/src/java/org/apache/fop/pdf/PDFFont.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/branches/Temp_TrueTypeInPostScript/src/java/org/apache/fop/pdf/PDFFont.java?rev=1309921&r1=1309920&r2=1309921&view=diff
==============================================================================
--- xmlgraphics/fop/branches/Temp_TrueTypeInPostScript/src/java/org/apache/fop/pdf/PDFFont.java (original)
+++ xmlgraphics/fop/branches/Temp_TrueTypeInPostScript/src/java/org/apache/fop/pdf/PDFFont.java Thu Apr 5 16:19:19 2012
@@ -174,7 +174,7 @@ public class PDFFont extends PDFDictiona
}
/** {@inheritDoc} */
- protected int output(OutputStream stream) throws IOException {
+ public int output(OutputStream stream) throws IOException {
validate();
return super.output(stream);
}
---------------------------------------------------------------------
To unsubscribe, e-mail: fop-commits-unsubscribe@xmlgraphics.apache.org
For additional commands, e-mail: fop-commits-help@xmlgraphics.apache.org