X-Git-Url: https://gerrit.simantics.org/r/gitweb?p=simantics%2Fplatform.git;a=blobdiff_plain;f=bundles%2Forg.simantics.diagram%2Fsrc%2Forg%2Fsimantics%2Fdiagram%2Fparticipant%2FCopyAsSVGParticipant.java;fp=bundles%2Forg.simantics.diagram%2Fsrc%2Forg%2Fsimantics%2Fdiagram%2Fparticipant%2FCopyAsSVGParticipant.java;h=b91c2bb7a451c60a7bffbec8a2bc96522d546a44;hp=0000000000000000000000000000000000000000;hb=7c341eb5f602ee3c7255e933204f4f72a6d455fa;hpb=e8b96542ea305825ae4dbf7c35f11275689ef564 diff --git a/bundles/org.simantics.diagram/src/org/simantics/diagram/participant/CopyAsSVGParticipant.java b/bundles/org.simantics.diagram/src/org/simantics/diagram/participant/CopyAsSVGParticipant.java new file mode 100644 index 000000000..b91c2bb7a --- /dev/null +++ b/bundles/org.simantics.diagram/src/org/simantics/diagram/participant/CopyAsSVGParticipant.java @@ -0,0 +1,175 @@ +/******************************************************************************* + * 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 ss = sel.getSelection(0); + copyAsSVG(getContext(), ss); + return true; + } + return false; + } + + private static void copyAsSVG(ICanvasContext canvasContext, Set elements) { + G2DSceneGraph sg = canvasContext.getSceneGraph(); + NodeMapper clipboardNodeMapper = new NodeMapper(); + List selectionRenderingDisabledNodes = new ArrayList(); + 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 hintMap = new HashMap(); + + 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); + } + } + } + } +}