diff --git a/xwiki-core/src/main/java/com/xpn/xwiki/pdf/impl/FileSystemURLFactory.java b/xwiki-core/src/main/java/com/xpn/xwiki/pdf/impl/FileSystemURLFactory.java
new file mode 100644
index 0000000..accda78
--- /dev/null
+++ b/xwiki-core/src/main/java/com/xpn/xwiki/pdf/impl/FileSystemURLFactory.java
@@ -0,0 +1,98 @@
+/*
+ * See the NOTICE file distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package com.xpn.xwiki.pdf.impl;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.net.URL;
+
+import com.xpn.xwiki.XWikiContext;
+import com.xpn.xwiki.doc.XWikiAttachment;
+import com.xpn.xwiki.doc.XWikiDocument;
+import com.xpn.xwiki.util.Util;
+import com.xpn.xwiki.web.XWikiServletURLFactory;
+
+/**
+ * Special URL Factory used during exports, which stores referenced attachments and resources on the filesystem, in a
+ * temporary folder, so that they can be included in the export result. The returned URLs point to these resources as
+ * {@code file://} links, and not as {@code http://} links.
+ *
+ * @version $Id$
+ */
+public class FileSystemURLFactory extends XWikiServletURLFactory
+{
+ public FileSystemURLFactory()
+ {
+ }
+
+ public URL createAttachmentURL(String filename, String web, String name, String action, String querystring,
+ String xwikidb, XWikiContext context)
+ {
+ try {
+ File tempdir = (File) context.get("pdfexportdir");
+ File file = new File(tempdir, web + "." + name + "." + filename);
+ if (!file.exists()) {
+ XWikiDocument doc = null;
+ doc = context.getWiki().getDocument(web + "." + name, context);
+ XWikiAttachment attachment = doc.getAttachment(filename);
+ byte[] data = new byte[0];
+ data = attachment.getContent(context);
+ FileOutputStream fos = new FileOutputStream(file);
+ fos.write(data);
+ fos.close();
+ }
+ return file.toURL();
+ } catch (Exception e) {
+ e.printStackTrace();
+ return super.createAttachmentURL(filename, web, name, action, null, xwikidb, context);
+ }
+ }
+
+ public URL createAttachmentRevisionURL(String filename, String web, String name, String revision, String xwikidb,
+ XWikiContext context)
+ {
+ try {
+ File tempdir = (File) context.get("pdfexportdir");
+ File file = new File(tempdir, web + "." + name + "." + filename);
+ if (!file.exists()) {
+ XWikiDocument doc = null;
+ doc = context.getWiki().getDocument(web + "." + name, context);
+ XWikiAttachment attachment = doc.getAttachment(filename).getAttachmentRevision(revision, context);
+ byte[] data = new byte[0];
+ data = attachment.getContent(context);
+ FileOutputStream fos = new FileOutputStream(file);
+ fos.write(data);
+ fos.close();
+ }
+ return file.toURL();
+ } catch (Exception e) {
+ e.printStackTrace();
+ return super.createAttachmentRevisionURL(filename, web, name, revision, xwikidb, context);
+ }
+ }
+
+ public String getURL(URL url, XWikiContext context)
+ {
+ if (url == null) {
+ return "";
+ }
+ return Util.escapeURL(url.toString());
+ }
+}
diff --git a/xwiki-core/src/main/java/com/xpn/xwiki/pdf/impl/PDFURIResolver.java b/xwiki-core/src/main/java/com/xpn/xwiki/pdf/impl/PDFURIResolver.java
new file mode 100644
index 0000000..463ddc8
--- /dev/null
+++ b/xwiki-core/src/main/java/com/xpn/xwiki/pdf/impl/PDFURIResolver.java
@@ -0,0 +1,102 @@
+/*
+ * See the NOTICE file distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package com.xpn.xwiki.pdf.impl;
+
+import java.io.ByteArrayInputStream;
+import java.io.UnsupportedEncodingException;
+import java.net.URLDecoder;
+import java.util.Map;
+
+import javax.xml.transform.Source;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.URIResolver;
+import javax.xml.transform.stream.StreamSource;
+
+import org.xwiki.model.EntityType;
+import org.xwiki.model.reference.AttachmentReference;
+import org.xwiki.model.reference.DocumentReference;
+
+import com.xpn.xwiki.XWikiContext;
+import com.xpn.xwiki.doc.XWikiAttachment;
+import com.xpn.xwiki.doc.XWikiDocument;
+
+/**
+ * Resolves URIs sent by Apache FOP to embed images in the exported PDF. The strategy is the following:
+ *
+ * - When an attachment is rendered during the export (specifically when {@code pdf.vm} is rendered), the
+ * {@link PdfURLFactory} is called and it saves the Attachment Entity Reference in a map in the XWiki Context
+ * - When Apache FOP embeds an image it calls this URI Resolver and we try to locate the Attachment Entity
+ * Reference from that map and return the attachment stream.
+ * - Attachment links do not call the Resolver and are thus exported correctly using a full URL to the XWiki
+ * server
+ *
+ *
+ * @version $Id$
+ */
+public class PDFURIResolver implements URIResolver
+{
+ /**
+ * @see #PDFURIResolver(com.xpn.xwiki.XWikiContext)
+ */
+ private Map attachmentMap;
+
+ /**
+ * @see #PDFURIResolver(com.xpn.xwiki.XWikiContext)
+ */
+ private XWikiContext context;
+
+ /**
+ * @param context the XWiki Context from where we try to find the attachment map saved in the {@link PdfURLFactory}
+ * earlier on
+ */
+ public PDFURIResolver(XWikiContext context)
+ {
+ this.attachmentMap = (Map) context.get(PdfURLFactory.PDF_EXPORT_CONTEXT_KEY);
+ this.context = context;
+ }
+
+ public Source resolve(String href, String base) throws TransformerException
+ {
+ if (this.attachmentMap != null) {
+ try {
+ AttachmentReference reference = this.attachmentMap.get(URLDecoder.decode(href, "UTF-8"));
+ if (reference != null) {
+ try {
+ DocumentReference documentReference =
+ new DocumentReference(reference.extractReference(EntityType.DOCUMENT));
+ XWikiDocument xdoc = this.context.getWiki().getDocument(documentReference, this.context);
+ // TODO: handle revisions
+ XWikiAttachment attachment = xdoc.getAttachment(
+ reference.extractReference(EntityType.ATTACHMENT).getName());
+ return new StreamSource(new ByteArrayInputStream(attachment.getContent(this.context)));
+ } catch (Exception e) {
+ throw new TransformerException(
+ String.format("Failed to resolve export URI [%s]", href), e);
+ }
+ }
+ } catch (UnsupportedEncodingException e) {
+ throw new TransformerException("Missing UTF8 encoding!", e);
+ }
+ }
+
+ // Defaults to the default URI Resolver in FO
+ return null;
+ }
+}
diff --git a/xwiki-core/src/main/java/com/xpn/xwiki/pdf/impl/PdfExportImpl.java b/xwiki-core/src/main/java/com/xpn/xwiki/pdf/impl/PdfExportImpl.java
index c52d7ee..0e56a4e 100644
--- a/xwiki-core/src/main/java/com/xpn/xwiki/pdf/impl/PdfExportImpl.java
+++ b/xwiki-core/src/main/java/com/xpn/xwiki/pdf/impl/PdfExportImpl.java
@@ -182,10 +182,10 @@ public class PdfExportImpl implements PdfExport
log.debug("XSL-FO source: " + xmlfo);
}
- exportXMLFO(xmlfo, out, type);
+ exportXMLFO(xmlfo, out, type, context);
}
- public void exportXMLFO(String xmlfo, OutputStream out, int type) throws XWikiException
+ public void exportXMLFO(String xmlfo, OutputStream out, int type, XWikiContext context) throws XWikiException
{
// XSL Transformation to XML-FO
@@ -193,6 +193,9 @@ public class PdfExportImpl implements PdfExport
FOUserAgent foUserAgent = fopFactory.newFOUserAgent();
// configure foUserAgent as desired
+ // Use a custom URI Resolver to handle embedding images in the exported PDF.
+ foUserAgent.setURIResolver(new PDFURIResolver(context));
+
// Construct fop with desired output format
Fop fop =
fopFactory.newFop((type == PdfExportImpl.RTF) ? MimeConstants.MIME_RTF : MimeConstants.MIME_PDF,
@@ -462,7 +465,7 @@ public class PdfExportImpl implements PdfExport
content = Util.getFileContent(new File(inputfile));
// XML-FO2 PDF
FileOutputStream fos = new FileOutputStream(new File(outputfile));
- pdf.exportXMLFO(content, fos, PdfExportImpl.PDF);
+ pdf.exportXMLFO(content, fos, PdfExportImpl.PDF, context);
fos.close();
} else if (param.equals("-html2pdf")) {
inputfile = argv[1];
diff --git a/xwiki-core/src/main/java/com/xpn/xwiki/pdf/impl/PdfURLFactory.java b/xwiki-core/src/main/java/com/xpn/xwiki/pdf/impl/PdfURLFactory.java
index f4e49b5..e54ceb7 100644
--- a/xwiki-core/src/main/java/com/xpn/xwiki/pdf/impl/PdfURLFactory.java
+++ b/xwiki-core/src/main/java/com/xpn/xwiki/pdf/impl/PdfURLFactory.java
@@ -20,73 +20,128 @@
*/
package com.xpn.xwiki.pdf.impl;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.xwiki.model.reference.AttachmentReference;
+import org.xwiki.model.reference.DocumentReference;
+
import com.xpn.xwiki.XWikiContext;
-import com.xpn.xwiki.doc.XWikiAttachment;
-import com.xpn.xwiki.doc.XWikiDocument;
-import com.xpn.xwiki.util.Util;
+import com.xpn.xwiki.web.XWikiDefaultURLFactory;
import com.xpn.xwiki.web.XWikiServletURLFactory;
-import java.io.File;
-import java.io.FileOutputStream;
-import java.net.URL;
-
-public class PdfURLFactory extends XWikiServletURLFactory
+public class PdfURLFactory extends XWikiDefaultURLFactory
{
- public PdfURLFactory()
+ private XWikiServletURLFactory servletURLFactory = new XWikiServletURLFactory();
+
+ private FileSystemURLFactory fileSystemURLFactory = new FileSystemURLFactory();
+
+ public void init(XWikiContext context)
{
+ this.servletURLFactory.init(context);
+ this.fileSystemURLFactory.init(context);
}
- public URL createAttachmentURL(String filename, String web, String name, String action, String querystring,
- String xwikidb, XWikiContext context)
+ /**
+ * Key used to save image attachment data.
+ * @see #saveAttachmentReference(java.net.URL, String, String, String, String, com.xpn.xwiki.XWikiContext)
+ */
+ static final String PDF_EXPORT_CONTEXT_KEY = "pdfExportImageURLMap";
+
+ public URL createAttachmentURL(String filename, String space, String name, String action, String querystring,
+ String wiki, XWikiContext context)
{
- try {
- File tempdir = (File) context.get("pdfexportdir");
- File file = new File(tempdir, web + "." + name + "." + filename);
- if (!file.exists()) {
- XWikiDocument doc = null;
- doc = context.getWiki().getDocument(web + "." + name, context);
- XWikiAttachment attachment = doc.getAttachment(filename);
- byte[] data = new byte[0];
- data = attachment.getContent(context);
- FileOutputStream fos = new FileOutputStream(file);
- fos.write(data);
- fos.close();
- }
- return file.toURL();
- } catch (Exception e) {
- e.printStackTrace();
- return super.createAttachmentURL(filename, web, name, action, null, xwikidb, context);
- }
+ URL url = this.servletURLFactory.createAttachmentURL(filename, space, name, action, querystring, wiki, context);
+ saveAttachmentReference(url, wiki, space, name, filename, context);
+ return url;
}
- public URL createAttachmentRevisionURL(String filename, String web, String name, String revision, String xwikidb,
+ @Override
+ public URL createAttachmentRevisionURL(String filename, String space, String name, String revision, String wiki,
XWikiContext context)
{
- try {
- File tempdir = (File) context.get("pdfexportdir");
- File file = new File(tempdir, web + "." + name + "." + filename);
- if (!file.exists()) {
- XWikiDocument doc = null;
- doc = context.getWiki().getDocument(web + "." + name, context);
- XWikiAttachment attachment = doc.getAttachment(filename).getAttachmentRevision(revision, context);
- byte[] data = new byte[0];
- data = attachment.getContent(context);
- FileOutputStream fos = new FileOutputStream(file);
- fos.write(data);
- fos.close();
- }
- return file.toURL();
- } catch (Exception e) {
- e.printStackTrace();
- return super.createAttachmentRevisionURL(filename, web, name, revision, xwikidb, context);
- }
+ URL url = this.servletURLFactory.createAttachmentURL(filename, space, name, revision, wiki, context);
+ saveAttachmentReference(url, wiki, space, name, filename, context);
+ return url;
}
- public String getURL(URL url, XWikiContext context)
+ public URL createAttachmentRevisionURL(String filename, String web, String name, String revision,
+ String querystring, String xwikidb, XWikiContext context)
+ {
+ URL url = this.servletURLFactory.createAttachmentRevisionURL(filename, web, name, revision, querystring,
+ xwikidb, context);
+ saveAttachmentReference(url, xwikidb, web, name, filename, context);
+ return url;
+ }
+
+ public URL createAttachmentRevisionURL(String filename, String web, String name, String revision, long recycleId,
+ String querystring, String xwikidb, XWikiContext context)
+ {
+ URL url = this.servletURLFactory.createAttachmentRevisionURL(filename, web, name, revision, recycleId,
+ querystring, xwikidb, context);
+ saveAttachmentReference(url, xwikidb, web, name, filename, context);
+ return url;
+ }
+
+ public URL createURL(String web, String name, String action, boolean redirect, XWikiContext context)
+ {
+ return this.fileSystemURLFactory.createURL(web, name, action, redirect, context);
+ }
+
+ public URL createURL(String web, String name, String action, String querystring, String anchor, String xwikidb,
+ XWikiContext context)
+ {
+ return this.fileSystemURLFactory.createURL(web, name, action, querystring, anchor, xwikidb, context);
+ }
+
+ public URL createExternalURL(String web, String name, String action, String querystring, String anchor,
+ String xwikidb, XWikiContext context)
+ {
+ return this.fileSystemURLFactory.createExternalURL(web, name, action, querystring, anchor, xwikidb, context);
+ }
+
+ public URL createSkinURL(String filename, String skin, XWikiContext context)
+ {
+ return this.fileSystemURLFactory.createSkinURL(filename, skin, context);
+ }
+
+ public URL createSkinURL(String filename, String web, String name, String xwikidb, XWikiContext context)
+ {
+ return this.fileSystemURLFactory.createSkinURL(filename, web, name, xwikidb, context);
+ }
+
+ public URL createResourceURL(String filename, boolean forceSkinAction, XWikiContext context)
+ {
+ return this.fileSystemURLFactory.createResourceURL(filename, forceSkinAction, context);
+ }
+
+ public URL getServerURL(XWikiContext context) throws MalformedURLException
+ {
+ return this.fileSystemURLFactory.getServerURL(context);
+ }
+
+ /**
+ * @param url the URL to save in the attachment map
+ * @param wiki the wiki where the attachment is located
+ * @param space the space where the attachment is located
+ * @param page the page where the attachment is located
+ * @param fileName the name of the attachment file
+ * @param context the XWiki Context where to find the attachment map that we add to
+ * @see PDFURIResolver
+ */
+ private void saveAttachmentReference(URL url, String wiki, String space, String page, String fileName,
+ XWikiContext context)
{
- if (url == null) {
- return "";
+ // Save Entity Reference pointed to by this URL in the Context so that it can be used at export time in the
+ // PDF URIResolver code.
+ Map attachmentMap =
+ (Map) context.get(PDF_EXPORT_CONTEXT_KEY);
+ if (attachmentMap == null) {
+ attachmentMap = new HashMap();
+ context.put(PDF_EXPORT_CONTEXT_KEY, attachmentMap);
}
- return Util.escapeURL(url.toString());
+ attachmentMap.put(url.toString(), new AttachmentReference(fileName, new DocumentReference(wiki, space, page)));
}
}