All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.pptx4j.convert.out.svginhtml.SvgExporter Maven / Gradle / Ivy

Go to download

docx4j is a library which helps you to work with the Office Open XML file format as used in docx documents, pptx presentations, and xlsx spreadsheets.

There is a newer version: 6.1.2
Show newest version
package org.pptx4j.convert.out.svginhtml;

import java.io.ByteArrayOutputStream;
import java.io.StringReader;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Templates;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;

import org.apache.log4j.Logger;
import org.docx4j.XmlUtils;
import org.docx4j.convert.out.AbstractConversionSettings;
import org.docx4j.convert.out.html.AbstractHtmlExporter;
import org.docx4j.convert.out.html.HTMLConversionImageHandler;
import org.docx4j.convert.out.html.AbstractHtmlExporter.HtmlSettings;
import org.docx4j.dml.CTTextCharacterProperties;
import org.docx4j.dml.CTTextParagraphProperties;
import org.docx4j.dml.CTTransform2D;
import org.docx4j.model.styles.StyleTree;
import org.docx4j.model.styles.Tree;
import org.docx4j.model.styles.StyleTree.AugmentedStyle;
import org.docx4j.openpackaging.exceptions.Docx4JException;
import org.docx4j.openpackaging.exceptions.InvalidFormatException;
import org.docx4j.openpackaging.packages.PresentationMLPackage;
import org.docx4j.openpackaging.parts.PresentationML.SlidePart;
import org.docx4j.wml.PPr;
import org.docx4j.wml.RPr;
import org.docx4j.wml.Style;
import org.plutext.jaxb.svg11.Line;
import org.plutext.jaxb.svg11.ObjectFactory;
import org.plutext.jaxb.svg11.Svg;
import org.pptx4j.Box;
import org.pptx4j.Point;
import org.pptx4j.jaxb.Context;
import org.pptx4j.model.ResolvedLayout;
import org.pptx4j.model.TextStyles;
import org.pptx4j.pml.CxnSp;
import org.pptx4j.pml.GroupShape;
import org.w3c.dom.Document;
import org.w3c.dom.DocumentFragment;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.Text;
import org.w3c.dom.traversal.NodeIterator;
import org.xml.sax.InputSource;

public class SvgExporter {
	
	public static class SvgSettings extends AbstractConversionSettings {
		public static final String IMAGE_TARGET_URI = "imageTargetUri";
		
		public void setImageTargetUri(String imageTargetUri) {
			settings.put(IMAGE_TARGET_URI, imageTargetUri);
		}
		
		public String getImageTargetUri() {
			return (String)settings.get(IMAGE_TARGET_URI);
		}
	}
	
	// NB: file suffix must end with .xhtml in order to see the SVG in a browser
	
	protected static Logger log = Logger.getLogger(SvgExporter.class);	

	public static JAXBContext jcSVG;	
    static ObjectFactory oFactory;
	static Templates xslt;			
	static {
		
		try {
			jcSVG = JAXBContext.newInstance("org.plutext.jaxb.svg11");
			oFactory = new ObjectFactory();

			Source xsltSource = new StreamSource(
						org.docx4j.utils.ResourceUtils.getResource(
								"org/pptx4j/convert/out/svginhtml/pptx2svginhtml.xslt"));
			xslt = XmlUtils.getTransformerTemplate(xsltSource);
		} catch (Exception e) {
			e.printStackTrace();
		} 
	}
	
	private static String imageDirPath;
	public static void setImageDirPath(String _imageDirPath) {
		imageDirPath = _imageDirPath;
	}
	
	
	/**
	 * Create an HTML (with SVG) page representing the slide.
	 * @param presentationMLPackage
	 * @param slide
	 * @return
	 * @throws Exception
	 */
	public static String svg(PresentationMLPackage presentationMLPackage,
			SlidePart slide) throws Exception {
		return svg(presentationMLPackage, slide, null);
	}
	
