Also moved rendering hints to more appropriate class.
refs #7557
Change-Id: If97b08ae82db111858b8350266b446b56114a30b
--- /dev/null
+package org.simantics.diagram.svg.export;
+
+import java.awt.Font;
+import java.awt.RenderingHints;
+import java.awt.RenderingHints.Key;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.batik.svggen.SVGGeneratorContext;
+import org.apache.batik.svggen.SVGGeneratorContext.GraphicContextDefaults;
+import org.apache.batik.svggen.SVGGraphics2D;
+import org.simantics.scenegraph.g2d.G2DPDFRenderingHints;
+import org.simantics.scenegraph.g2d.G2DRenderingHints;
+import org.w3c.dom.Document;
+
+public class SVGGraphics2DWithPassthruSupport extends SVGGraphics2D {
+
+ public SVGGraphics2DWithPassthruSupport(Document document) {
+ this(SVGGeneratorContext.createDefault(document));
+ }
+
+ public SVGGraphics2DWithPassthruSupport(SVGGeneratorContext context) {
+ super(updateDefaults(context), false);
+
+ setRenderingHint(G2DRenderingHints.KEY_TEXT_RENDERING_MODE, G2DRenderingHints.TextRenderingMode.AS_TEXT);
+ setRenderingHint(G2DRenderingHints.KEY_SVG_PASSTHRU, true);
+
+ this.shapeConverter = new SVGShapeWithPassthruSupport(generatorCtx);
+ }
+
+ private static SVGGeneratorContext updateDefaults(SVGGeneratorContext context) {
+ GraphicContextDefaults gcDefaults = new GraphicContextDefaults();
+
+ Map<Key, Object> hintMap = new HashMap<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));
+
+ context.setGraphicContextDefaults(gcDefaults);
+ return context;
+ }
+}
\ No newline at end of file
--- /dev/null
+package org.simantics.diagram.svg.export;
+
+import java.awt.Shape;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+
+import org.apache.batik.svggen.SVGGeneratorContext;
+import org.apache.batik.svggen.SVGShape;
+import org.simantics.db.common.utils.Logger;
+import org.simantics.scenegraph.utils.SVGPassthruShape;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.xml.sax.SAXException;
+
+public class SVGShapeWithPassthruSupport extends SVGShape {
+ private DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
+
+ public SVGShapeWithPassthruSupport(SVGGeneratorContext generatorCtx) {
+ super(generatorCtx);
+ dbf.setValidating(false);
+ dbf.setExpandEntityReferences(false);
+ try {
+ dbf.setFeature("http://xml.org/sax/features/namespaces", false);
+ dbf.setFeature("http://xml.org/sax/features/validation", false);
+ dbf.setFeature("http://apache.org/xml/features/nonvalidating/load-dtd-grammar", false);
+ dbf.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
+ } catch (ParserConfigurationException e) {
+ Logger.defaultLogError(e);
+ }
+ }
+
+ public Element toSVG(Shape shape) {
+ if (shape instanceof SVGPassthruShape) {
+ String source = ((SVGPassthruShape) shape).getSource();
+
+ try {
+
+ Document owner = generatorContext.getDOMFactory();
+
+ @SuppressWarnings("unchecked")
+ Map<String, String> defsMap = (Map<String, String>)owner.getUserData("defs-map");
+ if (defsMap == null) {
+ defsMap = new HashMap<String, String>();
+ owner.setUserData("defs-map", defsMap, null);
+ }
+
+ synchronized (defsMap) {
+ String symbolId = defsMap.get(source);
+ if (symbolId == null) {
+ symbolId = "S" + defsMap.size();
+ defsMap.put(source, symbolId);
+
+ DocumentBuilder db = dbf.newDocumentBuilder();
+
+ Document doc = db.parse(new ByteArrayInputStream(source.getBytes("UTF-8")));
+ Node node = doc.getDocumentElement();
+ Node localNode = owner.importNode(node, true);
+
+ if (localNode instanceof Element) {
+ Element g = generatorContext.getDOMFactory().createElementNS(SVG_NAMESPACE_URI, SVG_G_TAG);
+ Element e = (Element)localNode;
+ e.setAttribute("id", symbolId);
+ g.appendChild(localNode);
+ return g;
+ } else {
+ return null;
+ }
+ } else {
+ Element element = owner.createElement("use");
+ element.setAttribute("xlink:href", "#" + symbolId);
+ return element;
+ }
+ }
+ } catch (UnsupportedEncodingException e) {
+ Logger.defaultLogError(e);
+ return null;
+ } catch (IOException e) {
+ Logger.defaultLogError(e);
+ return null;
+ } catch (ParserConfigurationException e) {
+ Logger.defaultLogError(e);
+ return null;
+ } catch (SAXException e) {
+ Logger.defaultLogError(e);
+ return null;
+ }
+ } else {
+ return super.toSVG(shape);
+ }
+
+ }
+
+}
import org.simantics.scenegraph.ScenegraphUtils;
import org.simantics.scenegraph.g2d.G2DNode;
import org.simantics.scenegraph.g2d.G2DPDFRenderingHints;
-import org.simantics.scenegraph.g2d.G2DPDFRenderingHints.TextRenderingMode;
+import org.simantics.scenegraph.g2d.G2DRenderingHints;
+import org.simantics.scenegraph.g2d.G2DRenderingHints.TextRenderingMode;
import org.simantics.scenegraph.g2d.events.Event;
import org.simantics.scenegraph.g2d.events.EventTypes;
import org.simantics.scenegraph.g2d.events.KeyEvent.KeyPressedEvent;
// PDF
PdfWriter writer = (PdfWriter) g.getRenderingHint(G2DPDFRenderingHints.KEY_PDF_WRITER);
- TextRenderingMode renderingMode = (TextRenderingMode) g.getRenderingHint(G2DPDFRenderingHints.KEY_TEXT_RENDERING_MODE);
+ TextRenderingMode renderingMode = (TextRenderingMode) g.getRenderingHint(G2DRenderingHints.KEY_TEXT_RENDERING_MODE);
boolean renderAsText = writer != null || renderingMode == TextRenderingMode.AS_TEXT;
/// PDF
}
};
- /**
- * If this hint is not specified, the default interpretation should be
- * {@value #AS_PATHS}.
- *
- * @since 1.31.0
- */
- public static enum TextRenderingMode {
- AS_PATHS,
- AS_TEXT
- }
-
- /**
- * A rendering hint for telling text rendering Simantics G2D scene graph node
- * implementations how to render the text: as text or paths.
- *
- * @since 1.31.0
- */
- public static final Key KEY_TEXT_RENDERING_MODE = new Key(2004) {
- @Override
- public boolean isCompatibleValue(Object val) {
- return val instanceof TextRenderingMode;
- }
- };
-
}
\ No newline at end of file
}
};
+ /**
+ * If this hint is not specified, the default interpretation should be
+ * {@value #AS_PATHS}.
+ *
+ * @since 1.31.0
+ */
+ public static enum TextRenderingMode {
+ AS_PATHS,
+ AS_TEXT
+ }
+
+ /**
+ * A rendering hint for telling text rendering Simantics G2D scene graph node
+ * implementations how to render the text: as text or paths.
+ *
+ * @since 1.31.0
+ */
+ public static final Key KEY_TEXT_RENDERING_MODE = new Key(2004) {
+ @Override
+ public boolean isCompatibleValue(Object val) {
+ return val instanceof TextRenderingMode;
+ }
+ };
+
+ /**
+ * Instead of rendering SVGNode using SVG Salamander pass it to G2D as SVGPassthruShape in String format.
+ *
+ * @since 1.31.0
+ */
+ public static final Key KEY_SVG_PASSTHRU = new Key(2005) {
+ @Override
+ public boolean isCompatibleValue(Object val) {
+ return val instanceof Boolean;
+ }
+ };
+
}
\ No newline at end of file
import org.simantics.scenegraph.g2d.G2DNode;
import org.simantics.scenegraph.g2d.G2DPDFRenderingHints;
-import org.simantics.scenegraph.g2d.G2DPDFRenderingHints.TextRenderingMode;
+import org.simantics.scenegraph.g2d.G2DRenderingHints;
+import org.simantics.scenegraph.g2d.G2DRenderingHints.TextRenderingMode;
import org.simantics.scenegraph.utils.GeometryUtils;
-import com.lowagie.text.pdf.PdfWriter;
-
public class FlagNode extends G2DNode {
private static final long serialVersionUID = -1716729504104107151L;
double y = startY;
double textAreaWidth = textArea.getWidth() * gScaleRecip;
boolean renderAsText = g.getRenderingHint(G2DPDFRenderingHints.KEY_PDF_WRITER) != null
- || g.getRenderingHint(G2DPDFRenderingHints.KEY_TEXT_RENDERING_MODE) == TextRenderingMode.AS_TEXT;
+ || g.getRenderingHint(G2DRenderingHints.KEY_TEXT_RENDERING_MODE) == TextRenderingMode.AS_TEXT;
for (int i = 0; i < flagText.length; ++i) {
//String line = flagText[i];
import java.awt.geom.Rectangle2D;
import java.io.ByteArrayInputStream;
import java.io.IOException;
+import java.io.StringReader;
+import java.io.StringWriter;
import java.lang.ref.WeakReference;
import java.math.BigInteger;
import java.net.URI;
import java.util.Set;
import java.util.WeakHashMap;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+
import org.simantics.scenegraph.ExportableWidget.RasterOutputWidget;
import org.simantics.scenegraph.LoaderNode;
import org.simantics.scenegraph.ScenegraphUtils;
import org.simantics.scenegraph.g2d.G2DNode;
+import org.simantics.scenegraph.g2d.G2DRenderingHints;
import org.simantics.scenegraph.utils.BufferedImage;
import org.simantics.scenegraph.utils.G2DUtils;
import org.simantics.scenegraph.utils.InitValueSupport;
import org.simantics.scenegraph.utils.MipMapBufferedImage;
import org.simantics.scenegraph.utils.MipMapVRamBufferedImage;
+import org.simantics.scenegraph.utils.SVGPassthruShape;
import org.simantics.scenegraph.utils.VRamBufferedImage;
import org.simantics.scl.runtime.function.Function1;
import org.simantics.scl.runtime.function.Function2;
import org.simantics.utils.threads.AWTThread;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NodeList;
+import org.xml.sax.InputSource;
import com.kitfox.svg.RenderableElement;
import com.kitfox.svg.SVGCache;
if (data == null)
return; // Not initialized
- if (!data.equals(documentCache) || diagramCache == null || buffer == null)
- initBuffer(g2d);
-
AffineTransform ot = null;
if (!transform.isIdentity()) {
ot = g2d.getTransform();
g2d.transform(transform);
}
- if (buffer != null)
- buffer.paint(g2d);
+ if (g2d.getRenderingHint(G2DRenderingHints.KEY_SVG_PASSTHRU) == Boolean.TRUE) {
+ SVGPassthruShape.resetG2D(g2d);
+ String svg = assignments.isEmpty() ? data : applyAssigments(data, assignments);
+ if (svg != null) {
+ g2d.fill(new SVGPassthruShape(svg));
+ }
+ } else {
+ if (!data.equals(documentCache) || diagramCache == null || buffer == null)
+ initBuffer(g2d);
+
+ if (buffer != null)
+ buffer.paint(g2d);
+ }
if (ot != null)
g2d.setTransform(ot);
return dataHash;
}
+ private static DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
+ static {
+ dbf.setValidating(false);
+ try {
+ dbf.setFeature("http://xml.org/sax/features/namespaces", false);
+ dbf.setFeature("http://xml.org/sax/features/validation", false);
+ dbf.setFeature("http://apache.org/xml/features/nonvalidating/load-dtd-grammar", false);
+ dbf.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
+ } catch (ParserConfigurationException e) {
+ // Nothing to do
+ }
+ }
+
+ // Notice: Remember to change both implementations of applyAssigments when modifying the functionality.
+ protected static String applyAssigments(String svg, List<SVGNodeAssignment> assignments) {
+ try {
+ DocumentBuilder db = dbf.newDocumentBuilder();
+ Document doc = db.parse(new InputSource(new StringReader(svg)));
+
+ NodeList entries = doc.getElementsByTagName("*");
+ for (int i=0; i<entries.getLength(); i++) {
+ Element element = (Element) entries.item(i);
+ if (element.hasAttribute("id")) {
+ element.setIdAttribute("id", true);
+ }
+ }
+ for (SVGNodeAssignment ass : assignments) {
+ Element e = doc.getElementById(ass.elementId);
+ if (e != null) {
+ if ("$text".equals(ass.attributeNameOrId)) {
+ if (e.getTagName().equals("tspan")) {
+ if (ass.value.trim().isEmpty()) {
+ e.setTextContent("-");
+ } else {
+ e.setTextContent(ass.value);
+ }
+ }
+ } else if (ass.attributeNameOrId.startsWith("#")) {
+ e.setAttribute(ass.attributeNameOrId.substring(1), ass.value);
+ } else {
+ e.setAttribute(ass.attributeNameOrId, ass.value);
+ }
+ }
+ }
+
+ DOMSource domSource = new DOMSource(doc);
+ StringWriter writer = new StringWriter();
+ StreamResult result = new StreamResult(writer);
+ TransformerFactory tf = TransformerFactory.newInstance();
+ Transformer transformer = tf.newTransformer();
+ transformer.transform(domSource, result);
+ return writer.toString();
+
+ } catch (Exception e) {
+ return null;
+ }
+ }
+
+ // Notice: Remember to change both implementations of applyAssigments when modifying the functionality.
protected boolean applyAssignments(SVGDiagram diagram, List<SVGNodeAssignment> assignments) throws SVGException {
if (assignments.isEmpty())
return false;
--- /dev/null
+package org.simantics.scenegraph.utils;
+
+import java.awt.BasicStroke;
+import java.awt.Color;
+import java.awt.Font;
+import java.awt.Graphics2D;
+import java.awt.Rectangle;
+import java.awt.Shape;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.PathIterator;
+import java.awt.geom.Point2D;
+import java.awt.geom.Rectangle2D;
+
+public class SVGPassthruShape implements Shape {
+
+ private String source;
+ public final static Font DEFAULT_FONT = Font.decode(null);
+
+ public SVGPassthruShape(String source) {
+ this.source = source;
+ }
+
+ public String getSource() {
+ return source;
+ }
+
+ public static void resetG2D(Graphics2D g2d) {
+ g2d.setColor(new Color(0, 0, 0));
+ g2d.setBackground(new Color(0, 0, 0));
+ g2d.setStroke(new BasicStroke());
+ g2d.setFont(DEFAULT_FONT);
+ QualityHints.HIGH_QUALITY_HINTS.setQuality(g2d);
+ }
+
+ @Override
+ public Rectangle getBounds() {
+ return new Rectangle();
+ }
+
+ @Override
+ public Rectangle2D getBounds2D() {
+ return new Rectangle2D.Double();
+ }
+
+ @Override
+ public boolean contains(double x, double y) {
+ return false;
+ }
+
+ @Override
+ public boolean contains(Point2D p) {
+ return false;
+ }
+
+ @Override
+ public boolean intersects(double x, double y, double w, double h) {
+ return false;
+ }
+
+ @Override
+ public boolean intersects(Rectangle2D r) {
+ return false;
+ }
+
+ @Override
+ public boolean contains(double x, double y, double w, double h) {
+ return false;
+ }
+
+ @Override
+ public boolean contains(Rectangle2D r) {
+ return false;
+ }
+
+ @Override
+ public PathIterator getPathIterator(AffineTransform at) {
+ return new EmptyPathIterator();
+ }
+
+ @Override
+ public PathIterator getPathIterator(AffineTransform at, double flatness) {
+ return new EmptyPathIterator();
+ }
+
+ private class EmptyPathIterator implements PathIterator {
+
+ @Override
+ public void next() {
+
+ }
+
+ @Override
+ public boolean isDone() {
+ return true;
+ }
+
+ @Override
+ public int getWindingRule() {
+ return 0;
+ }
+
+ @Override
+ public int currentSegment(double[] coords) {
+ return 0;
+ }
+
+ @Override
+ public int currentSegment(float[] coords) {
+ return 0;
+ }
+ };
+
+}