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: + * + * + * @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))); } }