--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2007 VTT Technical Research Centre of Finland and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * VTT Technical Research Centre of Finland - initial API and implementation
+ *******************************************************************************/
+package org.simantics.diagram.participant;
+
+import java.awt.Font;
+import java.awt.RenderingHints;
+import java.awt.geom.Rectangle2D;
+import java.io.StringWriter;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.batik.dom.GenericDOMImplementation;
+import org.apache.batik.svggen.SVGGeneratorContext;
+import org.apache.batik.svggen.SVGGeneratorContext.GraphicContextDefaults;
+import org.apache.batik.svggen.SVGGraphics2D;
+import org.apache.batik.svggen.SVGGraphics2DIOException;
+import org.eclipse.swt.dnd.Clipboard;
+import org.eclipse.swt.dnd.Transfer;
+import org.eclipse.swt.widgets.Display;
+import org.simantics.diagram.elements.TextNode;
+import org.simantics.g2d.canvas.ICanvasContext;
+import org.simantics.g2d.canvas.impl.DependencyReflection.Dependency;
+import org.simantics.g2d.diagram.participant.AbstractDiagramParticipant;
+import org.simantics.g2d.diagram.participant.Selection;
+import org.simantics.g2d.element.ElementHints;
+import org.simantics.g2d.element.ElementUtils;
+import org.simantics.g2d.element.IElement;
+import org.simantics.scenegraph.INode;
+import org.simantics.scenegraph.g2d.G2DNode;
+import org.simantics.scenegraph.g2d.G2DSceneGraph;
+import org.simantics.scenegraph.g2d.events.EventHandlerReflection.EventHandler;
+import org.simantics.scenegraph.g2d.events.command.CommandEvent;
+import org.simantics.scenegraph.g2d.events.command.Commands;
+import org.simantics.scenegraph.g2d.nodes.LinkNode;
+import org.simantics.scenegraph.g2d.nodes.SelectionNode;
+import org.simantics.scenegraph.g2d.nodes.SingleElementNode;
+import org.simantics.scenegraph.g2d.nodes.connection.RouteGraphNode;
+import org.simantics.scenegraph.utils.NodeMapper;
+import org.simantics.scenegraph.utils.NodeUtil;
+import org.simantics.utils.ui.ErrorLogger;
+import org.w3c.dom.DOMImplementation;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+public class CopyAsSVGParticipant extends AbstractDiagramParticipant {
+
+ @Dependency
+ protected Selection sel;
+
+ @EventHandler(priority = 0)
+ public boolean handleCommand(CommandEvent e) {
+ if (e.command.equals(Commands.COPY_AS_SVG)) {
+ Set<IElement> ss = sel.getSelection(0);
+ copyAsSVG(getContext(), ss);
+ return true;
+ }
+ return false;
+ }
+
+ private static void copyAsSVG(ICanvasContext canvasContext, Set<IElement> elements) {
+ G2DSceneGraph sg = canvasContext.getSceneGraph();
+ NodeMapper clipboardNodeMapper = new NodeMapper();
+ List<G2DNode> selectionRenderingDisabledNodes = new ArrayList<G2DNode>();
+ SingleElementNode clipboardNode = sg.addNode("svg-clipboard-temp", SingleElementNode.class);
+
+ try {
+ for (IElement e : elements) {
+ INode node = e.getHint(ElementHints.KEY_SG_NODE);
+ if (node != null) {
+ // Don't render selection. Selection rendering could be a global rendering hint that is adhered by nodes!
+ for(RouteGraphNode n : NodeUtil.collectNodes(node, RouteGraphNode.class)) {
+ n.setIgnoreSelection(true);
+ selectionRenderingDisabledNodes.add(n);
+ }
+ for(SelectionNode n : NodeUtil.collectNodes(node, SelectionNode.class)) {
+ n.setIgnore(true);
+ selectionRenderingDisabledNodes.add(n);
+ }
+ for(TextNode n : NodeUtil.collectNodes(node, TextNode.class)) {
+ n.setShowSelection(false);
+ selectionRenderingDisabledNodes.add(n);
+ }
+
+ String nodeId = clipboardNodeMapper.add(node);
+ LinkNode delegate = clipboardNode.addNode(ElementUtils.generateNodeId(e), LinkNode.class);
+ delegate.setDelegateId( nodeId );
+ }
+ }
+
+ DOMImplementation domImpl = GenericDOMImplementation.getDOMImplementation();
+
+ String svgNS = "http://www.w3.org/2000/svg";
+ Document document = domImpl.createDocument(svgNS, "svg", null);
+
+ GraphicContextDefaults gcDefaults = new GraphicContextDefaults();
+ SVGGeneratorContext ctx = SVGGeneratorContext.createDefault(document);
+ Map<java.awt.RenderingHints.Key, Object> hintMap = new HashMap<java.awt.RenderingHints.Key, Object>();
+
+ hintMap.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
+ hintMap.put(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
+ hintMap.put(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
+ hintMap.put(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
+ hintMap.put(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
+
+ gcDefaults.setRenderingHints(new RenderingHints(hintMap));
+ gcDefaults.setFont(Font.decode(null));
+ ctx.setGraphicContextDefaults(gcDefaults);
+
+ SVGGraphics2D svgG2D = new SVGGraphics2D(ctx, false);
+
+ StringWriter writer = new StringWriter();
+
+ // Track connection crossings manually since we will render only the clipboard node.
+ sg.getNode(ConnectionCrossingsParticipant.CONNECTION_CROSSINGS_NODE_KEY).render(svgG2D);
+ clipboardNode.render(svgG2D);
+
+ Element root = svgG2D.getRoot();
+
+ Rectangle2D bounds = clipboardNode.getBoundsInLocal(true);
+ if (bounds != null) {
+ root.setAttributeNS(null, "viewBox", bounds.getMinX() + " " + bounds.getMinY() + " " + bounds.getWidth() + " " + bounds.getHeight());
+ root.setAttributeNS(null, "height", Double.toString(bounds.getHeight()));
+ root.setAttributeNS(null, "width", Double.toString(bounds.getWidth()));
+ }
+
+ try {
+ svgG2D.stream(root, writer, false, false);
+ } catch (SVGGraphics2DIOException e1) {
+ ErrorLogger.defaultLogError("Failed to copy the diagram selection as SVG." , e1);
+ }
+
+ byte[] svgContent = writer.toString().getBytes(StandardCharsets.UTF_8);
+
+ Display.getDefault().asyncExec(new Runnable() {
+ @Override
+ public void run() {
+ Clipboard cb = new Clipboard(Display.getCurrent());
+ cb.setContents(new byte[][] {svgContent},
+ new Transfer[] {
+ SVGTransfer.getInstance()
+ }
+ );
+ }
+ });
+
+ } finally {
+ clipboardNode.removeNodes();
+ clipboardNodeMapper.clear();
+ clipboardNode.remove();
+
+ // Restore the selection rendering state for changed nodes.
+ for (G2DNode n : selectionRenderingDisabledNodes) {
+ if (n instanceof RouteGraphNode) {
+ ((RouteGraphNode) n).setIgnoreSelection(false);
+ } else if (n instanceof SelectionNode) {
+ ((SelectionNode)n).setIgnore(false);
+ } else if (n instanceof TextNode) {
+ ((TextNode)n).setShowSelection(true);
+ }
+ }
+ }
+ }
+}