	/**
	 * Create an HTML (with SVG) page representing the slide.
	 * @param presentationMLPackage
	 * @param slide
	 * @param settings
	 * @return
	 * @throws Exception
	 */
	public static String svg(PresentationMLPackage presentationMLPackage,
			SlidePart slide, SvgSettings settings) throws Exception {
		
    	ResolvedLayout rl = ((SlidePart)slide).getResolvedLayout();	

//    	System.out.println( XmlUtils.marshaltoString(rl.getShapeTree(), false, true, Context.jcPML,
//		"http://schemas.openxmlformats.org/presentationml/2006/main", "spTree", GroupShape.class) );
    	
    	return SvgExporter.svg(presentationMLPackage, rl, settings);
	}
	
	/**
	 * @param presentationMLPackage
	 * @param layout
	 * @return
	 * @throws Exception
	 */
	private static String svg(PresentationMLPackage presentationMLPackage,
			ResolvedLayout layout, SvgSettings settings) throws Exception {
	
		ByteArrayOutputStream intermediate = new ByteArrayOutputStream();
		Result intermediateResult =  new StreamResult( intermediate );
			
		svg(presentationMLPackage, layout, intermediateResult, settings);
			
		return intermediate.toString("UTF-8");
		//log.info(svg);
	}
	
	
	private static void svg(PresentationMLPackage presentationMLPackage,
			ResolvedLayout layout, javax.xml.transform.Result result,
			SvgSettings settings) throws Exception {
    			
		org.w3c.dom.Document doc = XmlUtils.marshaltoW3CDomDocument(
				layout.getShapeTree(),
				Context.jcPML,
				"http://schemas.openxmlformats.org/presentationml/2006/main", "spTree", GroupShape.class); 	

		if (settings == null) {
			settings = new SvgSettings();
		}
		settings.setWmlPackage(presentationMLPackage);
		settings.getSettings().put("resolvedLayout", layout);
		if ((settings.getImageDirPath() == null) && (imageDirPath != null)) {
			settings.setImageDirPath(imageDirPath);
		}
		boolean privateImageHandler = false;
		if (settings.getImageHandler() == null) {
			settings.setImageHandler(
				new HTMLConversionImageHandler(settings.getImageDirPath(), 
						   settings.getImageTargetUri(), 
						   settings.isImageIncludeUUID()));
			privateImageHandler = true;
		}
		org.docx4j.XmlUtils.transform(doc, xslt, settings.getSettings(), result);
		if (privateImageHandler) {
			//remove a locally created imageHandler in case the SvgSettings get reused
			settings.getSettings().remove(HtmlSettings.IMAGE_HANDLER);
		}
	}

	public static boolean isDebugEnabled() {
		
		return log.isDebugEnabled();
	}
	
	public static DocumentFragment notImplemented(NodeIterator nodes, String message) {

		Node n = nodes.nextNode();
		log.warn("NOT IMPLEMENTED: support for "+ n.getNodeName() + "\n" + message);
		
		if (log.isDebugEnabled() ) {
			
			if (message==null) message="";
			
			log.debug( XmlUtils.w3CDomNodeToString(n)  );

			// Return something which will show up in the output
			return message("NOT IMPLEMENTED: support for " + n.getNodeName() + " - " + message);
		} else {
			
			// Put it in a comment node instead?
			
			return null;
		}
	}
	
