aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/documentation/content/xdocs/trunk/output.xml94
-rw-r--r--src/java/META-INF/services/org.apache.fop.fo.ElementMapping1
-rw-r--r--src/java/org/apache/fop/pdf/PDFEmbeddedFile.java45
-rw-r--r--src/java/org/apache/fop/pdf/PDFFactory.java54
-rw-r--r--src/java/org/apache/fop/pdf/PDFFileSpec.java41
-rw-r--r--src/java/org/apache/fop/pdf/PDFGoTo.java8
-rw-r--r--src/java/org/apache/fop/pdf/PDFLaunch.java24
-rw-r--r--src/java/org/apache/fop/pdf/PDFNames.java23
-rw-r--r--src/java/org/apache/fop/pdf/PDFProfile.java12
-rw-r--r--src/java/org/apache/fop/render/pdf/PDFDocumentHandler.java9
-rw-r--r--src/java/org/apache/fop/render/pdf/PDFDocumentNavigationHandler.java5
-rw-r--r--src/java/org/apache/fop/render/pdf/PDFRenderer.java8
-rw-r--r--src/java/org/apache/fop/render/pdf/PDFRenderingUtil.java65
-rw-r--r--src/java/org/apache/fop/render/pdf/extensions/AbstractPDFExtensionElement.java73
-rw-r--r--src/java/org/apache/fop/render/pdf/extensions/PDFElementMapping.java51
-rw-r--r--src/java/org/apache/fop/render/pdf/extensions/PDFEmbeddedFileElement.java105
-rw-r--r--src/java/org/apache/fop/render/pdf/extensions/PDFEmbeddedFileExtensionAttachment.java155
-rw-r--r--src/java/org/apache/fop/render/pdf/extensions/PDFExtensionAttachment.java65
-rw-r--r--src/java/org/apache/fop/render/pdf/extensions/PDFExtensionHandler.java97
-rw-r--r--status.xml5
20 files changed, 872 insertions, 68 deletions
diff --git a/src/documentation/content/xdocs/trunk/output.xml b/src/documentation/content/xdocs/trunk/output.xml
index d1ab9e19f..1766cd493 100644
--- a/src/documentation/content/xdocs/trunk/output.xml
+++ b/src/documentation/content/xdocs/trunk/output.xml
@@ -194,32 +194,76 @@ out = proc.getOutputStream();]]></source>
e.printStackTrace();
}
}]]></source>
- <p>
- Check the iText tutorial and documentation for setting access flags, password,
- encryption strength and other parameters.
- </p>
- </section>
- <section id="pdf-watermark">
- <title>Watermarks</title>
- <p>
- In addition to the <a href="#pdf-postprocess">PDF Post-processing</a> options, consider the following workarounds:
- </p>
- <ul>
- <li>
- Use a background image for the body region.
- </li>
- <li>
- (submitted by Trevor Campbell) Place an image in a
- region that overlaps the flowing text. For example, make
- region-before large enough to contain your image. Then include a
- block (if necessary, use an absolutely positioned block-container)
- containing the watermark image in the static-content for the
- region-before. Note that the image will be drawn on top of the
- normal content.
- </li>
- </ul>
+ <p>
+ Check the iText tutorial and documentation for setting access flags, password,
+ encryption strength and other parameters.
+ </p>
+ </section>
+ <section id="pdf-watermark">
+ <title>Watermarks</title>
+ <p>
+ In addition to the <a href="#pdf-postprocess">PDF Post-processing</a> options, consider the following workarounds:
+ </p>
+ <ul>
+ <li>
+ Use a background image for the body region.
+ </li>
+ <li>
+ (submitted by Trevor Campbell) Place an image in a
+ region that overlaps the flowing text. For example, make
+ region-before large enough to contain your image. Then include a
+ block (if necessary, use an absolutely positioned block-container)
+ containing the watermark image in the static-content for the
+ region-before. Note that the image will be drawn on top of the
+ normal content.
+ </li>
+ </ul>
+ </section>
+ <section id="pdf-extensions">
+ <title>Extensions</title>
+ <p>The PDF Renderer supports some PDF specific extensions which can be embedded
+ into the input FO document. To use the extensions the appropriate namespace must
+ be declared in the fo:root element like this:</p>
+ <source><![CDATA[
+<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format"
+ xmlns:pdf="http://xmlgraphics.apache.org/fop/extensions/pdf">
+ ]]></source>
+ <section id="pdf-embedded-file">
+ <title>Embedded Files</title>
+ <p>
+ It is possible to attach/embed arbitrary files into a PDF file. You can give a name and
+ a description of the file. Example:
+ </p>
+ <source><![CDATA[
+ <fo:declarations>
+ <pdf:embedded-file filename="image.jpg" src="url(file:///C:/Temp/myimage.jpg)" description="My image"/>
+ <pdf:embedded-file src="url(file:///C:/Temp/MyTextDoc.odt)"/>
+ </fo:declarations>
+ ]]></source>
+ <p>
+ <code>pdf:embedded-file</code> must be a child of <code>fo:declarations</code>.
+ The "src" property is used to reference the file that is to be embedded. This property
+ uses the "uri-specification" datatype from the XSL-FO specification.
+ The "filename" property is optional. If it is missing the filename is automatically set
+ from the URI/IRI of the "src" property. An optional description can also be added to
+ further describe the file attachment.
+ </p>
+ <p>
+ It is also possible to reference an embedded file from an <code>fo:basic-link</code>.
+ Use the special "embedded-file:" URI scheme with the filename as single argument after
+ the URI scheme. Example:
+ </p>
+ <source><![CDATA[
+<fo:basic-link external-destination="url(embedded-file:image.jpg)">Attached Image</fo:basic-link>
+]]></source>
+ <p>
+ Note: Not all PDF Viewers (including some Acrobat Versions) will open the embedded file
+ when clicking on the link. In that case, the user will have to open he attachment via
+ the separate list of file attachments.
+ </p>
+ </section>
+ </section>
</section>
-</section>
<section id="ps">
<title>PostScript</title>
<p>
diff --git a/src/java/META-INF/services/org.apache.fop.fo.ElementMapping b/src/java/META-INF/services/org.apache.fop.fo.ElementMapping
index b62f36196..6f4374a93 100644
--- a/src/java/META-INF/services/org.apache.fop.fo.ElementMapping
+++ b/src/java/META-INF/services/org.apache.fop.fo.ElementMapping
@@ -9,3 +9,4 @@ org.apache.fop.fo.extensions.xmp.RDFElementMapping
org.apache.fop.render.ps.extensions.PSExtensionElementMapping
org.apache.fop.render.afp.extensions.AFPElementMapping
org.apache.fop.render.pcl.extensions.PCLElementMapping
+org.apache.fop.render.pdf.extensions.PDFElementMapping
diff --git a/src/java/org/apache/fop/pdf/PDFEmbeddedFile.java b/src/java/org/apache/fop/pdf/PDFEmbeddedFile.java
new file mode 100644
index 000000000..78010dd78
--- /dev/null
+++ b/src/java/org/apache/fop/pdf/PDFEmbeddedFile.java
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.pdf;
+
+import java.util.Date;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * This class represents an embedded file stream.
+ */
+public class PDFEmbeddedFile extends PDFStream {
+
+ protected static Log log = LogFactory.getLog(PDFEmbeddedFile.class.getName());
+
+ /**
+ * Creates a new embedded file stream.
+ */
+ public PDFEmbeddedFile() {
+ super();
+ put("Type", new PDFName("EmbeddedFile"));
+ PDFDictionary params = new PDFDictionary();
+ params.put("CreationDate", params.formatDateTime(new Date()));
+ put("Params", params);
+ }
+
+}
diff --git a/src/java/org/apache/fop/pdf/PDFFactory.java b/src/java/org/apache/fop/pdf/PDFFactory.java
index bf3399b09..f92fdc4e7 100644
--- a/src/java/org/apache/fop/pdf/PDFFactory.java
+++ b/src/java/org/apache/fop/pdf/PDFFactory.java
@@ -1044,6 +1044,8 @@ public class PDFFactory {
return link;
}
+ private static final String EMBEDDED_FILE = "embedded-file:";
+
/**
* Create/find and return the appropriate external PDFAction according to the target
*
@@ -1056,32 +1058,70 @@ public class PDFFactory {
public PDFAction getExternalAction(String target, boolean newWindow) {
int index;
String targetLo = target.toLowerCase();
- // HTTP URL?
- if (targetLo.startsWith("http://")) {
+ if (target.startsWith(EMBEDDED_FILE)) {
+ // File Attachments (Embedded Files)
+ String filename = target.substring(EMBEDDED_FILE.length());
+ return getLaunchActionForEmbeddedFile(filename);
+ } else if (targetLo.startsWith("http://")) {
+ // HTTP URL?
return new PDFUri(target);
- // Non PDF files. Try to /Launch them.
} else if (targetLo.startsWith("file://")) {
+ // Non PDF files. Try to /Launch them.
target = target.substring("file://".length());
return getLaunchAction(target);
- // Bare PDF file name?
} else if (targetLo.endsWith(".pdf")) {
+ // Bare PDF file name?
return getGoToPDFAction(target, null, -1, newWindow);
- // PDF file + page?
} else if ((index = targetLo.indexOf(".pdf#page=")) > 0) {
+ // PDF file + page?
String filename = target.substring(0, index + 4);
int page = Integer.parseInt(target.substring(index + 10));
return getGoToPDFAction(filename, null, page, newWindow);
- // PDF file + destination?
} else if ((index = targetLo.indexOf(".pdf#dest=")) > 0) {
+ // PDF file + destination?
String filename = target.substring(0, index + 4);
String dest = target.substring(index + 10);
return getGoToPDFAction(filename, dest, -1, newWindow);
- // None of the above? Default to URI:
} else {
+ // None of the above? Default to URI:
return new PDFUri(target);
}
}
+ private PDFAction getLaunchActionForEmbeddedFile(String filename) {
+ PDFNames names = getDocument().getRoot().getNames();
+ if (names == null) {
+ throw new IllegalStateException(
+ "No Names dictionary present."
+ + " Cannot create Launch Action for embedded file: " + filename);
+ }
+ PDFNameTreeNode embeddedFiles = names.getEmbeddedFiles();
+ if (embeddedFiles == null) {
+ throw new IllegalStateException(
+ "No /EmbeddedFiles name tree present."
+ + " Cannot create Launch Action for embedded file: " + filename);
+ }
+ PDFArray files = embeddedFiles.getNames();
+ PDFReference embeddedFileRef = null;
+ int i = 0;
+ while (i < files.length()) {
+ String name = (String)files.get(i);
+ i++;
+ PDFReference ref = (PDFReference)files.get(i);
+ if (name.equals(filename)) {
+ embeddedFileRef = ref;
+ break;
+ }
+ i++;
+ }
+ if (embeddedFileRef == null) {
+ throw new IllegalStateException(
+ "No embedded file with name " + filename + " present.");
+ }
+ PDFLaunch launch = new PDFLaunch(embeddedFileRef);
+ return launch;
+ }
+
/**
* Create or find a PDF GoTo with the given page reference string and Y offset,
* and return its PDF object reference
diff --git a/src/java/org/apache/fop/pdf/PDFFileSpec.java b/src/java/org/apache/fop/pdf/PDFFileSpec.java
index 8de4164af..44195d4ee 100644
--- a/src/java/org/apache/fop/pdf/PDFFileSpec.java
+++ b/src/java/org/apache/fop/pdf/PDFFileSpec.java
@@ -19,16 +19,12 @@
package org.apache.fop.pdf;
+
/**
* class representing a /FileSpec object.
*
*/
-public class PDFFileSpec extends PDFObject {
-
- /**
- * the filename
- */
- protected String filename;
+public class PDFFileSpec extends PDFDictionary {
/**
* create a /FileSpec object.
@@ -39,29 +35,30 @@ public class PDFFileSpec extends PDFObject {
/* generic creation of object */
super();
+ put("Type", new PDFName("Filespec"));
+ put("F", filename);
+ }
- this.filename = filename;
+ private String getFilename() {
+ return (String)get("F");
}
/**
- * {@inheritDoc}
+ * Associates an dictionary with pointers to embedded file streams with this file spec.
+ * @param embeddedFile the dictionary with pointers to embedded file streams
*/
- public String toPDFString() {
- return getObjectID()
- + "<<\n/Type /FileSpec\n"
- + "/F (" + this.filename + ")\n"
- + ">>\nendobj\n";
+ public void setEmbeddedFile(PDFDictionary embeddedFileDict) {
+ put("EF", embeddedFileDict);
}
- /*
- * example
- * 29 0 obj
- * <<
- * /Type /FileSpec
- * /F (table1.pdf)
- * >>
- * endobj
+ /**
+ * Sets a description for the file spec.
+ * @param description the description
+ * @since PDF 1.6
*/
+ public void setDescription(String description) {
+ put("Desc", description);
+ }
/** {@inheritDoc} */
protected boolean contentEquals(PDFObject obj) {
@@ -75,7 +72,7 @@ public class PDFFileSpec extends PDFObject {
PDFFileSpec spec = (PDFFileSpec)obj;
- if (!spec.filename.equals(filename)) {
+ if (!spec.getFilename().equals(getFilename())) {
return false;
}
diff --git a/src/java/org/apache/fop/pdf/PDFGoTo.java b/src/java/org/apache/fop/pdf/PDFGoTo.java
index ad04650c6..0626f0fcd 100644
--- a/src/java/org/apache/fop/pdf/PDFGoTo.java
+++ b/src/java/org/apache/fop/pdf/PDFGoTo.java
@@ -42,10 +42,8 @@ public class PDFGoTo extends PDFAction {
* @param pageReference the pageReference represented by this object
*/
public PDFGoTo(String pageReference) {
- /* generic creation of object */
super();
-
- this.pageReference = pageReference;
+ setPageReference(pageReference);
}
/**
@@ -56,9 +54,7 @@ public class PDFGoTo extends PDFAction {
*/
public PDFGoTo(String pageReference, Point2D position) {
/* generic creation of object */
- super();
-
- this.pageReference = pageReference;
+ this(pageReference);
setPosition(position);
}
diff --git a/src/java/org/apache/fop/pdf/PDFLaunch.java b/src/java/org/apache/fop/pdf/PDFLaunch.java
index 386a7a9a3..f32afe656 100644
--- a/src/java/org/apache/fop/pdf/PDFLaunch.java
+++ b/src/java/org/apache/fop/pdf/PDFLaunch.java
@@ -22,21 +22,39 @@ package org.apache.fop.pdf;
*/
public class PDFLaunch extends PDFAction {
- private PDFFileSpec externalFileSpec;
+ private PDFReference externalFileSpec;
+ /**
+ * Creates a new /Launch action.
+ * @param fileSpec the file specification to launch
+ */
public PDFLaunch(PDFFileSpec fileSpec) {
+ this(fileSpec.makeReference());
+ }
+
+ /**
+ * Creates a new /Launch action.
+ * @param fileSpec a reference to the file specification
+ */
+ public PDFLaunch(PDFReference fileSpec) {
+ PDFObject fs = fileSpec.getObject();
+ if (fs != null) {
+ assert fs instanceof PDFFileSpec;
+ }
this.externalFileSpec = fileSpec;
}
+ /** {@inheritDoc} */
public String getAction() {
return this.referencePDF();
}
+ /** {@inheritDoc} */
public String toPDFString() {
StringBuffer sb = new StringBuffer(64);
sb.append(getObjectID());
sb.append("<<\n/S /Launch\n/F ");
- sb.append(externalFileSpec.referencePDF());
+ sb.append(externalFileSpec.toString());
sb.append(" \n>>\nendobj\n");
return sb.toString();
@@ -54,7 +72,7 @@ public class PDFLaunch extends PDFAction {
PDFLaunch launch = (PDFLaunch) obj;
- if (!launch.externalFileSpec.referencePDF().equals(externalFileSpec.referencePDF())) {
+ if (!launch.externalFileSpec.toString().equals(externalFileSpec.toString())) {
return false;
}
diff --git a/src/java/org/apache/fop/pdf/PDFNames.java b/src/java/org/apache/fop/pdf/PDFNames.java
index 4253dd6d1..aa6f64547 100644
--- a/src/java/org/apache/fop/pdf/PDFNames.java
+++ b/src/java/org/apache/fop/pdf/PDFNames.java
@@ -24,6 +24,9 @@ package org.apache.fop.pdf;
*/
public class PDFNames extends PDFDictionary {
+ private static final String DESTS = "Dests";
+ private static final String EMBEDDED_FILES = "EmbeddedFiles";
+
/**
* Create the Names object
*/
@@ -37,7 +40,7 @@ public class PDFNames extends PDFDictionary {
* @return the Dests object, or null if it's not used
*/
public PDFDests getDests() {
- return (PDFDests)get("Dests");
+ return (PDFDests)get(DESTS);
}
/**
@@ -45,7 +48,23 @@ public class PDFNames extends PDFDictionary {
* @param dests the Dests object
*/
public void setDests(PDFDests dests) {
- put("Dests", dests);
+ put(DESTS, dests);
+ }
+
+ /**
+ * Returns the EmbeddedFiles object
+ * @return the EmbeddedFiles object, or null if it's not used
+ */
+ public PDFNameTreeNode getEmbeddedFiles() {
+ return (PDFNameTreeNode)get(EMBEDDED_FILES);
+ }
+
+ /**
+ * Set the EmbeddedFiles object
+ * @param dests the EmbeddedFiles object
+ */
+ public void setEmbeddedFiles(PDFNameTreeNode dests) {
+ put(EMBEDDED_FILES, dests);
}
}
diff --git a/src/java/org/apache/fop/pdf/PDFProfile.java b/src/java/org/apache/fop/pdf/PDFProfile.java
index b645cb825..fb4575105 100644
--- a/src/java/org/apache/fop/pdf/PDFProfile.java
+++ b/src/java/org/apache/fop/pdf/PDFProfile.java
@@ -265,4 +265,16 @@ public class PDFProfile {
}
}
+ /** Checks if embedded files are allowed. */
+ public void verifyEmbeddedFilesAllowed() {
+ final String err = "{0} does not allow embedded files.";
+ if (isPDFAActive()) {
+ throw new PDFConformanceException(format(err, getPDFAMode()));
+ }
+ if (isPDFXActive()) {
+ //Implicit since file specs are forbidden
+ throw new PDFConformanceException(format(err, getPDFXMode()));
+ }
+ }
+
}
diff --git a/src/java/org/apache/fop/render/pdf/PDFDocumentHandler.java b/src/java/org/apache/fop/render/pdf/PDFDocumentHandler.java
index bc2f16f45..c7210344d 100644
--- a/src/java/org/apache/fop/render/pdf/PDFDocumentHandler.java
+++ b/src/java/org/apache/fop/render/pdf/PDFDocumentHandler.java
@@ -51,6 +51,7 @@ import org.apache.fop.render.intermediate.IFDocumentHandlerConfigurator;
import org.apache.fop.render.intermediate.IFDocumentNavigationHandler;
import org.apache.fop.render.intermediate.IFException;
import org.apache.fop.render.intermediate.IFPainter;
+import org.apache.fop.render.pdf.extensions.PDFEmbeddedFileExtensionAttachment;
import org.apache.fop.util.XMLUtil;
/**
@@ -292,6 +293,14 @@ public class PDFDocumentHandler extends AbstractBinaryWritingIFDocumentHandler {
} else if (extension instanceof Metadata) {
XMPMetadata wrapper = new XMPMetadata(((Metadata)extension));
pdfUtil.renderXMPMetadata(wrapper);
+ } else if (extension instanceof PDFEmbeddedFileExtensionAttachment) {
+ PDFEmbeddedFileExtensionAttachment embeddedFile
+ = (PDFEmbeddedFileExtensionAttachment)extension;
+ try {
+ pdfUtil.addEmbeddedFile(embeddedFile);
+ } catch (IOException ioe) {
+ throw new IFException("Error adding embedded file: " + embeddedFile.getSrc(), ioe);
+ }
} else {
log.debug("Don't know how to handle extension object. Ignoring: "
+ extension + " (" + extension.getClass().getName() + ")");
diff --git a/src/java/org/apache/fop/render/pdf/PDFDocumentNavigationHandler.java b/src/java/org/apache/fop/render/pdf/PDFDocumentNavigationHandler.java
index 5e1b1b250..c215243a7 100644
--- a/src/java/org/apache/fop/render/pdf/PDFDocumentNavigationHandler.java
+++ b/src/java/org/apache/fop/render/pdf/PDFDocumentNavigationHandler.java
@@ -166,10 +166,11 @@ public class PDFDocumentNavigationHandler implements IFDocumentNavigationHandler
} else if (action instanceof URIAction) {
URIAction u = (URIAction)action;
assert u.isComplete();
+ String uri = u.getURI();
PDFFactory factory = getPDFDoc().getFactory();
- pdfAction = factory.getExternalAction(u.getURI(), u.isNewWindow());
+ pdfAction = factory.getExternalAction(uri, u.isNewWindow());
if (!pdfAction.hasObjectNumber()) {
- //Some PDF actions a pooled
+ //Some PDF actions are pooled
getPDFDoc().registerObject(pdfAction);
}
this.completeActions.put(action.getID(), pdfAction);
diff --git a/src/java/org/apache/fop/render/pdf/PDFRenderer.java b/src/java/org/apache/fop/render/pdf/PDFRenderer.java
index aab84807c..b0809c10f 100644
--- a/src/java/org/apache/fop/render/pdf/PDFRenderer.java
+++ b/src/java/org/apache/fop/render/pdf/PDFRenderer.java
@@ -97,6 +97,7 @@ import org.apache.fop.render.AbstractPathOrientedRenderer;
import org.apache.fop.render.Graphics2DAdapter;
import org.apache.fop.render.RendererContext;
import org.apache.fop.render.pdf.PDFLogicalStructureHandler.MarkedContentInfo;
+import org.apache.fop.render.pdf.extensions.PDFEmbeddedFileExtensionAttachment;
import org.apache.fop.traits.RuleStyle;
import org.apache.fop.util.AbstractPaintingState;
import org.apache.fop.util.CharUtilities;
@@ -312,6 +313,13 @@ public class PDFRenderer extends AbstractPathOrientedRenderer implements PDFConf
ExtensionAttachment attachment = ((OffDocumentExtensionAttachment)odi).getAttachment();
if (XMPMetadata.CATEGORY.equals(attachment.getCategory())) {
pdfUtil.renderXMPMetadata((XMPMetadata)attachment);
+ } else if (PDFEmbeddedFileExtensionAttachment.CATEGORY.equals(
+ attachment.getCategory())) {
+ try {
+ pdfUtil.addEmbeddedFile((PDFEmbeddedFileExtensionAttachment)attachment);
+ } catch (IOException ioe) {
+ throw new RuntimeException("Error embedding file", ioe);
+ }
}
}
}
diff --git a/src/java/org/apache/fop/render/pdf/PDFRenderingUtil.java b/src/java/org/apache/fop/render/pdf/PDFRenderingUtil.java
index 3d68812b1..f6123fb33 100644
--- a/src/java/org/apache/fop/render/pdf/PDFRenderingUtil.java
+++ b/src/java/org/apache/fop/render/pdf/PDFRenderingUtil.java
@@ -20,6 +20,7 @@
package org.apache.fop.render.pdf;
import java.awt.color.ICC_Profile;
+import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
@@ -33,6 +34,7 @@ import org.apache.commons.io.IOUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+import org.apache.xmlgraphics.image.loader.util.ImageUtil;
import org.apache.xmlgraphics.xmp.Metadata;
import org.apache.xmlgraphics.xmp.schemas.XMPBasicAdapter;
import org.apache.xmlgraphics.xmp.schemas.XMPBasicSchema;
@@ -41,19 +43,26 @@ import org.apache.fop.accessibility.Accessibility;
import org.apache.fop.apps.FOUserAgent;
import org.apache.fop.fo.extensions.xmp.XMPMetadata;
import org.apache.fop.pdf.PDFAMode;
+import org.apache.fop.pdf.PDFArray;
import org.apache.fop.pdf.PDFConformanceException;
import org.apache.fop.pdf.PDFDictionary;
import org.apache.fop.pdf.PDFDocument;
+import org.apache.fop.pdf.PDFEmbeddedFile;
import org.apache.fop.pdf.PDFEncryptionManager;
import org.apache.fop.pdf.PDFEncryptionParams;
+import org.apache.fop.pdf.PDFFileSpec;
import org.apache.fop.pdf.PDFICCBasedColorSpace;
import org.apache.fop.pdf.PDFICCStream;
import org.apache.fop.pdf.PDFInfo;
import org.apache.fop.pdf.PDFMetadata;
+import org.apache.fop.pdf.PDFNameTreeNode;
+import org.apache.fop.pdf.PDFNames;
import org.apache.fop.pdf.PDFNumsArray;
import org.apache.fop.pdf.PDFOutputIntent;
import org.apache.fop.pdf.PDFPageLabels;
+import org.apache.fop.pdf.PDFReference;
import org.apache.fop.pdf.PDFXMode;
+import org.apache.fop.render.pdf.extensions.PDFEmbeddedFileExtensionAttachment;
import org.apache.fop.util.ColorProfileUtil;
/**
@@ -413,4 +422,60 @@ class PDFRenderingUtil implements PDFConfigurationConstants {
nums.put(pageIndex, dict);
}
+ /**
+ * Adds an embedded file to the PDF file.
+ * @param embeddedFile the object representing the embedded file to be added
+ * @throws IOException if an I/O error occurs
+ */
+ public void addEmbeddedFile(PDFEmbeddedFileExtensionAttachment embeddedFile)
+ throws IOException {
+ this.pdfDoc.getProfile().verifyEmbeddedFilesAllowed();
+ PDFNames names = this.pdfDoc.getRoot().getNames();
+ if (names == null) {
+ //Add Names if not already present
+ names = this.pdfDoc.getFactory().makeNames();
+ this.pdfDoc.getRoot().setNames(names);
+ }
+
+ //Create embedded file
+ PDFEmbeddedFile file = new PDFEmbeddedFile();
+ this.pdfDoc.registerObject(file);
+ Source src = getUserAgent().resolveURI(embeddedFile.getSrc());
+ InputStream in = ImageUtil.getInputStream(src);
+ if (in == null) {
+ throw new FileNotFoundException(embeddedFile.getSrc());
+ }
+ try {
+ OutputStream out = file.getBufferOutputStream();
+ IOUtils.copyLarge(in, out);
+ } finally {
+ IOUtils.closeQuietly(in);
+ }
+ PDFDictionary dict = new PDFDictionary();
+ dict.put("F", file);
+ PDFFileSpec fileSpec = new PDFFileSpec(embeddedFile.getFilename());
+ fileSpec.setEmbeddedFile(dict);
+ if (embeddedFile.getDesc() != null) {
+ fileSpec.setDescription(embeddedFile.getDesc());
+ }
+ this.pdfDoc.registerObject(fileSpec);
+
+ //Make sure there is an EmbeddedFiles in the Names dictionary
+ PDFNameTreeNode embeddedFiles = names.getEmbeddedFiles();
+ if (embeddedFiles == null) {
+ embeddedFiles = new PDFNameTreeNode();
+ //this.pdfDoc.registerObject(embeddedFiles);
+ names.setEmbeddedFiles(embeddedFiles);
+ }
+
+ //Add to EmbeddedFiles in the Names dictionary
+ PDFArray nameArray = embeddedFiles.getNames();
+ if (nameArray == null) {
+ nameArray = new PDFArray();
+ embeddedFiles.setNames(nameArray);
+ }
+ nameArray.add(embeddedFile.getFilename());
+ nameArray.add(new PDFReference(fileSpec));
+ }
+
}
diff --git a/src/java/org/apache/fop/render/pdf/extensions/AbstractPDFExtensionElement.java b/src/java/org/apache/fop/render/pdf/extensions/AbstractPDFExtensionElement.java
new file mode 100644
index 000000000..2b50112c0
--- /dev/null
+++ b/src/java/org/apache/fop/render/pdf/extensions/AbstractPDFExtensionElement.java
@@ -0,0 +1,73 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.render.pdf.extensions;
+
+// FOP
+import org.apache.fop.fo.FONode;
+import org.apache.fop.fo.extensions.ExtensionAttachment;
+
+/**
+ * Base class for the PDF-specific extension elements.
+ */
+public abstract class AbstractPDFExtensionElement extends FONode {
+
+ /**Extension attachment. */
+ protected PDFExtensionAttachment attachment;
+
+ /**
+ * Default constructor
+ *
+ * @param parent parent of this node
+ * @see org.apache.fop.fo.FONode#FONode(FONode)
+ */
+ public AbstractPDFExtensionElement(FONode parent) {
+ super(parent);
+ }
+
+ /** {@inheritDoc} */
+ public String getNamespaceURI() {
+ return PDFElementMapping.NAMESPACE;
+ }
+
+ /** {@inheritDoc} */
+ public String getNormalNamespacePrefix() {
+ return "pdf";
+ }
+
+ /**
+ * Returns the extension attachment.
+ * @return the extension attachment if one is created by the extension element, null otherwise.
+ * @see org.apache.fop.fo.FONode#getExtensionAttachment()
+ */
+ public ExtensionAttachment getExtensionAttachment() {
+ if (attachment == null) {
+ this.attachment = (PDFExtensionAttachment)instantiateExtensionAttachment();
+ }
+ return this.attachment;
+ }
+
+ /**
+ * Instantiates extension attachment object.
+ * @return extension attachment
+ */
+ protected abstract ExtensionAttachment instantiateExtensionAttachment();
+
+}
+
diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFElementMapping.java b/src/java/org/apache/fop/render/pdf/extensions/PDFElementMapping.java
new file mode 100644
index 000000000..55795f4fa
--- /dev/null
+++ b/src/java/org/apache/fop/render/pdf/extensions/PDFElementMapping.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.render.pdf.extensions;
+
+import org.apache.fop.fo.ElementMapping;
+import org.apache.fop.fo.FONode;
+
+/**
+ * This class provides the element mapping for the PDF-specific extensions.
+ */
+public class PDFElementMapping extends ElementMapping {
+
+ /** Namespace for the extension */
+ public static final String NAMESPACE = "http://xmlgraphics.apache.org/fop/extensions/pdf";
+
+ /** Main constructor */
+ public PDFElementMapping() {
+ this.namespaceURI = NAMESPACE;
+ }
+
+ /** {@inheritDoc} */
+ protected void initialize() {
+ if (foObjs == null) {
+ foObjs = new java.util.HashMap();
+ foObjs.put(PDFEmbeddedFileElement.ELEMENT, new PDFEmbeddedFileMaker());
+ }
+ }
+
+ static class PDFEmbeddedFileMaker extends ElementMapping.Maker {
+ public FONode make(FONode parent) {
+ return new PDFEmbeddedFileElement(parent);
+ }
+ }
+}
diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFEmbeddedFileElement.java b/src/java/org/apache/fop/render/pdf/extensions/PDFEmbeddedFileElement.java
new file mode 100644
index 000000000..d2c51aba4
--- /dev/null
+++ b/src/java/org/apache/fop/render/pdf/extensions/PDFEmbeddedFileElement.java
@@ -0,0 +1,105 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.render.pdf.extensions;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+
+import org.xml.sax.Attributes;
+import org.xml.sax.Locator;
+
+import org.apache.fop.apps.FOPException;
+import org.apache.fop.datatypes.URISpecification;
+import org.apache.fop.fo.Constants;
+import org.apache.fop.fo.FONode;
+import org.apache.fop.fo.PropertyList;
+import org.apache.fop.fo.extensions.ExtensionAttachment;
+
+/**
+ * Extension element for pdf:embedded-file.
+ */
+public class PDFEmbeddedFileElement extends AbstractPDFExtensionElement {
+
+ protected static final String ELEMENT = "embedded-file";
+
+ /**
+ * Main constructor
+ * @param parent parent FO node
+ */
+ protected PDFEmbeddedFileElement(FONode parent) {
+ super(parent);
+ }
+
+ /** {@inheritDoc} */
+ protected void startOfNode() throws FOPException {
+ super.startOfNode();
+ if (parent.getNameId() != Constants.FO_DECLARATIONS) {
+ invalidChildError(getLocator(), parent.getName(), getNamespaceURI(), getName(),
+ "rule.childOfDeclarations");
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void processNode(String elementName, Locator locator,
+ Attributes attlist, PropertyList propertyList)
+ throws FOPException {
+ PDFEmbeddedFileExtensionAttachment embeddedFile
+ = (PDFEmbeddedFileExtensionAttachment)getExtensionAttachment();
+ String desc = attlist.getValue("description");
+ if (desc != null && desc.length() > 0) {
+ embeddedFile.setDesc(desc);
+ }
+ String src = attlist.getValue("src");
+ src = URISpecification.getURL(src);
+ if (src != null && src.length() > 0) {
+ embeddedFile.setSrc(src);
+ } else {
+ missingPropertyError("src");
+ }
+ String filename = attlist.getValue("filename");
+ if (filename == null || filename.length() == 0) {
+ try {
+ URI uri = new URI(src);
+ String path = uri.getPath();
+ int idx = path.lastIndexOf('/');
+ if (idx > 0) {
+ filename = path.substring(idx + 1);
+ } else {
+ filename = path;
+ }
+ embeddedFile.setFilename(filename);
+ } catch (URISyntaxException e) {
+ //Filename could not be deduced from URI
+ missingPropertyError("name");
+ }
+ }
+ embeddedFile.setFilename(filename);
+ }
+
+ /** {@inheritDoc} */
+ public String getLocalName() {
+ return ELEMENT;
+ }
+
+ /** {@inheritDoc} */
+ protected ExtensionAttachment instantiateExtensionAttachment() {
+ return new PDFEmbeddedFileExtensionAttachment();
+ }
+}
diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFEmbeddedFileExtensionAttachment.java b/src/java/org/apache/fop/render/pdf/extensions/PDFEmbeddedFileExtensionAttachment.java
new file mode 100644
index 000000000..b4572663a
--- /dev/null
+++ b/src/java/org/apache/fop/render/pdf/extensions/PDFEmbeddedFileExtensionAttachment.java
@@ -0,0 +1,155 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.render.pdf.extensions;
+
+import org.xml.sax.ContentHandler;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.AttributesImpl;
+
+/**
+ * This is the pass-through value object for the PDF extension.
+ */
+public class PDFEmbeddedFileExtensionAttachment extends PDFExtensionAttachment {
+
+ /** element name */
+ protected static final String ELEMENT = "embedded-file";
+
+ /** name of file to be embedded */
+ private static final String ATT_NAME = "filename";
+
+ /** source of file to be embedded (URI) */
+ private static final String ATT_SRC = "src";
+
+ /** a description of the file to be embedded */
+ private static final String ATT_DESC = "desc";
+
+ /** filename attribute */
+ private String filename = null;
+
+ /** description attribute (optional) */
+ private String desc = null;
+
+ /** source name attribute */
+ private String src = null;
+
+ /**
+ * No-argument contructor.
+ */
+ public PDFEmbeddedFileExtensionAttachment() {
+ super();
+ }
+
+ /**
+ * Default constructor.
+ * @param filename the name of the file
+ * @param src the location of the file
+ * @param desc the description of the file
+ */
+ public PDFEmbeddedFileExtensionAttachment(String filename, String src, String desc) {
+ super();
+ this.filename = filename;
+ this.src = src;
+ this.desc = desc;
+ }
+
+ /**
+ * Returns the file name.
+ * @return the file name
+ */
+ public String getFilename() {
+ return filename;
+ }
+
+ /**
+ * Sets the file name.
+ * @param name The file name to set.
+ */
+ public void setFilename(String name) {
+ this.filename = name;
+ }
+
+ /**
+ * Returns the file description.
+ * @return the description
+ */
+ public String getDesc() {
+ return desc;
+ }
+
+ /**
+ * Sets the description of the file.
+ * @param desc the description to set
+ */
+ public void setDesc(String desc) {
+ this.desc = desc;
+ }
+
+ /**
+ * Returns the source URI of the file.
+ * @return the source URI
+ */
+ public String getSrc() {
+ return src;
+ }
+
+ /**
+ * Sets the source URI of the file.
+ * @param src the source URI
+ */
+ public void setSrc(String src) {
+ this.src = src;
+ }
+
+ /** {@inheritDoc} */
+ public String getCategory() {
+ return CATEGORY;
+ }
+
+ /** {@inheritDoc} */
+ public String toString() {
+ return "PDFEmbeddedFile(name=" + getFilename() + ", " + getSrc() + ")";
+ }
+
+ /**
+ * @return the element name
+ * @see org.apache.fop.render.ps.extensions.PDFExtensionAttachment#getElement()
+ */
+ protected String getElement() {
+ return ELEMENT;
+ }
+
+ /** {@inheritDoc} */
+ public void toSAX(ContentHandler handler) throws SAXException {
+ AttributesImpl atts = new AttributesImpl();
+ if (filename != null && filename.length() > 0) {
+ atts.addAttribute(null, ATT_NAME, ATT_NAME, "CDATA", filename);
+ }
+ if (src != null && src.length() > 0) {
+ atts.addAttribute(null, ATT_SRC, ATT_SRC, "CDATA", src);
+ }
+ if (desc != null && desc.length() > 0) {
+ atts.addAttribute(null, ATT_DESC, ATT_DESC, "CDATA", desc);
+ }
+ String element = getElement();
+ handler.startElement(CATEGORY, element, element, atts);
+ handler.endElement(CATEGORY, element, element);
+ }
+
+}
diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFExtensionAttachment.java b/src/java/org/apache/fop/render/pdf/extensions/PDFExtensionAttachment.java
new file mode 100644
index 000000000..a4f5b47b7
--- /dev/null
+++ b/src/java/org/apache/fop/render/pdf/extensions/PDFExtensionAttachment.java
@@ -0,0 +1,65 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.render.pdf.extensions;
+
+import org.apache.xmlgraphics.util.XMLizable;
+
+import org.apache.fop.fo.extensions.ExtensionAttachment;
+
+/**
+ * This is the pass-through value object for the PDF extension.
+ */
+public abstract class PDFExtensionAttachment implements ExtensionAttachment, XMLizable {
+
+ /** The category URI for this extension attachment. */
+ public static final String CATEGORY = "apache:fop:extensions:pdf";
+
+ /**
+ * Default constructor.
+ */
+ public PDFExtensionAttachment() {
+ //nop
+ }
+
+ /**
+ * @return the category URI
+ * @see org.apache.fop.fo.extensions.ExtensionAttachment#getCategory()
+ */
+ public String getCategory() {
+ return CATEGORY;
+ }
+
+ /** @return type name */
+ public String getType() {
+ String className = getClass().getName();
+ return className.substring(className.lastIndexOf('.') + 3);
+ }
+
+ /**
+ * @return a string representation of this object
+ * @see java.lang.Object#toString()
+ */
+ public String toString() {
+ return getType();
+ }
+
+ /** @return element */
+ protected abstract String getElement();
+}
diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFExtensionHandler.java b/src/java/org/apache/fop/render/pdf/extensions/PDFExtensionHandler.java
new file mode 100644
index 000000000..d285904a8
--- /dev/null
+++ b/src/java/org/apache/fop/render/pdf/extensions/PDFExtensionHandler.java
@@ -0,0 +1,97 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.render.pdf.extensions;
+
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.DefaultHandler;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.apache.fop.util.ContentHandlerFactory;
+import org.apache.fop.util.ContentHandlerFactory.ObjectBuiltListener;
+
+/**
+ * ContentHandler (parser) for restoring PDF extension objects from XML.
+ */
+public class PDFExtensionHandler extends DefaultHandler
+ implements ContentHandlerFactory.ObjectSource {
+
+ /** Logger instance */
+ protected static Log log = LogFactory.getLog(PDFExtensionHandler.class);
+
+ private Attributes lastAttributes;
+
+ private PDFExtensionAttachment returnedObject;
+ private ObjectBuiltListener listener;
+
+ /** {@inheritDoc} */
+ public void startElement(String uri, String localName, String qName, Attributes attributes)
+ throws SAXException {
+ boolean handled = false;
+ if (PDFExtensionAttachment.CATEGORY.equals(uri)) {
+ lastAttributes = attributes;
+ handled = false;
+ if (localName.equals(PDFEmbeddedFileExtensionAttachment.ELEMENT)) {
+ //handled in endElement
+ handled = true;
+ }
+ }
+ if (!handled) {
+ if (PDFExtensionAttachment.CATEGORY.equals(uri)) {
+ throw new SAXException("Unhandled element " + localName
+ + " in namespace: " + uri);
+ } else {
+ log.warn("Unhandled element " + localName
+ + " in namespace: " + uri);
+ }
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void endElement(String uri, String localName, String qName) throws SAXException {
+ if (PDFExtensionAttachment.CATEGORY.equals(uri)) {
+ if (PDFEmbeddedFileExtensionAttachment.ELEMENT.equals(localName)) {
+ String name = lastAttributes.getValue("name");
+ String src = lastAttributes.getValue("src");
+ String desc = lastAttributes.getValue("description");
+ this.returnedObject = new PDFEmbeddedFileExtensionAttachment(name, src, desc);
+ }
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void endDocument() throws SAXException {
+ if (listener != null) {
+ listener.notifyObjectBuilt(getObject());
+ }
+ }
+
+ /** {@inheritDoc} */
+ public Object getObject() {
+ return returnedObject;
+ }
+
+ /** {@inheritDoc} */
+ public void setObjectBuiltListener(ObjectBuiltListener listener) {
+ this.listener = listener;
+ }
+}
diff --git a/status.xml b/status.xml
index 660fc6002..146a65ad0 100644
--- a/status.xml
+++ b/status.xml
@@ -58,7 +58,10 @@
documents. Example: the fix of marks layering will be such a case when it's done.
-->
<release version="FOP Trunk" date="TBD">
- </release>
+ <action context="Renderers" dev="JM" type="add" fixes-bug="44460" due-to="Andrejus Chaliapinas">
+ Added support for PDF File Attachments (Embedded Files).
+ </action>
+ </release>
<release version="1.0" date="21 July 2010">
<action context="Renderers" dev="JM" type="fix">
AFP Output: Fixed positioning of Java2D-based images (when GOCA is enabled).