X-Git-Url: https://gerrit.simantics.org/r/gitweb?p=simantics%2Fplatform.git;a=blobdiff_plain;f=bundles%2Forg.simantics.scenegraph%2Fsrc%2Forg%2Fsimantics%2Fscenegraph%2Fg2d%2Fnodes%2FSVGNode.java;h=f8f692e1d88b70ee080c6adb1591ba5a24d3a70c;hp=cdd6a26458f578d4b30fbe3e5b8099a6191f6550;hb=81e3522f6cb5822f701b66a9fbf9be9f9bf97ec9;hpb=c26409b1caf2f1e560d37c5befd11b442399c3fe diff --git a/bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/g2d/nodes/SVGNode.java b/bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/g2d/nodes/SVGNode.java index cdd6a2645..f8f692e1d 100644 --- a/bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/g2d/nodes/SVGNode.java +++ b/bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/g2d/nodes/SVGNode.java @@ -17,6 +17,8 @@ import java.awt.geom.AffineTransform; import java.awt.geom.Rectangle2D; import java.io.ByteArrayInputStream; import java.io.IOException; +import java.io.StringReader; +import java.io.StringWriter; import java.lang.ref.WeakReference; import java.math.BigInteger; import java.net.URI; @@ -24,25 +26,44 @@ import java.net.URL; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.WeakHashMap; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.stream.StreamResult; + import org.simantics.scenegraph.ExportableWidget.RasterOutputWidget; import org.simantics.scenegraph.LoaderNode; import org.simantics.scenegraph.ScenegraphUtils; import org.simantics.scenegraph.g2d.G2DNode; +import org.simantics.scenegraph.g2d.G2DRenderingHints; import org.simantics.scenegraph.utils.BufferedImage; import org.simantics.scenegraph.utils.G2DUtils; import org.simantics.scenegraph.utils.InitValueSupport; import org.simantics.scenegraph.utils.MipMapBufferedImage; import org.simantics.scenegraph.utils.MipMapVRamBufferedImage; +import org.simantics.scenegraph.utils.SVGPassthruShape; import org.simantics.scenegraph.utils.VRamBufferedImage; import org.simantics.scl.runtime.function.Function1; import org.simantics.scl.runtime.function.Function2; import org.simantics.utils.threads.AWTThread; - +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.NodeList; +import org.xml.sax.InputSource; + +import com.kitfox.svg.RenderableElement; import com.kitfox.svg.SVGCache; import com.kitfox.svg.SVGDiagram; import com.kitfox.svg.SVGElement; @@ -56,69 +77,29 @@ import com.kitfox.svg.animation.AnimationElement; @RasterOutputWidget public class SVGNode extends G2DNode implements InitValueSupport, LoaderNode { - public static class SVGNodeAssignment { - public String elementId; - public String attributeNameOrId; - public String value; - public SVGNodeAssignment(String elementId, String attributeNameOrId, String value) { - this.elementId = elementId; - this.attributeNameOrId = attributeNameOrId; - this.value = value; - } - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((attributeNameOrId == null) ? 0 : attributeNameOrId.hashCode()); - result = prime * result + ((elementId == null) ? 0 : elementId.hashCode()); - result = prime * result + ((value == null) ? 0 : value.hashCode()); - return result; - } - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - SVGNodeAssignment other = (SVGNodeAssignment) obj; - if (attributeNameOrId == null) { - if (other.attributeNameOrId != null) - return false; - } else if (!attributeNameOrId.equals(other.attributeNameOrId)) - return false; - if (elementId == null) { - if (other.elementId != null) - return false; - } else if (!elementId.equals(other.elementId)) - return false; - if (value == null) { - if (other.value != null) - return false; - } else if (!value.equals(other.value)) - return false; - return true; - } - } - - private static final long serialVersionUID = 8508750881358776559L; + private static final long serialVersionUID = 8508750881358776559L; + private static final Logger LOGGER = LoggerFactory.getLogger(SVGNode.class); protected String data = null; protected String defaultData = null; protected Point targetSize = null; protected Boolean useMipMap = true; protected Rectangle2D bounds = null; - + protected List assignments = new ArrayList(); - transient BufferedImage buffer = null; - transient String documentCache = null; - transient SVGDiagram diagramCache = null; - transient String dataHash = null; + protected transient BufferedImage buffer = null; + protected transient String documentCache = null; + protected transient SVGDiagram diagramCache = null; + protected transient String dataHash = null; static transient Map> bufferCache = new HashMap>(); + @Override + public void init() { + super.init(); + } + @Override public void cleanup() { cleanDiagramCache(); @@ -133,6 +114,7 @@ public class SVGNode extends G2DNode implements InitValueSupport, LoaderNode { SVGDiagram d = diagramCache; if (d != null) { diagramCache = null; + dataHash = null; SVGUniverse univ = SVGCache.getSVGUniverse(); if (univ.decRefCountAndClear(d.getXMLBase()) == 0) { // Cleared! @@ -153,6 +135,7 @@ public class SVGNode extends G2DNode implements InitValueSupport, LoaderNode { } this.data = cached; this.defaultData = cached; + cleanDiagramCache(); } @SyncField("targetSize") @@ -188,22 +171,34 @@ public class SVGNode extends G2DNode implements InitValueSupport, LoaderNode { if (data == null) return; // Not initialized - if (!data.equals(documentCache) || diagramCache == null || buffer == null) - initBuffer(g2d); - AffineTransform ot = null; if (!transform.isIdentity()) { ot = g2d.getTransform(); g2d.transform(transform); } - if (buffer != null) - buffer.paint(g2d); + if (g2d.getRenderingHint(G2DRenderingHints.KEY_SVG_PASSTHRU) == Boolean.TRUE) { + SVGPassthruShape.resetG2D(g2d); + String svg = assignments.isEmpty() ? data : applyAssigments(data, assignments); + if (svg != null) { + g2d.fill(new SVGPassthruShape(svg)); + } + } else { + if (!data.equals(documentCache) || diagramCache == null || buffer == null) + initBuffer(g2d); + + if (buffer != null) + buffer.paint(g2d); + } if (ot != null) g2d.setTransform(ot); } + protected int dynamicHash() { + return 0; + } + protected String parseSVG() { if (data == null) return null; @@ -218,32 +213,54 @@ public class SVGNode extends G2DNode implements InitValueSupport, LoaderNode { diagramCache = null; } - // NOTE: hard-coded to assume all SVG data is encoded in UTF-8 - byte[] dataBytes = data.getBytes("UTF-8"); - dataHash = digest(dataBytes, assignments); + // Lets check for rootAssignment that contributes the whole SVG + SVGNodeAssignment rootAssignment = null; + if (!assignments.isEmpty()) { + for (SVGNodeAssignment ass : assignments) { + if (ass.attributeNameOrId.equals("$root")) { + rootAssignment = ass; + break; + } + } + } + byte[] dataBytes; + if (rootAssignment != null) { + dataBytes = rootAssignment.value.getBytes("UTF-8"); + } else { + // NOTE: hard-coded to assume all SVG data is encoded in UTF-8 + dataBytes = data.getBytes("UTF-8"); + } + dataHash = digest(dataBytes, assignments, dynamicHash()); URI uri = univ.loadSVG(new ByteArrayInputStream(dataBytes), dataHash); diagramCache = univ.getDiagram(uri, false); if (diagramCache != null) { univ.incRefCount(diagramCache.getXMLBase()); - - if (diagramCache.getRoot() == null) { + SVGRoot root = diagramCache.getRoot(); + if (root == null) { univ.decRefCount(diagramCache.getXMLBase()); diagramCache = univ.getDiagram(univ.loadSVG(BROKEN_SVG_DATA), false); dataHash = "broken"; univ.incRefCount(diagramCache.getXMLBase()); bbox = (Rectangle2D) diagramCache.getRoot().getBoundingBox().clone(); } else { - bbox = diagramCache.getRoot().getBoundingBox(); + bbox = root.getBoundingBox(); if (bbox.isEmpty()) { - univ.decRefCount(diagramCache.getXMLBase()); - diagramCache = univ.getDiagram(univ.loadSVG(EMPTY_SVG_DATA), false); - dataHash = "empty"; - univ.incRefCount(diagramCache.getXMLBase()); - bbox = (Rectangle2D) diagramCache.getRoot().getBoundingBox().clone(); + // Lets check if this should be visible or not + Set presentationAttributes = root.getPresentationAttributes(); + if (!presentationAttributes.contains("display")) { + // TODO: fix this - How can one read values of attributes in SVG salamander??? + univ.decRefCount(diagramCache.getXMLBase()); + diagramCache = univ.getDiagram(univ.loadSVG(EMPTY_SVG_DATA), false); + dataHash = "empty"; + univ.incRefCount(diagramCache.getXMLBase()); + bbox = (Rectangle2D) root.getBoundingBox().clone(); + } else { + bbox = new Rectangle2D.Double(0, 0, 0, 0); + } } else { if (applyAssignments(diagramCache, assignments)) { - bbox = (Rectangle2D) diagramCache.getRoot().getBoundingBox().clone(); + bbox = (Rectangle2D) root.getBoundingBox().clone(); } else { bbox = (Rectangle2D) bbox.clone(); } @@ -268,37 +285,117 @@ public class SVGNode extends G2DNode implements InitValueSupport, LoaderNode { return dataHash; } - private static boolean applyAssignments(SVGDiagram diagram, List assignments) throws SVGException { + private static DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); + static { + dbf.setValidating(false); + try { + dbf.setFeature("http://xml.org/sax/features/namespaces", false); + dbf.setFeature("http://xml.org/sax/features/validation", false); + dbf.setFeature("http://apache.org/xml/features/nonvalidating/load-dtd-grammar", false); + dbf.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false); + } catch (ParserConfigurationException e) { + // Nothing to do + } + } + + // Notice: Remember to change both implementations of applyAssigments when modifying the functionality. + protected static String applyAssigments(String svg, List assignments) { + try { + DocumentBuilder db = dbf.newDocumentBuilder(); + Document doc = db.parse(new InputSource(new StringReader(svg))); + + NodeList entries = doc.getElementsByTagName("*"); + for (int i=0; i