/* * 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.internal.export; import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileInputStream; import java.io.InputStream; import java.io.OutputStream; import java.nio.charset.Charset; import java.util.HashMap; import java.util.Map; import org.artofsolving.jodconverter.document.DocumentFormat; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.xwiki.officeimporter.converter.OfficeConverter; import org.xwiki.officeimporter.server.OfficeServer; import com.xpn.xwiki.XWikiContext; import com.xpn.xwiki.XWikiException; import com.xpn.xwiki.pdf.impl.PdfExportImpl; import com.xpn.xwiki.web.Utils; /** * Exports a wiki document as an office document. * <p> * Note: We extend {@link PdfExportImpl} for convenience. This is just a temporary solution. The entire export code * needs to be redesigned and moved in a separate module. * * @version $Id: 9d31f46cee668ab0abcd2ddf00aeba684b4aaef4 $ * @since 3.1M1 */ public class OfficeExporter extends PdfExportImpl { /** Logging helper object. */ private static final Logger LOGGER = LoggerFactory.getLogger(OfficeExporter.class); /** * The component used to access the office document converter. */ private OfficeServer officeServer = Utils.getComponent(OfficeServer.class); /** * @param extension the output file name extension, which specifies the export format (e.g. pdf, odt) * @return the export type matching the specified format, or {@code null} if the specified format is not supported */ public ExportType getExportType(String extension) { if (this.officeServer.getState() == OfficeServer.ServerState.CONNECTED) { DocumentFormat format = this.officeServer.getConverter().getFormatRegistry().getFormatByExtension(extension); if (format != null) { return new ExportType(format.getMediaType(), format.getExtension()); } } return null; } @Override protected void exportXHTML(String xhtml, OutputStream out, ExportType type, XWikiContext context) throws XWikiException { // We assume the office server is connected. The code calling this method should check the server state. exportXHTML(xhtml, out, this.officeServer.getConverter().getFormatRegistry().getFormatByExtension(type.getExtension()), context); } /** * Converts the given XHTML to the specified format and writes the result to the output stream. * * @param xhtml the XHTML to be exported * @param out where to write the export result * @param format the office format to convert the XHTML to * @param context the XWiki context, used to access the mapping between images embedded in the given XHTML and their * location on the file system * @throws XWikiException if the conversion fails */ private void exportXHTML(String xhtml, OutputStream out, DocumentFormat format, XWikiContext context) throws XWikiException { String html = applyXSLT(xhtml, getOfficeExportXSLT(context)); String inputFileName = "export_input.html"; String outputFileName = "export_output." + format.getExtension(); Map<String, InputStream> inputStreams = new HashMap<String, InputStream>(); // We assume that the HTML was generated using the XWiki encoding. Charset charset = Charset.forName(context.getWiki().getEncoding()); inputStreams.put(inputFileName, new ByteArrayInputStream(html.getBytes(charset))); addEmbeddedObjects(inputStreams, context); OfficeConverter officeConverter = this.officeServer.getConverter(); try { Map<String, byte[]> ouput = officeConverter.convert(inputStreams, inputFileName, outputFileName); out.write(ouput.values().iterator().next()); } catch (Exception e) { throw new XWikiException(XWikiException.MODULE_XWIKI_EXPORT, XWikiException.ERROR_XWIKI_APP_SEND_RESPONSE_EXCEPTION, String.format( "Exception while exporting to %s (%s).", format.getName(), format.getExtension()), e); } } /** * Adds the objects referenced in the exported XHTML to the map of input streams, allowing them to be embedded in * the output office document. * * @param inputStreams the map of input streams that is passed to the office converter * @param context the XWiki context, used to access the mapping between images embedded in the given XHTML and their * location on the file system */ private void addEmbeddedObjects(Map<String, InputStream> inputStreams, XWikiContext context) { @SuppressWarnings("unchecked") Map<String, File> fileMapping = (Map<String, File>) context.get("pdfexport-file-mapping"); for (File file : fileMapping.values()) { try { // Embedded files are placed in the same folder as the HTML input file during office conversion. inputStreams.put(file.getName(), new FileInputStream(file)); } catch (Exception e) { LOGGER.warn(String.format("Failed to embed %s in the office export.", file.getName()), e); } } } /** * Get the XSLT for preparing a (valid) XHTML to be converted to an office format. * * @param context the current request context * @return the content of the XSLT as a byte stream * @see PdfExportImpl#getXslt(String, String, XWikiContext) */ private InputStream getOfficeExportXSLT(XWikiContext context) { return getXslt("officeExportXSLT", "officeExport.xsl", context); } }