	public static DocumentFragment message(String message) {
		
		if (!log.isDebugEnabled()) return null;

		String html = "
" + message + "
"; javax.xml.parsers.DocumentBuilderFactory dbf = DocumentBuilderFactory .newInstance(); dbf.setNamespaceAware(true); StringReader reader = new StringReader(html); InputSource inputSource = new InputSource(reader); Document doc = null; try { doc = dbf.newDocumentBuilder().parse(inputSource); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } reader.close(); DocumentFragment docfrag = doc.createDocumentFragment(); docfrag.appendChild(doc.getDocumentElement()); return docfrag; } public static DocumentFragment createBlockForP( PresentationMLPackage pmlPackage, ResolvedLayout rl, String lvl, String cNvPrName, String phType, NodeIterator childResults, NodeIterator lvlNpPr ) { StyleTree styleTree = null; try { styleTree = pmlPackage.getStyleTree(); } catch (InvalidFormatException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } log.debug("lvl:" +lvl); int level; if (lvl.equals("NaN")) { level = 1; } else { level = Integer.parseInt(lvl); } String pStyleVal; System.out.println("cNvPrName: " + cNvPrName + "; " + "phType: " + phType ); if (cNvPrName.toLowerCase().indexOf("subtitle")>-1 || phType.toLowerCase().indexOf("subtitle")>-1) { // Subtitle on first page in default layout is styled as a Body. pStyleVal = "Lvl" + level + "Master" + rl.getMasterNumber() + "Body"; } else if (cNvPrName.toLowerCase().indexOf("title")>-1 || phType.toLowerCase().indexOf("title")>-1) { pStyleVal = "Lvl" + level + "Master" + rl.getMasterNumber() + "Title"; } else { // eg cNvPrName: TextBox 2; phType: pStyleVal = "Lvl" + level + "Master" + rl.getMasterNumber() + "Other"; } System.out.println("--> " + pStyleVal ); try { // Create a DOM builder and parse the fragment DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); Document document = factory.newDocumentBuilder().newDocument(); //log.info("Document: " + document.getClass().getName() ); Node xhtmlP = document.createElement("p"); document.appendChild(xhtmlP); // Set @class log.debug(pStyleVal); Tree pTree = styleTree.getParagraphStylesTree(); org.docx4j.model.styles.Node asn = pTree.get(pStyleVal); ((Element)xhtmlP).setAttribute("class", StyleTree.getHtmlClassAttributeValue(pTree, asn) ); StringBuffer inlineStyle = new StringBuffer(); // Do we have CTTextParagraphProperties // // Convert it to a WordML pPr CTTextParagraphProperties lvlPPr = unmarshalFormatting(lvlNpPr); if (lvlPPr!=null) { log.debug("We have lvlPPr"); log.debug( XmlUtils.marshaltoString(lvlPPr, true, true, Context.jcPML, "FIXME", "lvl1pPr", CTTextParagraphProperties.class) ); PPr pPr = TextStyles.getWmlPPr(lvlPPr); if (pPr!=null) { AbstractHtmlExporter.createCss(pmlPackage, pPr, inlineStyle, false); } // TODO RPR } // Without this, top-margin is too large in Webkit (Midor). // Not tested elsewhere... inlineStyle.append("margin-left:3px; margin-top:3px;"); if (!inlineStyle.toString().equals("") ) { ((Element)xhtmlP).setAttribute("style", inlineStyle.toString() ); } // Our fo:block wraps whatever result tree fragment // our style sheet produced when it applied-templates // to the child nodes // init Node n = childResults.nextNode(); do { // getNumberXmlNode creates a span node, which is empty // if there is no numbering. // Let's get rid of any such . // What we actually get is a document node if (n.getNodeType()==Node.DOCUMENT_NODE) { log.debug("handling DOCUMENT_NODE"); // Do just enough of the handling here NodeList nodes = n.getChildNodes(); if (nodes != null) { for (int i=0; i "); } else { XmlUtils.treeCopy( (Node)nodes.item(i), xhtmlP ); } } } } else { // log.info("Node we are importing: " + n.getClass().getName() ); // foBlockElement.appendChild( // document.importNode(n, true) ); /* * Node we'd like to import is of type org.apache.xml.dtm.ref.DTMNodeProxy * which causes * org.w3c.dom.DOMException: NOT_SUPPORTED_ERR: The implementation does not support the requested type of object or operation. * * See http://osdir.com/ml/text.xml.xerces-j.devel/2004-04/msg00066.html * * So instead of importNode, use */ XmlUtils.treeCopy( n, xhtmlP ); } // next n = childResults.nextNode(); } while ( n !=null ); DocumentFragment docfrag = document.createDocumentFragment(); docfrag.appendChild(document.getDocumentElement()); return docfrag; } catch (Exception e) { e.printStackTrace(); System.out.println(e.toString() ); log.error(e); } return null; } private static CTTextParagraphProperties unmarshalFormatting(NodeIterator lvlNpPr ) { // Get the pPr node as a JAXB object, // so we can read it using our standard // methods. Its a bit sad that we // can't just adorn our DOM tree with the // original JAXB objects? try { //CTTextListStyle lstStyle = null; CTTextParagraphProperties pPr = null; if (lvlNpPr!=null) { Node n = lvlNpPr.nextNode(); log.debug(n.getClass().getName()); String str = XmlUtils.w3CDomNodeToString(n); //log.debug("'" + str + "'"); // Convert to String first ... // unmarshalling the node directly doesn't work as expected // (see comment in XmlUtils) // if (n!=null) { // return (CTTextParagraphProperties)XmlUtils.unmarshal(n, Context.jcPML, // CTTextParagraphProperties.class); // } if (!str.equals("")) { return (CTTextParagraphProperties)XmlUtils.unmarshalString(str, Context.jcPML, CTTextParagraphProperties.class); } } } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } return null; } public static DocumentFragment createBlockForR( PresentationMLPackage pmlPackage, NodeIterator rPrNodeIt, NodeIterator childResults) { DocumentFragment docfrag = null; Document d = null; Element span = null; try { // Create a DOM builder and parse the fragment DocumentBuilderFactory factory = DocumentBuilderFactory .newInstance(); d = factory.newDocumentBuilder().newDocument(); span = d.createElement("span"); d.appendChild(span); CTTextCharacterProperties textCharProps = (CTTextCharacterProperties) nodeToObjectModel( rPrNodeIt.nextNode(), CTTextCharacterProperties.class); RPr rPr = TextStyles.getWmlRPr(textCharProps); // Does our rPr contain anything else? StringBuffer inlineStyle = new StringBuffer(); AbstractHtmlExporter.createCss(pmlPackage, rPr, inlineStyle); if (!inlineStyle.toString().equals("")) { span.setAttribute("style", inlineStyle.toString()); } Node n = childResults.nextNode(); XmlUtils.treeCopy(n, span); } catch (Exception e) { e.printStackTrace(); log.error(e); // If something went wrong with the formatting, // still try to display the text! Node n = childResults.nextNode(); XmlUtils.treeCopy(n, span); } // Machinery docfrag = d.createDocumentFragment(); docfrag.appendChild(d.getDocumentElement()); return docfrag; } // public static CTTextListStyle unmarshalFormatting(NodeIterator lstStyleNodeIt ) { // // // Get the pPr node as a JAXB object, // // so we can read it using our standard // // methods. Its a bit sad that we // // can't just adorn our DOM tree with the // // original JAXB objects? // try { // CTTextListStyle lstStyle = null; // if (lstStyleNodeIt!=null) { // Node n = lstStyleNodeIt.nextNode(); // if (n!=null) { // return (CTTextListStyle)XmlUtils.unmarshal(n, Context.jcPML, CTTextListStyle.class); // } // } // } catch (Exception e) { // // TODO Auto-generated catch block // e.printStackTrace(); // } // return null; // // } public static String getCssForStyles(PresentationMLPackage pmlPackage) { StringBuffer result = new StringBuffer(); StyleTree styleTree=null; try { styleTree = pmlPackage.getStyleTree(); } catch (InvalidFormatException e) { // TODO Auto-generated catch block e.printStackTrace(); } // Second iteration - paragraph level pPr *and rPr* result.append("\n /* PARAGRAPH STYLES */ \n"); Tree pTree = styleTree.getParagraphStylesTree(); for (org.docx4j.model.styles.Node n : pTree.toList() ) { Style s = n.getData().getStyle(); result.append( "."+ s.getStyleId() + " {display:block;" ); if (s.getPPr()==null) { log.debug("null pPr for style " + s.getStyleId()); } else { AbstractHtmlExporter.createCss(pmlPackage, s.getPPr(), result, false ); } if (s.getRPr()==null) { log.debug("null rPr for style " + s.getStyleId()); } else { AbstractHtmlExporter.createCss(pmlPackage, s.getRPr(), result); } result.append( "}\n" ); } if (log.isDebugEnabled()) { return result.toString(); } else { String debug = result.toString(); return debug; } } public static DocumentFragment shapeToSVG( PresentationMLPackage pmlPackage, NodeIterator shapeIt ) { DocumentFragment docfrag=null; Document d=null; try { Object shape = null; if (shapeIt!=null) { Node n = shapeIt.nextNode(); if (n==null) { d=makeErr( "[null node?!]" ); } else { log.debug("Handling " + n.getNodeName()); if (n.getNodeName().equals("p:cxnSp") ) { shape = nodeToObjectModel(n, CxnSp.class); d = CxnSpToSVG( (CxnSp)shape); } else { log.info("** TODO " + n.getNodeName() ); d=makeErr( "[" + n.getNodeName() + "]" ); } } } } catch (Exception e) { log.error(e); d=makeErr(e.getMessage() ); } // Machinery docfrag = d.createDocumentFragment(); docfrag.appendChild(d.getDocumentElement()); return docfrag; } /** * Connection (line) */ public static Document CxnSpToSVG(CxnSp cxnSp) { // Geometrical transforms CTTransform2D xfrm = cxnSp.getSpPr().getXfrm(); Box b = new Box(xfrm.getOff().getX(), xfrm.getOff().getY(), xfrm.getExt().getCx(), xfrm.getExt().getCy() ); if (xfrm.getRot()!=0) { b.rotate(xfrm.getRot()); } if (xfrm.isFlipH() ) { b.flipH(); } if (xfrm.isFlipV() ) { b.flipV(); } // Convert from EMU to pixels b.toPixels(); // Wrap in a div positioning it on the page Document document = createDocument(); Element xhtmlDiv = document.createElement("div"); // Firefox needs the following; Chrome doesn't xhtmlDiv.setAttribute("style", "position: absolute; width:100%; height:100%; left:0px; top:0px;"); Node n = document.appendChild(xhtmlDiv); // Convert the object itself to SVG Svg svg = oFactory.createSvg(); Line line = oFactory.createLine(); svg.getSVGDescriptionClassOrSVGAnimationClassOrSVGStructureClass().add(line); line.setX1(b.getOffset().getXAsString() ); line.setY1(b.getOffset().getYAsString() ); Point otherEnd = b.getOtherCorner(); line.setX2( otherEnd.getXAsString() ); line.setY2( otherEnd.getYAsString() ); line.setStyle("stroke:rgb(99,99,99)"); // You can't see the line in Midori, unless you specify the color. // width eg stroke-width:2 is optional Document d2 = XmlUtils.marshaltoW3CDomDocument(svg, jcSVG); XmlUtils.treeCopy(d2, n); return document; } private static Document makeErr(String msg) { Document d=null; DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); try { d = factory.newDocumentBuilder().newDocument(); } catch (ParserConfigurationException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } Element span = d.createElement("span"); span.setAttribute("style", "color:red;"); d.appendChild(span); Text err = d.createTextNode( msg ); span.appendChild(err); return d; } public static Object nodeToObjectModel(Node n, Class declaredType) throws Docx4JException { if (n==null) { throw new Docx4JException("null input"); } // if (log.isDebugEnabled() ) { // System.out.println("IN: " + XmlUtils.w3CDomNodeToString(n)); // } Object jaxb=null; try { jaxb = XmlUtils.unmarshal(n, Context.jcPML, declaredType); } catch (JAXBException e1) { throw new Docx4JException("Couldn't unmarshall " + XmlUtils.w3CDomNodeToString(n), e1); } try { if (jaxb instanceof JAXBElement ) { JAXBElement jb = (JAXBElement)jaxb; if (jb.getDeclaredType().getName().equals(declaredType.getName() )) { return jb.getValue(); } else { log.error("UNEXPECTED " + XmlUtils.JAXBElementDebug(jb) ); throw new Docx4JException("Expected " + declaredType.getName() + " but got " + XmlUtils.JAXBElementDebug(jb) ); } } else if (jaxb.getClass().getName().equals(declaredType.getName() )) { return jaxb; } else { log.error( jaxb.getClass().getName() ); throw new Docx4JException("Expected " + declaredType.getName() + " but got " + jaxb.getClass().getName() ); } } catch (ClassCastException e) { throw new Docx4JException("Expected " + declaredType.getName() + " but got " + jaxb.getClass().getName(), e ); } } public static Document createDocument() { DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); Document document=null; try { document = factory.newDocumentBuilder().newDocument(); } catch (ParserConfigurationException e) { e.printStackTrace(); } return document; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy