If you have a customized xhtml variant, like evernote ENML format and you want to render it with Flying Saucer R8 you must first make sure that the extra element (in ENML case, en-media
) is defined as a block-level element. To do that you create your own NamespaceHandler and you make sure that you return "display: block;"
for en-media
in the implementation of NamespaceHandler.getNonCssStyling
. (See ENMLNamespaceHandler.java
below)
....
XHTMLPanel panel = new XHTMLPanel();
panel.setDocument(doc,"",new ENMLNamespaceHandler(new XhtmlNamespaceHandler()));
....
class ENMLNamespaceHandler implements NamespaceHandler {
....
public String getNonCssStyling(Element e) {
String toReturn = delegate.getNonCssStyling(e);
if ("en-media".equalsIgnoreCase(e.getNodeName())) {
toReturn = "display: block;";
}
return toReturn;
}
....
}
With that you ensure that xhtmlrenderer will call ReplacedElementFactory.createReplacedElement
for en-media
. Now you must supply a ReplacedElementFactory that it’s able to process en-media
. Usually the implementation of the createReplacedElement()
involves creating a Swing JComponent, wrapping it in a SwingReplacedElement
and adding it to the LayoutContext.getCanvas()
.
....
ReplacedElementFactory cef = new ENMLReplacedElementFactory(new SwingReplacedElementFactory());
XHTMLPanel panel = new XHTMLPanel();
panel.getSharedContext().setReplacedElementFactory(cef);
panel.setDocument(doc,"",new ENMLNamespaceHandler(new XhtmlNamespaceHandler()));
....
public class ENMLReplacedElementFactory implements ReplacedElementFactory {
...
public ReplacedElement createReplacedElement(LayoutContext context, BlockBox box,
UserAgentCallback uac, int cssWidth, int cssHeight)
{
if ("en-media".equals(box.getElement().getNodeName())) {
JTextArea cc = new JTextArea();
cc.setText("Missing implementation for en-media");
cc.setSize(cc.getPreferredSize());
context.getCanvas().add(cc);
ReplacedElement toReturn = new SwingReplacedElement(cc) {
public boolean isRequiresInteractivePaint() {
return false;
}
};
return toReturn;
}
ReplacedElement toReturn = delegate.createReplacedElement(context, box, uac, cssWidth, cssHeight);
return toReturn;
}
....
}
References:
- Experiment: embedding Flash in Flying Saucer
- NamespaceHandler
- ReplacedElementFactory
- Re: custom layout support
- http://wiki.java.net/bin/view/Javadesktop/TheFlyingSaucerInProcessFAQ
package com.rubenlaguna.en4j.NoteContentViewModule;
import java.util.logging.Logger;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.xhtmlrenderer.css.extend.StylesheetFactory;
import org.xhtmlrenderer.css.sheet.StylesheetInfo;
import org.xhtmlrenderer.extend.NamespaceHandler;
/**
*
* @author ecerulm
*/
class ENMLNamespaceHandler implements NamespaceHandler {
private final NamespaceHandler delegate;
public ENMLNamespaceHandler(NamespaceHandler h) {
this.delegate = h;
}
public boolean isImageElement(Element e) {
return delegate.isImageElement(e);
}
public boolean isFormElement(Element e) {
return delegate.isFormElement(e);
}
public StylesheetInfo[] getStylesheets(Document doc) {
return delegate.getStylesheets(doc);
}
public String getNonCssStyling(Element e) {
String toReturn = delegate.getNonCssStyling(e);
if ("en-media".equalsIgnoreCase(e.getNodeName())) {
toReturn = "display: block;";
}
Logger.getLogger(ENMLNamespaceHandler.class.getName()).info("style for ("+e.getNodeName()+") is ("+toReturn+")");
return toReturn;
}
public String getNamespace() {
return delegate.getNamespace();
}
public String getLinkUri(Element e) {
return delegate.getLinkUri(e);
}
public String getLang(Element e) {
return delegate.getLang(e);
}
public String getImageSourceURI(Element e) {
return delegate.getImageSourceURI(e);
}
public String getID(Element e) {
return delegate.getID(e);
}
public String getElementStyling(Element e) {
return delegate.getElementStyling(e);
}
public String getDocumentTitle(Document doc) {
return delegate.getDocumentTitle(doc);
}
public StylesheetInfo getDefaultStylesheet(StylesheetFactory factory) {
return delegate.getDefaultStylesheet(factory);
}
public String getClass(Element e) {
return delegate.getClass(e);
}
public String getAttributeValue(Element e, String namespaceURI, String attrName) {
return delegate.getAttributeValue(e, namespaceURI, attrName);
}
public String getAttributeValue(Element e, String attrName) {
return delegate.getAttributeValue(e, attrName);
}
public String getAnchorName(Element e) {
return delegate.getAnchorName(e);
}
}
ENMLReplacedElementFactory.java:
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package com.rubenlaguna.en4j.NoteContentViewModule;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextArea;
import org.w3c.dom.Element;
import org.xhtmlrenderer.extend.ReplacedElement;
import org.xhtmlrenderer.extend.ReplacedElementFactory;
import org.xhtmlrenderer.extend.UserAgentCallback;
import org.xhtmlrenderer.layout.LayoutContext;
import org.xhtmlrenderer.render.BlockBox;
import org.xhtmlrenderer.simple.extend.FormSubmissionListener;
import org.xhtmlrenderer.swing.EmptyReplacedElement;
import org.xhtmlrenderer.swing.ImageReplacedElement;
import org.xhtmlrenderer.swing.SwingReplacedElement;
import org.xhtmlrenderer.swing.SwingReplacedElementFactory;
import org.xhtmlrenderer.util.ImageUtil;
/**
*
* @author ecerulm
*/
class ENMLReplacedElementFactory implements ReplacedElementFactory {
private final SwingReplacedElementFactory delegate;
private final Logger LOG = Logger.getLogger(ENMLReplacedElementFactory.class.getName());
public ENMLReplacedElementFactory(SwingReplacedElementFactory delegate) {
this.delegate = delegate;
}
public void setFormSubmissionListener(FormSubmissionListener fsl) {
delegate.setFormSubmissionListener(fsl);
}
public void reset() {
delegate.reset();
}
public void remove(Element e) {
delegate.remove(e);
}
public ReplacedElement createReplacedElement(LayoutContext context, BlockBox box, UserAgentCallback uac, int cssWidth, int cssHeight) {
ReplacedElement toReturn = null;
Logger.getLogger(ENMLReplacedElementFactory.class.getName()).log(Level.INFO, "Element:" + box.getElement().getNodeName());
Logger.getLogger(ENMLReplacedElementFactory.class.getName()).log(Level.INFO, "Element content:" + box.getElement().getNodeValue());
if ("en-media".equals(box.getElement().getNodeName())) {
//if("image/jpeg".equalsIgnoreCase(box.getElement().getAttribute("type"))){
//
// toReturn = loadImage()
// return toReturn;
//}
toReturn = brokenImage(context, 100, 100);
}
if (null == toReturn) {
toReturn = delegate.createReplacedElement(context, box, uac, cssWidth, cssHeight);
}
return toReturn;
}
private ReplacedElement brokenImage(LayoutContext context, int cssWidth, int cssHeight) {
//TODO: add a real implementation that returns an image
ReplacedElement toReturn = null;
JTextArea cc = new JTextArea();
cc.setText("Missing implementation for en-media");
//cc.setPreferredSize(new Dimension(cssWidth, cssHeight));
cc.setSize(cc.getPreferredSize());
context.getCanvas().add(cc);
toReturn = new SwingReplacedElement(cc) {
public boolean isRequiresInteractivePaint() {
return false;
}
};
return toReturn;
}
}