X-Git-Url: https://gerrit.simantics.org/r/gitweb?a=blobdiff_plain;f=bundles%2Forg.simantics.modeling%2Fsrc%2Forg%2Fsimantics%2Fmodeling%2FSCLScenegraph.java;h=559469d4a21ce908aff65f9574355363a989d2f3;hb=refs%2Fchanges%2F22%2F4422%2F1;hp=ef2fb5d66cbeff9faa4f5458dc91955be12fea98;hpb=1efdd1cea0fcef60df0267f23e4ffda9fab5b23d;p=simantics%2Fplatform.git diff --git a/bundles/org.simantics.modeling/src/org/simantics/modeling/SCLScenegraph.java b/bundles/org.simantics.modeling/src/org/simantics/modeling/SCLScenegraph.java index ef2fb5d66..559469d4a 100644 --- a/bundles/org.simantics.modeling/src/org/simantics/modeling/SCLScenegraph.java +++ b/bundles/org.simantics.modeling/src/org/simantics/modeling/SCLScenegraph.java @@ -51,6 +51,7 @@ import org.simantics.g2d.canvas.ICanvasContext; import org.simantics.g2d.diagram.DiagramHints; import org.simantics.g2d.diagram.IDiagram; import org.simantics.g2d.diagram.handler.DataElementMap; +import org.simantics.g2d.diagram.participant.ElementPainter.SelectionShapeNode; import org.simantics.g2d.diagram.participant.Selection; import org.simantics.g2d.element.IElement; import org.simantics.g2d.scenegraph.ICanvasSceneGraphProvider; @@ -68,6 +69,7 @@ import org.simantics.scenegraph.g2d.nodes.BackgroundNode; import org.simantics.scenegraph.g2d.nodes.BoundsNode; import org.simantics.scenegraph.g2d.nodes.ConnectionNode; import org.simantics.scenegraph.g2d.nodes.DataNode; +import org.simantics.scenegraph.g2d.nodes.LinkNode; import org.simantics.scenegraph.g2d.nodes.NavigationNode; import org.simantics.scenegraph.g2d.nodes.SVGNode; import org.simantics.scenegraph.g2d.nodes.SelectionNode; @@ -84,6 +86,7 @@ import org.slf4j.LoggerFactory; import org.w3c.dom.DOMImplementation; import org.w3c.dom.Document; import org.w3c.dom.Element; +import org.w3c.dom.Node; import org.w3c.dom.NodeList; public class SCLScenegraph { @@ -93,9 +96,16 @@ public class SCLScenegraph { private static final String MAIN_SECTION = "main"; private static final String SELECTION_SECTION = "selection"; private static final String SELECTION_MASK_SECTION = "selectionMask"; - + private static final String[] ALL_SECTIONS = { MAIN_SECTION, SELECTION_SECTION, SELECTION_MASK_SECTION }; + // Changed from 0.001 to 0.0001 to prevent creation of huge BufferedImage's when + // generating PDF from SVG. If SVG contains any transparency then Batik uses + // bitmap-rendering which remarkably slows things down + // See org.apache.batik.gvt.AbstractGraphicsNode.paint(Graphics2D) where decisions are made + // if AlphaComposite should be painted + private static final String OPACITY = "0.0001"; + @Deprecated public static ICanvasSceneGraphProvider getICanvasSceneGraphProvider(Resource model, Resource diagram, String diagramRVI) throws DatabaseException, InterruptedException { ICanvasSceneGraphProvider provider = DiagramNodeUtil.loadSceneGraphProvider(model, diagram, diagramRVI); @@ -666,22 +676,28 @@ public class SCLScenegraph { // get the bounds of the content Rectangle2D content = rtreeBounds; - // To account for dynamic padding of selection rectangles (5 units + stroke width) - int offset = 6; - double scale = width < 0 || height < 0 ? 1.0 : Math.min((width - 2*offset) / content.getWidth(), (height - 2*offset) / content.getHeight()); - - AffineTransform tr = new AffineTransform(); - tr.translate(offset, offset); - tr.scale(scale, scale); - tr.translate(-content.getX(), -content.getY()); - tr.getMatrix(matrix); + if (content != null) { + // To account for dynamic padding of selection rectangles (5 units + stroke width) + int offset = 6; + + double scale = width < 0 || height < 0 ? 1.0 : Math.min((width - 2*offset) / content.getWidth(), (height - 2*offset) / content.getHeight()); + + AffineTransform tr = new AffineTransform(); + tr.translate(offset, offset); + tr.scale(scale, scale); + tr.translate(-content.getX(), -content.getY()); + tr.getMatrix(matrix); + svgGenerator.setSVGCanvasSize(new Dimension((int)Math.ceil(scale * content.getWidth()) + 2*offset, (int)Math.ceil(scale * content.getHeight()) + 2*offset)); + } else { + svgGenerator.setSVGCanvasSize(new Dimension(100, 100)); + } //svgGenerator.translate(offset, offset); //svgGenerator.scale(scale, scale); // translate svgGenerator to the x and y coordinates of current content //svgGenerator.translate(-content.getX(), -content.getY()); - svgGenerator.setSVGCanvasSize(new Dimension((int)Math.ceil(scale * content.getWidth()) + 2*offset, (int)Math.ceil(scale * content.getHeight()) + 2*offset)); + //svgGenerator.setClip(content); result.append(MAIN_SECTION, ""); @@ -707,7 +723,11 @@ public class SCLScenegraph { result.append(ALL_SECTIONS, ""); StringBuilder res = new StringBuilder(); - res.append(""); + if (width > 0 && height > 0 ) { + res.append(""); + } else { + res.append(""); + } res.append(""); res.append(result.get(MAIN_SECTION)); res.append(result.get(SELECTION_SECTION)); @@ -768,15 +788,39 @@ public class SCLScenegraph { private String getKey(SingleElementNode node) { String key; if(node.getKey() != null) { - if (mappings.containsKey(node.getKey())) + if (mappings.containsKey(node.getKey())) { key = mappings.get(node.getKey()).toString(); - else + key = escape(key); + } else { key = node.getKey().toString(); + key = escape(key); + } } else { key = Long.toString(node.getId()); } return key; } + + private String escape(String key) { + // Keys may contain '<' '>' characters, which causes errors in browser SVG handling. + return org.apache.commons.lang.StringEscapeUtils.escapeHtml(key); + } + + private String removeElem(String xml, String elemStart) { + // FIXME: This is rather nasty and error prone way of removing elements from XML string. + // This only supports elements with /> end element tag. Elements ends with are not supported! + int start = xml.indexOf("<"+elemStart); + if (start>=0) { + int end = xml.indexOf(">",start); + if (end >= 0) { + if (start > 0) + return xml.substring(0,start)+xml.substring(end+1); + else + return xml.substring(end+1); + } + } + return null; + } @Override public void enter(IG2DNode node) { @@ -793,7 +837,7 @@ public class SCLScenegraph { String key = getKey((ConnectionNode) node); parentBuilder.append(MAIN_SECTION, "\n"); parentBuilder.append(SELECTION_SECTION, "\n"); - parentBuilder.append(SELECTION_MASK_SECTION, "\n"); + parentBuilder.append(SELECTION_MASK_SECTION, "\n"); Element doc = renderSVGNode(svgGenerator, (IG2DNode)node); String svg = printSVGDocument(doc); @@ -841,7 +885,7 @@ public class SCLScenegraph { Rectangle2D rect = n.getRect(); // NaN if(rect.getHeight() == rect.getHeight() && rect.getWidth() == rect.getWidth()) { - parentBuilder2.append(SELECTION_MASK_SECTION,""); @@ -871,7 +915,21 @@ public class SCLScenegraph { parentBuilder.append(MAIN_SECTION, "\n"); } } - parentBuilder.append(MAIN_SECTION, svg.getSVGText()); + String svgContent = svg.getSVGText(); + // SVGNode content may contain SVG/XML elements, which break browser compatibility + int start = svgContent.indexOf("= 0) + svgContent = svgContent.substring(start); + else { + String s = removeElem(svgContent, "?xml"); + if (s != null) { + svgContent = ""+s+""; + s = removeElem(svgContent, "!DOCTYPE"); + if (s != null) + svgContent = s; + } + } + parentBuilder.append(MAIN_SECTION, svgContent); if (!at.isIdentity()) { parentBuilder.append(MAIN_SECTION, "\n"); } @@ -899,6 +957,84 @@ public class SCLScenegraph { parentBuilder.append(ALL_SECTIONS, "\n"); } } + } else if (node instanceof TextNode) { + TextNode text = (TextNode)node; + + SingleElementNode parentSEN = (SingleElementNode)NodeUtil.getNearestParentOfType(node, SingleElementNode.class); + if(parentSEN != null) { + + text.setShowSelection(false); + Element doc = renderSVGNode(svgGenerator, (IG2DNode)node); + String svg = printSVGDocument(doc); + parentBuilder.append(MAIN_SECTION, svg); + + RenderSVGContext parentBuilder2 = getParentBuilder(parentSEN); + + String key = getKey(parentSEN); + text.setShowSelection(true); + doc = renderSVGNode(svgGenerator, (IG2DNode)node); + svg = printSVGDocument(doc); + + parentBuilder2.append(SELECTION_SECTION, "\n"); + parentBuilder2.append(SELECTION_SECTION, svg); + parentBuilder2.append(SELECTION_SECTION, "\n"); + parentBuilder2.append(SELECTION_MASK_SECTION, "\n"); + Rectangle2D rect = text.getBounds(); + // NaN + if(rect.getHeight() == rect.getHeight() && rect.getWidth() == rect.getWidth()) { + parentBuilder2.append(SELECTION_MASK_SECTION,""); + } + parentBuilder2.append(SELECTION_MASK_SECTION,"\n"); + + } + } else if (!(node instanceof RouteGraphNode) && !(node instanceof LinkNode)){ + try { + Element doc = renderSVGNode(svgGenerator, (IG2DNode)node); + NodeList gList = doc.getElementsByTagName("g"); + if (gList.getLength() == 0) + return; + boolean hasContent = false; + for (int i = 0; i < gList.getLength(); i++) { + Node gNode = gList.item(i); + if (gNode.hasChildNodes()) { + hasContent = true; + break; + } + } + if (!hasContent) + return; + String svg = printSVGDocument(doc); + if (node instanceof SelectionShapeNode) { + SingleElementNode parentSEN = (SingleElementNode)NodeUtil.getNearestParentOfType(node, SingleElementNode.class); + if(parentSEN != null) { + String key = getKey(parentSEN); + RenderSVGContext parentBuilder2 = getParentBuilder(parentSEN); + parentBuilder2.append(SELECTION_SECTION, "\n"); + parentBuilder2.append(SELECTION_SECTION, svg); + parentBuilder2.append(SELECTION_SECTION, "\n"); + + parentBuilder2.append(SELECTION_MASK_SECTION, "\n"); + Rectangle2D rect = node.getBounds(); + // NaN + if(rect.getHeight() == rect.getHeight() && rect.getWidth() == rect.getWidth()) { + parentBuilder2.append(SELECTION_MASK_SECTION,""); + } + parentBuilder2.append(SELECTION_MASK_SECTION,"\n"); + } + } else { + parentBuilder.append(MAIN_SECTION, ""); + parentBuilder.append(MAIN_SECTION, svg); + parentBuilder.append(MAIN_SECTION, "\n"); + } + } catch (Exception e) { + // TODO: There are nodes that do not behave well when rendered to SVG. For backwards compatibility, we don't handle the exceptions. + } } //enters.put(node, b.length());