+ public void append(String key, String svgText) {
+ StringBuilder builder = documents.get(key);
+ if(builder == null) {
+ builder = new StringBuilder();
+ documents.put(key, builder);
+ }
+ builder.append(svgText);
+ }
+
+ public void append(RenderSVGContext other) {
+ for(String key : other.documents.keySet()) {
+ append(key, other.get(key));
+ }
+ }
+
+ public String get(String key) {
+ StringBuilder builder = documents.get(key);
+ if(builder == null) return "";
+ else return builder.toString();
+ }
+
+ }
+
+ private static String renderSVG0(ICanvasContext ctx, Function1<Set<?>, Map<?, ?>> mappingFunction) {
+
+ // Get a DOMImplementation.
+ DOMImplementation domImpl = GenericDOMImplementation.getDOMImplementation();
+
+ // Create an instance of org.w3c.dom.Document.
+ String svgNS = "http://www.w3.org/2000/svg";
+ Document document = domImpl.createDocument(svgNS, "svg", null);
+
+ // Create an instance of the SVG Generator.
+ SVGGraphics2D svgGenerator = new Generator(document);
+
+ RenderSVGContext result = new RenderSVGContext();
+
+ try {
+
+ Selection selection = ctx.getAtMostOneItemOfClass(Selection.class);
+ if (selection != null) {
+ // This prevents workbench selection from being left over.
+ // Also prevents scene graph crap from being left on the screen.
+ IDiagram d = ctx.getDefaultHintContext().getHint(DiagramHints.KEY_DIAGRAM);
+ selection.setSelection(0, d.getElements());
+ }
+
+ G2DSceneGraph sg = ctx.getSceneGraph();
+ sg.performCleanup();
+ G2DParentNode root = (G2DParentNode) sg.getRootNode();
+
+ // rtree is the actual content of the diagram
+ RTreeNode rtree = NodeUtil.getNearestChildByClass(root, RTreeNode.class);
+ Rectangle2D rtreeBounds = NodeUtil.getLocalBounds(rtree);
+
+ // nav is a node that has zooming functionalities
+ NavigationNode nav = NodeUtil.getNearestChildByClass(root, NavigationNode.class);
+ nav.setZoomEnabled(true);
+
+ // fit view with the contents of rtreeBounds
+ nav.zoomTo(rtreeBounds);
+
+ // get the bounds of the content
+ Rectangle2D content = NodeUtil.getLocalBounds(nav);
+
+ svgGenerator.scale(3,3);
+
+ // translate svgGenerator to the x and y coordinates of current content
+ svgGenerator.translate(-1 * content.getX(), (-1 * content.getY()));
+
+ Rectangle2D destination = new Rectangle2D.Double(0,0,1000,1000);
+ double sx = destination.getWidth() / content.getWidth();
+ double sy = destination.getHeight() / content.getHeight();
+ double scale = sx < sy ? sx : sy;
+
+ // Set svgCanvasSize to the given size parameters
+ svgGenerator.setSVGCanvasSize(new Dimension((int)(scale * content.getWidth()), (int)(scale * content.getHeight())));
+ svgGenerator.setClip(content);
+
+ double trX = -1 * content.getX();
+ double trY = -1 * content.getY();
+
+ // NaNs
+ if(!Double.isFinite(trX)) trX = 0;
+ if(!Double.isFinite(trY)) trY = 0;
+
+ result.append(MAIN_SECTION, "<g class=\"symbols\">");
+ result.append(SELECTION_SECTION, "<g class=\"selections\">");
+ result.append(SELECTION_MASK_SECTION, "<g class=\"selectionMasks\">");
+
+ result.append(ALL_SECTIONS, "<g transform=\"translate(");
+ result.append(ALL_SECTIONS, "" + trX);
+ result.append(ALL_SECTIONS, ", ");
+ result.append(ALL_SECTIONS, "" + trY);
+ result.append(ALL_SECTIONS, ")\">");
+
+
+ KeyVisitor keyVisitor = new KeyVisitor();
+ sg.accept(keyVisitor);
+
+ Set<Object> keys = keyVisitor.getKeys();
+
+ Map<?, ?> mappings = mappingFunction.apply(keys);
+
+ IG2DNodeVisitor visitor = new PrintingVisitor(result, mappings);
+
+ sg.accept(visitor);
+
+ } catch (Throwable t) {
+ LOGGER.error("Problems rendering canvas context to SVG", t);
+ }
+
+
+ result.append(ALL_SECTIONS, "</g></g>");
+
+ StringBuilder res = new StringBuilder();
+ res.append("<svg width=\"100%\" height=\"100%\" stroke=\"black\">");
+ res.append(result.get(MAIN_SECTION));
+ res.append(result.get(SELECTION_SECTION));
+ res.append(result.get(SELECTION_MASK_SECTION));
+ res.append(result.get("</svg>"));
+
+// System.err.println(" == FINAL RESULT == ");
+// System.err.println(res.toString());
+
+ return res.toString();
+
+ }
+
+
+
+ private static class KeyVisitor implements IG2DNodeVisitor {
+
+ private Set<Object> keys = new HashSet<>();
+
+ @Override
+ public void enter(IG2DNode node) {
+ if (node instanceof SingleElementNode) {
+ Object key = ((SingleElementNode) node).getKey();
+ if (key != null) {
+ keys.add(key);
+ }
+ }
+ }
+
+ @Override
+ public void leave(IG2DNode node) {
+ // Nothing to do
+ }
+
+ public Set<Object> getKeys() {
+ return keys;
+ }
+ }
+
+ private static class PrintingVisitor implements IG2DNodeVisitor {
+
+ int indent = 0;
+
+ HashMap<SingleElementNode,RenderSVGContext> senBuilders = new HashMap<>();
+
+ private RenderSVGContext result;
+
+ private Map<?, ?> mappings;
+
+ public PrintingVisitor(RenderSVGContext result, Map<?, ?> mappings) {
+ this.result = result;
+ this.mappings = mappings;
+ }
+
+ private String getKey(SingleElementNode node) {
+ String key;
+ if (mappings.containsKey(node.getKey()))
+ key = mappings.get(node.getKey()).toString();
+ else
+ key = node.getKey().toString();
+ return key;
+ }
+
+ @Override
+ public void enter(IG2DNode node) {
+
+ RenderSVGContext parentBuilder = getParentBuilder(node);
+
+ indent++;
+ if(node instanceof ConnectionNode) {
+
+ for(RouteGraphNode n : NodeUtil.collectNodes(node, RouteGraphNode.class)) {
+ n.setIgnoreSelection(true);
+ }
+
+ String key = getKey((ConnectionNode) node);
+ parentBuilder.append(MAIN_SECTION, "\n<g class=\"connection\" id=\"" + key + "\">");
+ parentBuilder.append(SELECTION_SECTION, "\n<g style=\"visibility:hidden\" class=\"selection\" id=\"" + key + "\">");
+ parentBuilder.append(SELECTION_MASK_SECTION, "\n<g class=\"selectionMask\" opacity=\"0.001\" id=\"" + key + "\">");
+
+ Element doc = renderSVGNode((IG2DNode)node);
+ String svg = printSVGDocument(doc);
+ parentBuilder.append(MAIN_SECTION, svg);
+
+ for(RouteGraphNode n : NodeUtil.collectNodes(node, RouteGraphNode.class)) {
+ n.setIgnoreSelection(false);
+ }
+
+ doc = renderSVGNode((IG2DNode)node);
+ svg = printSVGDocument(doc);
+ parentBuilder.append(SELECTION_SECTION, svg);
+
+ BasicStroke bs = new BasicStroke(10f);
+
+ for(RouteGraphNode n : NodeUtil.collectNodes(node, RouteGraphNode.class)) {
+ n.setDynamicStroke(bs);
+ }
+
+ doc = renderSVGNode((IG2DNode)node);
+ svg = printSVGDocument(doc);
+ parentBuilder.append(SELECTION_MASK_SECTION, svg);
+
+ parentBuilder.append(SELECTION_MASK_SECTION, "\n</g>");
+ parentBuilder.append(SELECTION_SECTION, "\n</g>");
+ parentBuilder.append(MAIN_SECTION, "\n</g>");
+
+ } else if (node instanceof SelectionNode) {
+
+ SelectionNode n = (SelectionNode)node;
+ SingleElementNode parentSEN = (SingleElementNode)NodeUtil.getNearestParentOfType(node, SingleElementNode.class);
+ if(parentSEN != null && parentSEN.getKey() != null) {
+
+ RenderSVGContext parentBuilder2 = getParentBuilder(parentSEN);
+
+ String key = getKey(parentSEN);
+ Element doc = renderSVGNode((IG2DNode)node);
+ String svg = printSVGDocument(doc);
+ parentBuilder2.append(SELECTION_SECTION, "\n<g style=\"visibility:hidden\" class=\"selection\" id=\"" + key + "\">");
+ parentBuilder2.append(SELECTION_SECTION, svg);
+ parentBuilder2.append(SELECTION_SECTION, "\n</g>");
+ parentBuilder2.append(SELECTION_MASK_SECTION, "\n<g class=\"selectionMask\" id=\"" + key + "\">");
+ Rectangle2D rect = n.getRect();
+ // NaN
+ if(rect.getHeight() == rect.getHeight() && rect.getWidth() == rect.getWidth()) {
+ parentBuilder2.append(SELECTION_MASK_SECTION,"<rect style=\"fill:#fff\" opacity=\"0.001\"");
+ parentBuilder2.append(SELECTION_MASK_SECTION," x=\"" + rect.getX() + "\" y=\"" + rect.getY() + "\"");
+ parentBuilder2.append(SELECTION_MASK_SECTION," width=\"" + rect.getWidth() + "\" height=\"" + rect.getHeight() + "\"");
+ parentBuilder2.append(SELECTION_MASK_SECTION,"></rect>");
+ }
+ parentBuilder2.append(SELECTION_MASK_SECTION,"\n</g>");
+ }
+ } else if (node instanceof SVGNode) {
+ SVGNode svg = (SVGNode)node;
+ parentBuilder.append(MAIN_SECTION, svg.getSVGText());
+ } else if (node instanceof G2DParentNode) {
+ AffineTransform at = node.getTransform();
+ if(node instanceof SingleElementNode) {
+ SingleElementNode sen = (SingleElementNode)node;
+ if(sen.getKey() != null) {
+ String key = getKey(sen);
+ String typeClass = sen.getTypeClass();
+ String clazz = "definedElement";
+ if(typeClass != null && !typeClass.isEmpty())
+ clazz = clazz + " " + typeClass;
+
+ parentBuilder.append(MAIN_SECTION, "\n<g class=\""+clazz+"\" id=\"" + key + "\">");
+ }
+ senBuilders.put(sen, new RenderSVGContext());
+ }
+ if(!at.isIdentity()) {
+ if(at.getScaleX() == 1.0 && at.getScaleY() == 1.0 && at.getShearX() == 0.0 && at.getShearY() == 0.0) {
+ String m = "translate(" + at.getTranslateX() + " " + at.getTranslateY() + ")";
+ parentBuilder.append(ALL_SECTIONS, "\n<g transform=\"" + m + "\">");
+ } else {
+ double[] ds = new double[6];
+ at.getMatrix(ds);
+ String m = "matrix(" + ds[0] + " " + ds[1] + " " + ds[2] + " " + ds[3] + " " + ds[4] + " " + ds[5] + ")";
+ parentBuilder.append(ALL_SECTIONS, "\n<g transform=\"" + m + "\">");
+ }
+ }
+ }
+
+ //enters.put(node, b.length());
+
+ }
+
+ private RenderSVGContext getParentBuilder(IG2DNode node) {
+
+ INode parentSEN = NodeUtil.getNearestParentOfType(node, SingleElementNode.class);
+ if(parentSEN instanceof G2DSceneGraph) return result;
+
+ RenderSVGContext parentBuilder = senBuilders.get(parentSEN);
+ if(parentBuilder == null) return result;
+
+ return parentBuilder;
+
+ }
+
+ @Override
+ public void leave(IG2DNode node) {
+
+ if(node instanceof ConnectionNode || node instanceof SVGNode) {
+ // We are done
+ } else if (node instanceof G2DParentNode) {
+
+ RenderSVGContext parentBuilder = getParentBuilder(node);
+
+ if(node instanceof SingleElementNode) {
+ SingleElementNode sen = (SingleElementNode)node;
+ RenderSVGContext b = senBuilders.get(sen);
+ String content = b.get(MAIN_SECTION);
+ if(content.isEmpty()) {
+ if(sen.getKey() != null) {
+
+ for(SelectionNode n : NodeUtil.collectNodes(node, SelectionNode.class)) {
+ n.setIgnore(true);
+ }
+
+ Element doc = renderSVGNode((IG2DNode)node);
+ String svg = printSVGDocument(doc);
+ parentBuilder.append(MAIN_SECTION, svg);
+ }
+ } else {
+ parentBuilder.append(b);
+ }
+ }
+
+ AffineTransform at = node.getTransform();
+ if(!at.isIdentity()) {
+ parentBuilder.append(ALL_SECTIONS, "</g>");
+ }
+ if(node instanceof SingleElementNode) {
+ SingleElementNode sen = (SingleElementNode)node;
+ if(sen.getKey() != null) {
+ parentBuilder.append(MAIN_SECTION, "</g>");
+ }
+ }
+ }
+ indent --;
+ }
+ }