package org.simantics.modeling;
import java.awt.BasicStroke;
+import java.awt.Color;
import java.awt.Dimension;
import java.awt.RenderingHints;
import java.awt.RenderingHints.Key;
import org.simantics.diagram.elements.TextGridNode;
import org.simantics.diagram.elements.TextNode;
import org.simantics.diagram.stubs.DiagramResource;
+import org.simantics.g2d.canvas.Hints;
import org.simantics.g2d.canvas.ICanvasContext;
import org.simantics.g2d.diagram.DiagramHints;
import org.simantics.g2d.diagram.IDiagram;
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;
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 {
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);
}
return "No nodes in scenegraph!";
}
+
+ /**
+ * Returns background color of a canvasContext or null.
+ * @param ctx
+ * @return color in RGBA List<Integer> format, or null.
+ */
+ public static List<Integer> getBackgroundColor(ICanvasContext ctx) {
+ Color color = ctx.getDefaultHintContext().getHint(Hints.KEY_BACKGROUND_COLOR);
+ if (color == null)
+ return null;
+ ArrayList<Integer> rgba = new ArrayList<>(4);
+ rgba.add(color.getRed());
+ rgba.add(color.getGreen());
+ rgba.add(color.getBlue());
+ rgba.add(color.getAlpha());
+ return rgba;
+ }
public static String sceneGraphTest (ICanvasContext ctx, String module, String value) {
public static final String svgNS = "http://www.w3.org/2000/svg";
- public Generator(SVGGeneratorContext ctx, boolean joku) {
- super(ctx, joku);
+ public Generator(SVGGeneratorContext ctx, boolean textAsShapes) {
+ super(ctx, textAsShapes);
}
public Generator(Document document) {
super(document);
+ // prevent batik comment in each g-element
+ getGeneratorContext().setComment(null);
}
@Override
RenderSVGContext result = new RenderSVGContext();
+ double[] matrix = new double[6];
try {
Selection selection = ctx.getAtMostOneItemOfClass(Selection.class);
// 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());
-
- svgGenerator.translate(offset, offset);
- svgGenerator.scale(scale, scale);
+ 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.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, "<g class=\"symbols\">");
StringBuilder res = new StringBuilder();
res.append("<svg width=\"100%\" height=\"100%\" stroke=\"black\">");
+ res.append("<g transform=\"matrix(").append(matrix[0]).append(",").append(matrix[1]).append(",").append(matrix[2]).append(",").append(matrix[3]).append(",").append(matrix[4]).append(",").append(matrix[5]).append(")\">");
res.append(result.get(MAIN_SECTION));
res.append(result.get(SELECTION_SECTION));
res.append(result.get(SELECTION_MASK_SECTION));
+ res.append("</g>");
res.append("</svg>");
// System.err.println(" == FINAL RESULT == ");
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 + "\">");
+ parentBuilder.append(SELECTION_MASK_SECTION, "\n<g class=\"selectionMask\" opacity=\"" + OPACITY + "\" id=\"" + key + "\">");
Element doc = renderSVGNode(svgGenerator, (IG2DNode)node);
String svg = printSVGDocument(doc);
parentBuilder.append(SELECTION_SECTION, "\n</g>");
parentBuilder.append(MAIN_SECTION, "\n</g>");
- } else if (node instanceof SelectionNode) {
+ } else if (isSelection0(node)) {
SelectionNode n = (SelectionNode)node;
SingleElementNode parentSEN = (SingleElementNode)NodeUtil.getNearestParentOfType(node, SingleElementNode.class);
parentBuilder2.append(SELECTION_SECTION, "\n<g style=\"visibility:hidden\" class=\"selection\" id=\"" + key + "\">");
parentBuilder2.append(SELECTION_SECTION, svg);
parentBuilder2.append(SELECTION_SECTION, "\n</g>");
- AffineTransform transform = svgGenerator.getTransform();
- double[] matrix = new double[6];
- transform.getMatrix(matrix);
- String matrixString = String.format("matrix(%f,%f,%f,%f,%f,%f)", matrix[0], matrix[1], matrix[2], matrix[3], matrix[4], matrix[5]);
- parentBuilder2.append(SELECTION_MASK_SECTION, "\n<g class=\"selectionMask\" id=\"" + key + "\" transform=\"" + matrixString + "\">");
+ parentBuilder2.append(SELECTION_MASK_SECTION, "\n<g class=\"selectionMask\" id=\"" + key /*+ "\" transform=\"" + matrixString + "\"*/+ "\">");
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,"<rect style=\"fill:#fff\" opacity=\"" + OPACITY + "\"");
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 SelectionNode) {
+
+ Element doc = renderSVGNode(svgGenerator, (IG2DNode)node);
+ String svg = printSVGDocument(doc);
+ parentBuilder.append(MAIN_SECTION, "<g>");
+ parentBuilder.append(MAIN_SECTION, svg);
+ parentBuilder.append(MAIN_SECTION, "\n</g>");
+
} else if (node instanceof SVGNode) {
SVGNode svg = (SVGNode)node;
+ AffineTransform at = svg.getTransform();
+ 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(MAIN_SECTION, "\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(MAIN_SECTION, "\n<g transform=\"" + m + "\">");
+ }
+ }
parentBuilder.append(MAIN_SECTION, svg.getSVGText());
+ if (!at.isIdentity()) {
+ parentBuilder.append(MAIN_SECTION, "\n</g>");
+ }
} else if (node instanceof G2DParentNode) {
AffineTransform at = node.getTransform();
if(node instanceof SingleElementNode) {
parentBuilder.append(ALL_SECTIONS, "\n<g transform=\"" + m + "\">");
}
}
+ } 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<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 /*+ "\" transform=\"" + matrixString + "\"*/+ "\">");
+ Rectangle2D rect = text.getBounds();
+ // NaN
+ if(rect.getHeight() == rect.getHeight() && rect.getWidth() == rect.getWidth()) {
+ parentBuilder2.append(SELECTION_MASK_SECTION,"<rect style=\"fill:#fff\" opacity=\"" + OPACITY + "\"");
+ 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 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);
+ parentBuilder.append(MAIN_SECTION, "<g class=\"" +node.getSimpleClassName() +"\">");
+ parentBuilder.append(MAIN_SECTION, svg);
+ parentBuilder.append(MAIN_SECTION, "\n</g>");
+ } 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());
}
+ private boolean isSelection0(IG2DNode node) {
+
+ if(node instanceof SelectionNode) {
+ SelectionNode sn = (SelectionNode)node;
+ return sn.getSelectionId() == 0;
+ } else {
+ return false;
+ }
+
+ }
+
private RenderSVGContext getParentBuilder(IG2DNode node) {
INode parentSEN = NodeUtil.getNearestParentOfType(node, SingleElementNode.class);