]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/g2d/nodes/SVGNode.java
Supply SVG text editor with element measurement context
[simantics/platform.git] / bundles / org.simantics.scenegraph / src / org / simantics / scenegraph / g2d / nodes / SVGNode.java
index 2fc37b7d59ab6bc9179943ab80b60246118e08e3..d21ba1f16a73c91ee9517244492bfa6e2dccec2e 100644 (file)
@@ -24,9 +24,11 @@ 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 org.simantics.scenegraph.ExportableWidget.RasterOutputWidget;
@@ -43,6 +45,7 @@ import org.simantics.scl.runtime.function.Function1;
 import org.simantics.scl.runtime.function.Function2;
 import org.simantics.utils.threads.AWTThread;
 
+import com.kitfox.svg.RenderableElement;
 import com.kitfox.svg.SVGCache;
 import com.kitfox.svg.SVGDiagram;
 import com.kitfox.svg.SVGElement;
@@ -63,16 +66,21 @@ public class SVGNode extends G2DNode implements InitValueSupport, LoaderNode {
     protected Point           targetSize       = null;
     protected Boolean         useMipMap        = true;
     protected Rectangle2D     bounds           = null;
-    
+
     protected List<SVGNodeAssignment> assignments = new ArrayList<SVGNodeAssignment>();
 
-    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<String, WeakReference<BufferedImage>> bufferCache = new HashMap<String, WeakReference<BufferedImage>>();
 
+    @Override
+    public void init() {
+        super.init();
+    }
+
     @Override
     public void cleanup() {
         cleanDiagramCache();
@@ -158,6 +166,10 @@ public class SVGNode extends G2DNode implements InitValueSupport, LoaderNode {
             g2d.setTransform(ot);
     }
 
+    protected int dynamicHash() {
+        return 0;
+    }
+
     protected String parseSVG() {
         if (data == null)
             return null;
@@ -172,32 +184,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();
                             }
@@ -222,10 +256,12 @@ public class SVGNode extends G2DNode implements InitValueSupport, LoaderNode {
         return dataHash;
     }
 
-    private static boolean applyAssignments(SVGDiagram diagram, List<SVGNodeAssignment> assignments) throws SVGException {
+    protected boolean applyAssignments(SVGDiagram diagram, List<SVGNodeAssignment> assignments) throws SVGException {
         if (assignments.isEmpty())
             return false;
+
         boolean changed = false;
+
         for (SVGNodeAssignment ass : assignments) {
             SVGElement e = diagram.getElement(ass.elementId);
             if (e != null) {
@@ -242,21 +278,28 @@ public class SVGNode extends G2DNode implements InitValueSupport, LoaderNode {
                             ((Text) parent).rebuild();
                         changed = true;
                     }
+                } else if (ass.attributeNameOrId.startsWith("#")) {
+                    e.setAttribute(ass.attributeNameOrId.substring(1), AnimationElement.AT_CSS, ass.value);
+                    changed = true;
                 } else {
                     e.setAttribute(ass.attributeNameOrId, AnimationElement.AT_AUTO, ass.value);
                     changed = true;
                 }
             }
         }
-        diagram.updateTime(0);
+
         return changed;
     }
 
     public static Rectangle2D getBounds(String data) {
-        return getBounds(data, null);
+        return getBounds(data, 0);
+    }
+
+    public static Rectangle2D getBounds(String data, int dynamicHash) {
+        return getBounds(data, Collections.emptyList(), dynamicHash);
     }
 
-    public static Rectangle2D getBounds(String data, List<SVGNodeAssignment> assignments) {
+    public static Rectangle2D getBounds(String data, List<SVGNodeAssignment> assignments, int dynamicHash) {
         if (data == null) {
             new Exception("null SVG data").printStackTrace();
             return null;
@@ -266,7 +309,7 @@ public class SVGNode extends G2DNode implements InitValueSupport, LoaderNode {
         try {
             // NOTE: hard-coded to assume all SVG data is encoded in UTF-8
             byte[] dataBytes = data.getBytes("UTF-8");
-            String digest = digest(dataBytes, assignments);
+            String digest = digest(dataBytes, assignments, dynamicHash);
 
             SVGUniverse univ = SVGCache.getSVGUniverse();
             // TODO: this completely removes any parallel processing from the SVG loading which would be nice to have.
@@ -300,10 +343,10 @@ public class SVGNode extends G2DNode implements InitValueSupport, LoaderNode {
     }
 
     public static Rectangle2D getRealBounds(String data) {
-       return getRealBounds(data, null);
+        return getRealBounds(data, Collections.emptyList(), 0);
     }
 
-    public static Rectangle2D getRealBounds(String data, List<SVGNodeAssignment> assignments) {
+    public static Rectangle2D getRealBounds(String data, List<SVGNodeAssignment> assignments, int dynamicHash) {
         if (data == null) {
             new Exception("null SVG data").printStackTrace();
             return null;
@@ -313,7 +356,7 @@ public class SVGNode extends G2DNode implements InitValueSupport, LoaderNode {
         try {
             // NOTE: hard-coded to assume all SVG data is encoded in UTF-8
             byte[] dataBytes = data.getBytes("UTF-8");
-            String digest = digest(dataBytes, assignments);
+            String digest = digest(dataBytes, assignments, dynamicHash);
 
             SVGUniverse univ = SVGCache.getSVGUniverse();
             // TODO: this completely removes any parallel processing from the SVG loading which would be nice to have.
@@ -335,13 +378,12 @@ public class SVGNode extends G2DNode implements InitValueSupport, LoaderNode {
     }
 
     protected void initBuffer(Graphics2D g2d) {
-       
         if (!data.equals(documentCache) || diagramCache == null) {
-               dataHash = parseSVG();
-               if (diagramCache == null) {
-                       System.err.println("UNABLE TO PARSE SVG:\n" + data);
-                       return;
-               }
+            dataHash = parseSVG();
+            if (diagramCache == null) {
+                System.err.println("UNABLE TO PARSE SVG:\n" + data);
+                return;
+            }
         }
 
         if (buffer != null) {
@@ -353,14 +395,14 @@ public class SVGNode extends G2DNode implements InitValueSupport, LoaderNode {
         } else if(diagramCache.getViewRect().getWidth()==0 || diagramCache.getViewRect().getHeight()==0) {
             buffer = null;
         } else if(useMipMap) {
-               if(G2DUtils.isAccelerated(g2d)) {
+            if(G2DUtils.isAccelerated(g2d)) {
                 buffer = new MipMapVRamBufferedImage(diagramCache, bounds, targetSize);
             } else {
                 buffer = new MipMapBufferedImage(diagramCache, bounds, targetSize);
             }
             bufferCache.put(dataHash, new WeakReference<BufferedImage>(buffer));
         } else {
-               if(G2DUtils.isAccelerated(g2d)) {
+            if(G2DUtils.isAccelerated(g2d)) {
                 buffer = new VRamBufferedImage(diagramCache, bounds, targetSize);
             } else {
                 buffer = new BufferedImage(diagramCache, bounds, targetSize);
@@ -392,13 +434,13 @@ public class SVGNode extends G2DNode implements InitValueSupport, LoaderNode {
     }
 
     static WeakHashMap<String, String> digestCache = new WeakHashMap<String, String>();
-    
-    static String digest(byte[] dataBytes, List<SVGNodeAssignment> assignments) {
+
+    static String digest(byte[] dataBytes, List<SVGNodeAssignment> assignments, int dynamicHash) {
         try {
             MessageDigest md = MessageDigest.getInstance("MD5");
             byte[] messageDigest = md.digest(dataBytes);
             BigInteger number = new BigInteger(1, messageDigest);
-            String dataHash = number.toString(16) + (assignments != null ? assignments.hashCode() : 0);
+            String dataHash = number.toString(16) + (assignments != null ? assignments.hashCode() : 0) + 31 * dynamicHash;
             String result = digestCache.get(dataHash);
             if(result == null) {
                result = dataHash;
@@ -435,12 +477,21 @@ public class SVGNode extends G2DNode implements InitValueSupport, LoaderNode {
        public void synchronizeTransform(double[] data) {
                this.setTransform(new AffineTransform(data));
        }
-       
+
        public String getSVGText() {
                String ret = data.replace("<svg", "<g").replaceAll("svg>", "g>");
                //return diagramCache.toString();
                //return data.replace("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?><!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\"><svg xmlns=\"http://www.w3.org/2000/svg\" overflow=\"visible\" version=\"1.1\"", "<g").replaceAll("svg>", "/g>");
                return ret;
        }
-       
+
+    public Rectangle2D getElementBounds(String id) throws SVGException {
+        SVGElement e = diagramCache.getElement(id);
+        if (e instanceof RenderableElement) {
+            return ((RenderableElement)e).getBoundingBox();
+        } else {
+           return null;
+        }
+    }
+
 }