]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.diagram/src/org/simantics/diagram/participant/CopyAsSVGParticipant.java
Option to copy diagram selection to clipboard as SVG graphics
[simantics/platform.git] / bundles / org.simantics.diagram / src / org / simantics / diagram / participant / CopyAsSVGParticipant.java
1 /*******************************************************************************
2  * Copyright (c) 2007 VTT Technical Research Centre of Finland and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  *     VTT Technical Research Centre of Finland - initial API and implementation
10  *******************************************************************************/
11 package org.simantics.diagram.participant;
12
13 import java.awt.Font;
14 import java.awt.RenderingHints;
15 import java.awt.geom.Rectangle2D;
16 import java.io.StringWriter;
17 import java.nio.charset.StandardCharsets;
18 import java.util.ArrayList;
19 import java.util.HashMap;
20 import java.util.List;
21 import java.util.Map;
22 import java.util.Set;
23
24 import org.apache.batik.dom.GenericDOMImplementation;
25 import org.apache.batik.svggen.SVGGeneratorContext;
26 import org.apache.batik.svggen.SVGGeneratorContext.GraphicContextDefaults;
27 import org.apache.batik.svggen.SVGGraphics2D;
28 import org.apache.batik.svggen.SVGGraphics2DIOException;
29 import org.eclipse.swt.dnd.Clipboard;
30 import org.eclipse.swt.dnd.Transfer;
31 import org.eclipse.swt.widgets.Display;
32 import org.simantics.diagram.elements.TextNode;
33 import org.simantics.g2d.canvas.ICanvasContext;
34 import org.simantics.g2d.canvas.impl.DependencyReflection.Dependency;
35 import org.simantics.g2d.diagram.participant.AbstractDiagramParticipant;
36 import org.simantics.g2d.diagram.participant.Selection;
37 import org.simantics.g2d.element.ElementHints;
38 import org.simantics.g2d.element.ElementUtils;
39 import org.simantics.g2d.element.IElement;
40 import org.simantics.scenegraph.INode;
41 import org.simantics.scenegraph.g2d.G2DNode;
42 import org.simantics.scenegraph.g2d.G2DSceneGraph;
43 import org.simantics.scenegraph.g2d.events.EventHandlerReflection.EventHandler;
44 import org.simantics.scenegraph.g2d.events.command.CommandEvent;
45 import org.simantics.scenegraph.g2d.events.command.Commands;
46 import org.simantics.scenegraph.g2d.nodes.LinkNode;
47 import org.simantics.scenegraph.g2d.nodes.SelectionNode;
48 import org.simantics.scenegraph.g2d.nodes.SingleElementNode;
49 import org.simantics.scenegraph.g2d.nodes.connection.RouteGraphNode;
50 import org.simantics.scenegraph.utils.NodeMapper;
51 import org.simantics.scenegraph.utils.NodeUtil;
52 import org.simantics.utils.ui.ErrorLogger;
53 import org.w3c.dom.DOMImplementation;
54 import org.w3c.dom.Document;
55 import org.w3c.dom.Element;
56
57 public class CopyAsSVGParticipant extends AbstractDiagramParticipant {
58
59     @Dependency
60     protected Selection sel;
61
62     @EventHandler(priority = 0)
63     public boolean handleCommand(CommandEvent e) {
64         if (e.command.equals(Commands.COPY_AS_SVG)) {
65             Set<IElement> ss = sel.getSelection(0);
66             copyAsSVG(getContext(), ss);
67             return true;
68         }
69         return false;
70     }
71
72     private static void copyAsSVG(ICanvasContext canvasContext, Set<IElement> elements) {
73         G2DSceneGraph sg = canvasContext.getSceneGraph();
74         NodeMapper clipboardNodeMapper = new NodeMapper();
75         List<G2DNode> selectionRenderingDisabledNodes = new ArrayList<G2DNode>();
76         SingleElementNode clipboardNode = sg.addNode("svg-clipboard-temp", SingleElementNode.class); 
77
78         try {
79             for (IElement e : elements) {
80                 INode node = e.getHint(ElementHints.KEY_SG_NODE);
81                 if (node != null) {
82                     // Don't render selection. Selection rendering could be a global rendering hint that is adhered by nodes!  
83                     for(RouteGraphNode n : NodeUtil.collectNodes(node, RouteGraphNode.class)) {
84                         n.setIgnoreSelection(true);
85                         selectionRenderingDisabledNodes.add(n);
86                     }
87                     for(SelectionNode n : NodeUtil.collectNodes(node, SelectionNode.class)) {
88                         n.setIgnore(true);
89                         selectionRenderingDisabledNodes.add(n);
90                     }
91                     for(TextNode n : NodeUtil.collectNodes(node, TextNode.class)) {
92                         n.setShowSelection(false);
93                         selectionRenderingDisabledNodes.add(n);
94                     }
95
96                     String nodeId = clipboardNodeMapper.add(node);
97                     LinkNode delegate = clipboardNode.addNode(ElementUtils.generateNodeId(e), LinkNode.class);
98                     delegate.setDelegateId( nodeId );
99                 }
100             }
101
102             DOMImplementation domImpl =  GenericDOMImplementation.getDOMImplementation();
103
104             String svgNS = "http://www.w3.org/2000/svg";
105             Document document = domImpl.createDocument(svgNS, "svg", null);
106
107             GraphicContextDefaults gcDefaults = new GraphicContextDefaults();
108             SVGGeneratorContext ctx = SVGGeneratorContext.createDefault(document);
109             Map<java.awt.RenderingHints.Key, Object> hintMap = new HashMap<java.awt.RenderingHints.Key, Object>();
110
111             hintMap.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
112             hintMap.put(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
113             hintMap.put(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
114             hintMap.put(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
115             hintMap.put(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
116
117             gcDefaults.setRenderingHints(new RenderingHints(hintMap));
118             gcDefaults.setFont(Font.decode(null));
119             ctx.setGraphicContextDefaults(gcDefaults);
120
121             SVGGraphics2D svgG2D = new SVGGraphics2D(ctx, false);
122
123             StringWriter writer = new StringWriter();
124
125             // Track connection crossings manually since we will render only the clipboard node.
126             sg.getNode(ConnectionCrossingsParticipant.CONNECTION_CROSSINGS_NODE_KEY).render(svgG2D);
127             clipboardNode.render(svgG2D);
128
129             Element root = svgG2D.getRoot();
130
131             Rectangle2D bounds = clipboardNode.getBoundsInLocal(true);
132             if (bounds != null) {
133                 root.setAttributeNS(null, "viewBox", bounds.getMinX() + " " + bounds.getMinY() + " " + bounds.getWidth() + " " + bounds.getHeight()); 
134                 root.setAttributeNS(null, "height", Double.toString(bounds.getHeight()));
135                 root.setAttributeNS(null, "width", Double.toString(bounds.getWidth()));
136             }
137
138             try {
139                 svgG2D.stream(root, writer, false, false);
140             } catch (SVGGraphics2DIOException e1) {
141                 ErrorLogger.defaultLogError("Failed to copy the diagram selection as SVG." , e1);
142             }
143
144             byte[] svgContent = writer.toString().getBytes(StandardCharsets.UTF_8);
145
146             Display.getDefault().asyncExec(new Runnable() {
147                 @Override
148                 public void run() {
149                     Clipboard cb = new Clipboard(Display.getCurrent());
150                     cb.setContents(new byte[][] {svgContent}, 
151                             new Transfer[] {
152                                 SVGTransfer.getInstance()
153                             }
154                     );
155                 }
156             });
157
158         } finally {
159             clipboardNode.removeNodes();
160             clipboardNodeMapper.clear();
161             clipboardNode.remove();
162
163             // Restore the selection rendering state for changed nodes.
164             for (G2DNode n : selectionRenderingDisabledNodes) {
165                 if (n instanceof RouteGraphNode) {
166                     ((RouteGraphNode) n).setIgnoreSelection(false);
167                 } else if (n instanceof SelectionNode) {
168                     ((SelectionNode)n).setIgnore(false);
169                 } else if (n instanceof TextNode) {
170                     ((TextNode)n).setShowSelection(true);
171                 }
172             }
173         }
174     }
175 }