2010-11-15 20:35:36 +00:00
|
|
|
/* ========================================================================
|
|
|
|
* PlantUML : a free UML diagram generator
|
|
|
|
* ========================================================================
|
|
|
|
*
|
2019-01-16 18:34:41 +00:00
|
|
|
* (C) Copyright 2009-2020, Arnaud Roques
|
2010-11-15 20:35:36 +00:00
|
|
|
*
|
2016-03-06 16:47:34 +00:00
|
|
|
* Project Info: http://plantuml.com
|
2010-11-15 20:35:36 +00:00
|
|
|
*
|
2017-03-15 19:13:31 +00:00
|
|
|
* If you like this project or if you find it useful, you can support us at:
|
|
|
|
*
|
|
|
|
* http://plantuml.com/patreon (only 1$ per month!)
|
|
|
|
* http://plantuml.com/paypal
|
|
|
|
*
|
2010-11-15 20:35:36 +00:00
|
|
|
* This file is part of PlantUML.
|
|
|
|
*
|
|
|
|
* PlantUML is free software; you can redistribute it and/or modify it
|
|
|
|
* under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* PlantUML distributed in the hope that it will be useful, but
|
|
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
2013-12-10 19:36:50 +00:00
|
|
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
2010-11-15 20:35:36 +00:00
|
|
|
* License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public
|
|
|
|
* License along with this library; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
|
|
|
|
* USA.
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* Original Author: Arnaud Roques
|
|
|
|
*
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
package net.sourceforge.plantuml.svg;
|
|
|
|
|
2017-06-05 11:27:21 +00:00
|
|
|
import java.awt.geom.Dimension2D;
|
2013-12-10 19:36:50 +00:00
|
|
|
import java.awt.image.BufferedImage;
|
2010-11-15 20:35:36 +00:00
|
|
|
import java.io.ByteArrayOutputStream;
|
|
|
|
import java.io.IOException;
|
|
|
|
import java.io.OutputStream;
|
2013-12-10 19:36:50 +00:00
|
|
|
import java.util.ArrayList;
|
2010-11-15 20:35:36 +00:00
|
|
|
import java.util.Arrays;
|
|
|
|
import java.util.HashMap;
|
|
|
|
import java.util.List;
|
|
|
|
import java.util.Map;
|
2020-05-17 21:15:50 +00:00
|
|
|
import java.util.regex.Matcher;
|
|
|
|
import java.util.regex.Pattern;
|
2010-11-15 20:35:36 +00:00
|
|
|
|
|
|
|
import javax.xml.parsers.DocumentBuilder;
|
|
|
|
import javax.xml.parsers.DocumentBuilderFactory;
|
|
|
|
import javax.xml.parsers.ParserConfigurationException;
|
|
|
|
import javax.xml.transform.OutputKeys;
|
|
|
|
import javax.xml.transform.Transformer;
|
|
|
|
import javax.xml.transform.TransformerException;
|
|
|
|
import javax.xml.transform.TransformerFactory;
|
|
|
|
import javax.xml.transform.dom.DOMSource;
|
|
|
|
import javax.xml.transform.stream.StreamResult;
|
|
|
|
|
2020-04-19 16:04:39 +00:00
|
|
|
import org.w3c.dom.CDATASection;
|
|
|
|
import org.w3c.dom.Comment;
|
|
|
|
import org.w3c.dom.Document;
|
|
|
|
import org.w3c.dom.Element;
|
|
|
|
|
2011-08-08 17:48:29 +00:00
|
|
|
import net.sourceforge.plantuml.Log;
|
2019-09-14 18:12:04 +00:00
|
|
|
import net.sourceforge.plantuml.SignatureUtils;
|
2017-04-05 17:37:42 +00:00
|
|
|
import net.sourceforge.plantuml.SvgString;
|
2013-12-10 19:36:50 +00:00
|
|
|
import net.sourceforge.plantuml.code.Base64Coder;
|
2020-05-30 15:20:23 +00:00
|
|
|
import net.sourceforge.plantuml.security.ImageIO;
|
|
|
|
import net.sourceforge.plantuml.security.SecurityUtils;
|
2019-03-29 22:14:07 +00:00
|
|
|
import net.sourceforge.plantuml.tikz.TikzGraphics;
|
2010-11-15 20:35:36 +00:00
|
|
|
import net.sourceforge.plantuml.ugraphic.UPath;
|
|
|
|
import net.sourceforge.plantuml.ugraphic.USegment;
|
2011-04-19 16:50:40 +00:00
|
|
|
import net.sourceforge.plantuml.ugraphic.USegmentType;
|
2020-03-18 10:50:02 +00:00
|
|
|
import net.sourceforge.plantuml.ugraphic.color.ColorMapper;
|
|
|
|
import net.sourceforge.plantuml.ugraphic.color.HColorGradient;
|
2010-11-15 20:35:36 +00:00
|
|
|
|
|
|
|
public class SvgGraphics {
|
|
|
|
|
|
|
|
// http://tutorials.jenkov.com/svg/index.html
|
|
|
|
// http://www.svgbasics.com/
|
|
|
|
// http://apike.ca/prog_svg_text.html
|
|
|
|
// http://www.w3.org/TR/SVG11/shapes.html
|
|
|
|
// http://en.wikipedia.org/wiki/Scalable_Vector_Graphics
|
|
|
|
|
2013-12-10 19:36:50 +00:00
|
|
|
// Animation:
|
|
|
|
// http://srufaculty.sru.edu/david.dailey/svg/
|
|
|
|
// Shadow:
|
|
|
|
// http://www.svgbasics.com/filters3.html
|
|
|
|
// http://www.w3schools.com/svg/svg_feoffset.asp
|
|
|
|
// http://www.adobe.com/svg/demos/samples.html
|
|
|
|
|
2019-09-22 17:20:16 +00:00
|
|
|
private static final String XLINK_TITLE1 = "title";
|
|
|
|
private static final String XLINK_TITLE2 = "xlink:title";
|
|
|
|
private static final String XLINK_HREF1 = "href";
|
|
|
|
private static final String XLINK_HREF2 = "xlink:href";
|
2019-09-14 18:12:04 +00:00
|
|
|
|
2010-11-15 20:35:36 +00:00
|
|
|
final private Document document;
|
2011-04-19 16:50:40 +00:00
|
|
|
final private Element root;
|
2010-11-15 20:35:36 +00:00
|
|
|
final private Element defs;
|
2011-04-19 16:50:40 +00:00
|
|
|
final private Element gRoot;
|
2010-11-15 20:35:36 +00:00
|
|
|
|
|
|
|
private String fill = "black";
|
|
|
|
private String stroke = "black";
|
2013-12-10 19:36:50 +00:00
|
|
|
private String strokeWidth;
|
2010-11-15 20:35:36 +00:00
|
|
|
private String strokeDasharray = null;
|
2011-04-19 16:50:40 +00:00
|
|
|
private final String backcolor;
|
|
|
|
|
|
|
|
private int maxX = 10;
|
|
|
|
private int maxY = 10;
|
|
|
|
|
2019-11-03 17:40:03 +00:00
|
|
|
private final String preserveAspectRatio;
|
|
|
|
|
2013-12-10 19:36:50 +00:00
|
|
|
private final double scale;
|
2016-07-04 19:06:50 +00:00
|
|
|
private final String filterUid;
|
|
|
|
private final String shadowId;
|
2016-09-29 19:51:18 +00:00
|
|
|
private final String gradientId;
|
2018-03-09 21:37:34 +00:00
|
|
|
private final boolean svgDimensionStyle;
|
2013-12-10 19:36:50 +00:00
|
|
|
|
2011-04-19 16:50:40 +00:00
|
|
|
final protected void ensureVisible(double x, double y) {
|
|
|
|
if (x > maxX) {
|
|
|
|
maxX = (int) (x + 1);
|
|
|
|
}
|
|
|
|
if (y > maxY) {
|
|
|
|
maxY = (int) (y + 1);
|
|
|
|
}
|
|
|
|
}
|
2010-11-15 20:35:36 +00:00
|
|
|
|
2019-11-03 17:40:03 +00:00
|
|
|
public SvgGraphics(boolean svgDimensionStyle, Dimension2D minDim, double scale, String hover, long seed,
|
|
|
|
String preserveAspectRatio) {
|
|
|
|
this(svgDimensionStyle, minDim, null, scale, hover, seed, preserveAspectRatio);
|
2010-11-15 20:35:36 +00:00
|
|
|
}
|
|
|
|
|
2018-03-09 21:37:34 +00:00
|
|
|
public SvgGraphics(boolean svgDimensionStyle, Dimension2D minDim, String backcolor, double scale, String hover,
|
2019-11-03 17:40:03 +00:00
|
|
|
long seed, String preserveAspectRatio) {
|
2010-11-15 20:35:36 +00:00
|
|
|
try {
|
2018-03-09 21:37:34 +00:00
|
|
|
this.svgDimensionStyle = svgDimensionStyle;
|
2013-12-10 19:36:50 +00:00
|
|
|
this.scale = scale;
|
2011-04-19 16:50:40 +00:00
|
|
|
this.document = getDocument();
|
|
|
|
this.backcolor = backcolor;
|
2019-11-03 17:40:03 +00:00
|
|
|
this.preserveAspectRatio = preserveAspectRatio;
|
2017-06-05 11:27:21 +00:00
|
|
|
ensureVisible(minDim.getWidth(), minDim.getHeight());
|
2010-11-15 20:35:36 +00:00
|
|
|
|
2011-04-19 16:50:40 +00:00
|
|
|
this.root = getRootNode();
|
2010-11-15 20:35:36 +00:00
|
|
|
|
|
|
|
// Create a node named defs, which will be the parent
|
|
|
|
// for a pair of linear gradient definitions.
|
|
|
|
defs = simpleElement("defs");
|
2011-04-19 16:50:40 +00:00
|
|
|
gRoot = simpleElement("g");
|
2013-12-10 19:36:50 +00:00
|
|
|
strokeWidth = "" + scale;
|
2017-04-19 18:30:16 +00:00
|
|
|
this.filterUid = "b" + getSeed(seed);
|
|
|
|
this.shadowId = "f" + getSeed(seed);
|
|
|
|
this.gradientId = "g" + getSeed(seed);
|
2017-02-15 21:34:36 +00:00
|
|
|
if (hover != null) {
|
|
|
|
defs.appendChild(getPathHover(hover));
|
|
|
|
}
|
2010-11-15 20:35:36 +00:00
|
|
|
} catch (ParserConfigurationException e) {
|
|
|
|
e.printStackTrace();
|
|
|
|
throw new IllegalStateException(e);
|
|
|
|
}
|
2013-12-10 19:36:50 +00:00
|
|
|
}
|
2010-11-15 20:35:36 +00:00
|
|
|
|
2017-02-15 21:34:36 +00:00
|
|
|
private Element getPathHover(String hover) {
|
|
|
|
final Element style = simpleElement("style");
|
|
|
|
final CDATASection cdata = document.createCDATASection("path:hover { stroke: " + hover + " !important;}");
|
|
|
|
style.setAttribute("type", "text/css");
|
|
|
|
style.appendChild(cdata);
|
|
|
|
return style;
|
|
|
|
}
|
|
|
|
|
2017-04-19 18:30:16 +00:00
|
|
|
private static String getSeed(final long seed) {
|
|
|
|
return Long.toString(Math.abs(seed), 36);
|
2016-07-25 19:25:28 +00:00
|
|
|
}
|
|
|
|
|
2013-12-10 19:36:50 +00:00
|
|
|
private Element pendingBackground;
|
|
|
|
|
2020-03-18 10:50:02 +00:00
|
|
|
public void paintBackcolorGradient(ColorMapper mapper, HColorGradient gr) {
|
2020-05-17 21:15:50 +00:00
|
|
|
final String id = createSvgGradient(mapper.toHtml(gr.getColor1()), mapper.toHtml(gr.getColor2()),
|
|
|
|
gr.getPolicy());
|
2013-12-10 19:36:50 +00:00
|
|
|
setFillColor("url(#" + id + ")");
|
|
|
|
setStrokeColor(null);
|
|
|
|
pendingBackground = createRectangleInternal(0, 0, 0, 0);
|
|
|
|
getG().appendChild(pendingBackground);
|
2010-11-15 20:35:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// This method returns a reference to a simple XML
|
|
|
|
// element node that has no attributes.
|
|
|
|
private Element simpleElement(String type) {
|
|
|
|
final Element theElement = (Element) document.createElement(type);
|
2011-04-19 16:50:40 +00:00
|
|
|
root.appendChild(theElement);
|
2010-11-15 20:35:36 +00:00
|
|
|
return theElement;
|
|
|
|
}
|
|
|
|
|
|
|
|
private Document getDocument() throws ParserConfigurationException {
|
|
|
|
final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
|
|
|
|
|
|
|
|
final DocumentBuilder builder = factory.newDocumentBuilder();
|
|
|
|
final Document document = builder.newDocument();
|
|
|
|
document.setXmlStandalone(true);
|
|
|
|
return document;
|
|
|
|
}
|
|
|
|
|
|
|
|
// This method returns a reference to a root node that
|
|
|
|
// has already been appended to the document.
|
2011-04-19 16:50:40 +00:00
|
|
|
private Element getRootNode() {
|
2010-11-15 20:35:36 +00:00
|
|
|
// Create the root node named svg and append it to
|
|
|
|
// the document.
|
|
|
|
final Element svg = (Element) document.createElement("svg");
|
|
|
|
document.appendChild(svg);
|
|
|
|
|
|
|
|
// Set some attributes on the root node that are
|
|
|
|
// required for proper rendering. Note that the
|
|
|
|
// approach used here is somewhat different from the
|
|
|
|
// approach used in the earlier program named Svg01,
|
|
|
|
// particularly with regard to the style.
|
|
|
|
svg.setAttribute("xmlns", "http://www.w3.org/2000/svg");
|
2013-12-10 19:36:50 +00:00
|
|
|
svg.setAttribute("xmlns:xlink", "http://www.w3.org/1999/xlink");
|
2010-11-15 20:35:36 +00:00
|
|
|
svg.setAttribute("version", "1.1");
|
|
|
|
|
|
|
|
return svg;
|
|
|
|
}
|
|
|
|
|
2013-12-10 19:36:50 +00:00
|
|
|
public void svgEllipse(double x, double y, double xRadius, double yRadius, double deltaShadow) {
|
|
|
|
manageShadow(deltaShadow);
|
|
|
|
if (hidden == false) {
|
|
|
|
final Element elt = (Element) document.createElement("ellipse");
|
|
|
|
elt.setAttribute("cx", format(x));
|
|
|
|
elt.setAttribute("cy", format(y));
|
|
|
|
elt.setAttribute("rx", format(xRadius));
|
|
|
|
elt.setAttribute("ry", format(yRadius));
|
|
|
|
elt.setAttribute("fill", fill);
|
|
|
|
elt.setAttribute("style", getStyle());
|
2016-07-04 19:06:50 +00:00
|
|
|
addFilterShadowId(elt, deltaShadow);
|
2013-12-10 19:36:50 +00:00
|
|
|
getG().appendChild(elt);
|
|
|
|
}
|
|
|
|
ensureVisible(x + xRadius + deltaShadow * 2, y + yRadius + deltaShadow * 2);
|
|
|
|
}
|
|
|
|
|
|
|
|
public void svgArcEllipse(double rx, double ry, double x1, double y1, double x2, double y2) {
|
|
|
|
if (hidden == false) {
|
|
|
|
final String path = "M" + format(x1) + "," + format(y1) + " A" + format(rx) + "," + format(ry) + " 0 0 0 "
|
|
|
|
+ format(x2) + " " + format(y2);
|
|
|
|
final Element elt = (Element) document.createElement("path");
|
|
|
|
elt.setAttribute("d", path);
|
|
|
|
elt.setAttribute("fill", fill);
|
|
|
|
elt.setAttribute("style", getStyle());
|
|
|
|
getG().appendChild(elt);
|
|
|
|
}
|
|
|
|
ensureVisible(x1, y1);
|
|
|
|
ensureVisible(x2, y2);
|
2010-11-15 20:35:36 +00:00
|
|
|
}
|
|
|
|
|
2013-12-10 19:36:50 +00:00
|
|
|
private Map<List<Object>, String> gradients = new HashMap<List<Object>, String>();
|
2010-11-15 20:35:36 +00:00
|
|
|
|
2013-12-10 19:36:50 +00:00
|
|
|
public String createSvgGradient(String color1, String color2, char policy) {
|
|
|
|
final List<Object> key = Arrays.asList((Object) color1, color2, policy);
|
2010-11-15 20:35:36 +00:00
|
|
|
String id = gradients.get(key);
|
|
|
|
if (id == null) {
|
|
|
|
final Element elt = (Element) document.createElement("linearGradient");
|
2013-12-10 19:36:50 +00:00
|
|
|
if (policy == '|') {
|
|
|
|
elt.setAttribute("x1", "0%");
|
|
|
|
elt.setAttribute("y1", "50%");
|
|
|
|
elt.setAttribute("x2", "100%");
|
|
|
|
elt.setAttribute("y2", "50%");
|
|
|
|
} else if (policy == '\\') {
|
|
|
|
elt.setAttribute("x1", "0%");
|
|
|
|
elt.setAttribute("y1", "100%");
|
|
|
|
elt.setAttribute("x2", "100%");
|
|
|
|
elt.setAttribute("y2", "0%");
|
|
|
|
} else if (policy == '-') {
|
|
|
|
elt.setAttribute("x1", "50%");
|
|
|
|
elt.setAttribute("y1", "0%");
|
|
|
|
elt.setAttribute("x2", "50%");
|
|
|
|
elt.setAttribute("y2", "100%");
|
|
|
|
} else {
|
|
|
|
elt.setAttribute("x1", "0%");
|
|
|
|
elt.setAttribute("y1", "0%");
|
|
|
|
elt.setAttribute("x2", "100%");
|
|
|
|
elt.setAttribute("y2", "100%");
|
|
|
|
}
|
2016-09-29 19:51:18 +00:00
|
|
|
id = gradientId + gradients.size();
|
2010-11-15 20:35:36 +00:00
|
|
|
gradients.put(key, id);
|
|
|
|
elt.setAttribute("id", id);
|
|
|
|
|
|
|
|
final Element stop1 = (Element) document.createElement("stop");
|
|
|
|
stop1.setAttribute("stop-color", color1);
|
|
|
|
stop1.setAttribute("offset", "0%");
|
|
|
|
final Element stop2 = (Element) document.createElement("stop");
|
|
|
|
stop2.setAttribute("stop-color", color2);
|
|
|
|
stop2.setAttribute("offset", "100%");
|
|
|
|
|
|
|
|
elt.appendChild(stop1);
|
|
|
|
elt.appendChild(stop2);
|
|
|
|
defs.appendChild(elt);
|
|
|
|
}
|
|
|
|
return id;
|
|
|
|
}
|
|
|
|
|
|
|
|
public final void setFillColor(String fill) {
|
|
|
|
this.fill = fill == null ? "none" : fill;
|
|
|
|
}
|
|
|
|
|
|
|
|
public final void setStrokeColor(String stroke) {
|
2015-04-07 18:18:37 +00:00
|
|
|
this.stroke = stroke == null ? "none" : stroke;
|
2010-11-15 20:35:36 +00:00
|
|
|
}
|
|
|
|
|
2013-12-10 19:36:50 +00:00
|
|
|
public final void setStrokeWidth(double strokeWidth, String strokeDasharray) {
|
|
|
|
this.strokeWidth = "" + (scale * strokeWidth);
|
2010-11-15 20:35:36 +00:00
|
|
|
this.strokeDasharray = strokeDasharray;
|
|
|
|
}
|
|
|
|
|
2017-12-11 21:02:10 +00:00
|
|
|
private final List<Element> pendingAction = new ArrayList<Element>();
|
2011-04-19 16:50:40 +00:00
|
|
|
|
|
|
|
public final Element getG() {
|
2017-12-11 21:02:10 +00:00
|
|
|
if (pendingAction.size() == 0) {
|
2011-04-19 16:50:40 +00:00
|
|
|
return gRoot;
|
|
|
|
}
|
2017-12-11 21:02:10 +00:00
|
|
|
return pendingAction.get(0);
|
2011-04-19 16:50:40 +00:00
|
|
|
}
|
|
|
|
|
2017-04-26 17:48:37 +00:00
|
|
|
public void svgRectangle(double x, double y, double width, double height, double rx, double ry, double deltaShadow,
|
|
|
|
String id) {
|
2015-04-07 18:18:37 +00:00
|
|
|
if (height <= 0 || width <= 0) {
|
2018-03-09 21:37:34 +00:00
|
|
|
return;
|
|
|
|
// To be restored when Teoz will be finished
|
|
|
|
// throw new IllegalArgumentException();
|
2015-04-07 18:18:37 +00:00
|
|
|
}
|
2013-12-10 19:36:50 +00:00
|
|
|
manageShadow(deltaShadow);
|
|
|
|
if (hidden == false) {
|
|
|
|
final Element elt = createRectangleInternal(x, y, width, height);
|
2016-07-04 19:06:50 +00:00
|
|
|
addFilterShadowId(elt, deltaShadow);
|
2013-12-10 19:36:50 +00:00
|
|
|
if (rx > 0 && ry > 0) {
|
|
|
|
elt.setAttribute("rx", format(rx));
|
|
|
|
elt.setAttribute("ry", format(ry));
|
|
|
|
}
|
2017-04-26 17:48:37 +00:00
|
|
|
if (id != null) {
|
|
|
|
elt.setAttribute("id", id);
|
|
|
|
}
|
2013-12-10 19:36:50 +00:00
|
|
|
getG().appendChild(elt);
|
|
|
|
}
|
|
|
|
ensureVisible(x + width + 2 * deltaShadow, y + height + 2 * deltaShadow);
|
|
|
|
}
|
2011-04-19 16:50:40 +00:00
|
|
|
|
2013-12-10 19:36:50 +00:00
|
|
|
private Element createRectangleInternal(double x, double y, double width, double height) {
|
2010-11-15 20:35:36 +00:00
|
|
|
final Element elt = (Element) document.createElement("rect");
|
2013-12-10 19:36:50 +00:00
|
|
|
elt.setAttribute("x", format(x));
|
|
|
|
elt.setAttribute("y", format(y));
|
|
|
|
elt.setAttribute("width", format(width));
|
|
|
|
elt.setAttribute("height", format(height));
|
2010-11-15 20:35:36 +00:00
|
|
|
elt.setAttribute("fill", fill);
|
|
|
|
elt.setAttribute("style", getStyle());
|
2013-12-10 19:36:50 +00:00
|
|
|
return elt;
|
|
|
|
}
|
|
|
|
|
|
|
|
public void svgLine(double x1, double y1, double x2, double y2, double deltaShadow) {
|
|
|
|
manageShadow(deltaShadow);
|
|
|
|
if (hidden == false) {
|
|
|
|
final Element elt = (Element) document.createElement("line");
|
|
|
|
elt.setAttribute("x1", format(x1));
|
|
|
|
elt.setAttribute("y1", format(y1));
|
|
|
|
elt.setAttribute("x2", format(x2));
|
|
|
|
elt.setAttribute("y2", format(y2));
|
|
|
|
elt.setAttribute("style", getStyle());
|
2016-07-04 19:06:50 +00:00
|
|
|
addFilterShadowId(elt, deltaShadow);
|
2013-12-10 19:36:50 +00:00
|
|
|
getG().appendChild(elt);
|
2010-11-15 20:35:36 +00:00
|
|
|
}
|
2013-12-10 19:36:50 +00:00
|
|
|
ensureVisible(x1 + 2 * deltaShadow, y1 + 2 * deltaShadow);
|
|
|
|
ensureVisible(x2 + 2 * deltaShadow, y2 + 2 * deltaShadow);
|
2010-11-15 20:35:36 +00:00
|
|
|
}
|
|
|
|
|
2013-12-10 19:36:50 +00:00
|
|
|
private String getStyle() {
|
2015-04-07 18:18:37 +00:00
|
|
|
return getStyleInternal(stroke, strokeWidth, strokeDasharray);
|
2010-11-15 20:35:36 +00:00
|
|
|
}
|
|
|
|
|
2015-04-07 18:18:37 +00:00
|
|
|
private static String getStyleInternal(String color, String strokeWidth, String strokeDasharray) {
|
|
|
|
final StringBuilder style = new StringBuilder("stroke: " + color + "; stroke-width: " + strokeWidth + ";");
|
2010-11-15 20:35:36 +00:00
|
|
|
if (strokeDasharray != null) {
|
|
|
|
style.append(" stroke-dasharray: " + strokeDasharray + ";");
|
|
|
|
}
|
|
|
|
return style.toString();
|
|
|
|
}
|
|
|
|
|
2013-12-10 19:36:50 +00:00
|
|
|
public void svgPolygon(double deltaShadow, double... points) {
|
2019-02-09 21:56:24 +00:00
|
|
|
assert points.length % 2 == 0;
|
2013-12-10 19:36:50 +00:00
|
|
|
manageShadow(deltaShadow);
|
|
|
|
if (hidden == false) {
|
|
|
|
final Element elt = (Element) document.createElement("polygon");
|
|
|
|
final StringBuilder sb = new StringBuilder();
|
|
|
|
for (double coord : points) {
|
|
|
|
if (sb.length() > 0) {
|
|
|
|
sb.append(",");
|
|
|
|
}
|
|
|
|
sb.append(format(coord));
|
|
|
|
}
|
|
|
|
elt.setAttribute("points", sb.toString());
|
|
|
|
elt.setAttribute("fill", fill);
|
|
|
|
elt.setAttribute("style", getStyle());
|
2016-07-04 19:06:50 +00:00
|
|
|
addFilterShadowId(elt, deltaShadow);
|
2013-12-10 19:36:50 +00:00
|
|
|
getG().appendChild(elt);
|
2010-11-15 20:35:36 +00:00
|
|
|
}
|
2011-04-19 16:50:40 +00:00
|
|
|
|
|
|
|
for (int i = 0; i < points.length; i += 2) {
|
2013-12-10 19:36:50 +00:00
|
|
|
ensureVisible(points[i] + 2 * deltaShadow, points[i + 1] + 2 * deltaShadow);
|
2011-04-19 16:50:40 +00:00
|
|
|
}
|
|
|
|
|
2010-11-15 20:35:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public void text(String text, double x, double y, String fontFamily, int fontSize, String fontWeight,
|
2015-05-31 18:56:03 +00:00
|
|
|
String fontStyle, String textDecoration, double textLength, Map<String, String> attributes,
|
|
|
|
String textBackColor) {
|
2013-12-10 19:36:50 +00:00
|
|
|
if (hidden == false) {
|
|
|
|
final Element elt = (Element) document.createElement("text");
|
2016-11-18 21:12:09 +00:00
|
|
|
// required for web-kit based browsers
|
|
|
|
// elt.setAttribute("text-rendering", "geometricPrecision");
|
2013-12-10 19:36:50 +00:00
|
|
|
elt.setAttribute("x", format(x));
|
|
|
|
elt.setAttribute("y", format(y));
|
|
|
|
elt.setAttribute("fill", fill);
|
|
|
|
elt.setAttribute("font-size", format(fontSize));
|
|
|
|
// elt.setAttribute("text-anchor", "middle");
|
|
|
|
elt.setAttribute("lengthAdjust", "spacingAndGlyphs");
|
|
|
|
elt.setAttribute("textLength", format(textLength));
|
|
|
|
if (fontWeight != null) {
|
|
|
|
elt.setAttribute("font-weight", fontWeight);
|
|
|
|
}
|
|
|
|
if (fontStyle != null) {
|
|
|
|
elt.setAttribute("font-style", fontStyle);
|
|
|
|
}
|
|
|
|
if (textDecoration != null) {
|
|
|
|
elt.setAttribute("text-decoration", textDecoration);
|
|
|
|
}
|
|
|
|
if (fontFamily != null) {
|
2017-02-01 18:55:51 +00:00
|
|
|
// http://plantuml.sourceforge.net/qa/?qa=5432/svg-monospace-output-has-wrong-font-family
|
|
|
|
if ("monospaced".equalsIgnoreCase(fontFamily)) {
|
|
|
|
fontFamily = "monospace";
|
|
|
|
}
|
2013-12-10 19:36:50 +00:00
|
|
|
elt.setAttribute("font-family", fontFamily);
|
2017-07-03 17:59:53 +00:00
|
|
|
|
|
|
|
if (fontFamily.equalsIgnoreCase("monospace") || fontFamily.equalsIgnoreCase("courier")) {
|
|
|
|
text = text.replace(' ', (char) 160);
|
|
|
|
}
|
2013-12-10 19:36:50 +00:00
|
|
|
}
|
2015-05-31 18:56:03 +00:00
|
|
|
if (textBackColor != null) {
|
|
|
|
final String backFilterId = getFilterBackColor(textBackColor);
|
|
|
|
elt.setAttribute("filter", "url(#" + backFilterId + ")");
|
|
|
|
}
|
2013-12-10 19:36:50 +00:00
|
|
|
for (Map.Entry<String, String> ent : attributes.entrySet()) {
|
|
|
|
elt.setAttribute(ent.getKey(), ent.getValue());
|
|
|
|
}
|
|
|
|
elt.setTextContent(text);
|
|
|
|
getG().appendChild(elt);
|
2015-04-07 18:18:37 +00:00
|
|
|
|
2019-03-29 22:14:07 +00:00
|
|
|
// http://forum.plantuml.net/9158/hyperlink-without-underline
|
|
|
|
// if (textDecoration != null && textDecoration.contains("underline")) {
|
|
|
|
// final double delta = 2;
|
|
|
|
// final Element elt2 = (Element) document.createElement("line");
|
|
|
|
// elt2.setAttribute("x1", format(x));
|
|
|
|
// elt2.setAttribute("y1", format(y + delta));
|
|
|
|
// elt2.setAttribute("x2", format(x + textLength));
|
|
|
|
// elt2.setAttribute("y2", format(y + delta));
|
|
|
|
// elt2.setAttribute("style", getStyleInternal(fill, "1.0", null));
|
|
|
|
// getG().appendChild(elt2);
|
|
|
|
// }
|
2015-04-07 18:18:37 +00:00
|
|
|
|
2011-08-08 17:48:29 +00:00
|
|
|
}
|
2011-04-19 16:50:40 +00:00
|
|
|
ensureVisible(x, y);
|
|
|
|
ensureVisible(x + textLength, y);
|
2010-11-15 20:35:36 +00:00
|
|
|
}
|
|
|
|
|
2015-05-31 18:56:03 +00:00
|
|
|
private final Map<String, String> filterBackColor = new HashMap<String, String>();
|
|
|
|
|
|
|
|
private String getIdFilterBackColor(String color) {
|
|
|
|
String result = filterBackColor.get(color);
|
|
|
|
if (result == null) {
|
2016-07-04 19:06:50 +00:00
|
|
|
result = filterUid + filterBackColor.size();
|
2015-05-31 18:56:03 +00:00
|
|
|
filterBackColor.put(color, result);
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
private String getFilterBackColor(String color) {
|
|
|
|
String id = filterBackColor.get(color);
|
|
|
|
if (id != null) {
|
|
|
|
return id;
|
|
|
|
}
|
|
|
|
id = getIdFilterBackColor(color);
|
|
|
|
final Element filter = (Element) document.createElement("filter");
|
|
|
|
filter.setAttribute("id", id);
|
|
|
|
filter.setAttribute("x", "0");
|
|
|
|
filter.setAttribute("y", "0");
|
|
|
|
filter.setAttribute("width", "1");
|
|
|
|
filter.setAttribute("height", "1");
|
2016-08-25 20:45:37 +00:00
|
|
|
addFilter(filter, "feFlood", "flood-color", color, "result", "flood");
|
|
|
|
addFilter(filter, "feComposite", "in", "SourceGraphic", "in2", "flood", "operator", "over");
|
2015-05-31 18:56:03 +00:00
|
|
|
defs.appendChild(filter);
|
|
|
|
return id;
|
2010-11-15 20:35:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
private Transformer getTransformer() throws TransformerException {
|
|
|
|
// Get a TransformerFactory object.
|
2016-12-14 21:01:03 +00:00
|
|
|
TransformerFactory xformFactory = null;
|
2020-02-18 21:24:31 +00:00
|
|
|
// try {
|
|
|
|
// final Class<?> factoryClass = Class
|
|
|
|
// .forName("com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl");
|
|
|
|
// xformFactory = (TransformerFactory) factoryClass.newInstance();
|
|
|
|
// } catch (Exception e) {
|
2020-03-18 10:50:02 +00:00
|
|
|
xformFactory = TransformerFactory.newInstance();
|
2020-02-18 21:24:31 +00:00
|
|
|
// }
|
2011-08-08 17:48:29 +00:00
|
|
|
Log.info("TransformerFactory=" + xformFactory.getClass());
|
2010-11-15 20:35:36 +00:00
|
|
|
|
|
|
|
// Get an XSL Transformer object.
|
|
|
|
final Transformer transformer = xformFactory.newTransformer();
|
2011-08-08 17:48:29 +00:00
|
|
|
Log.info("Transformer=" + transformer.getClass());
|
2010-11-15 20:35:36 +00:00
|
|
|
|
|
|
|
// // Sets the standalone property in the first line of
|
|
|
|
// // the output file.
|
2016-08-25 20:45:37 +00:00
|
|
|
transformer.setOutputProperty(OutputKeys.STANDALONE, "no");
|
|
|
|
// transformer.setOutputProperty(OutputKeys.DOCTYPE_PUBLIC, "SVG 1.1");
|
|
|
|
// transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no");
|
2010-11-15 20:35:36 +00:00
|
|
|
|
|
|
|
return transformer;
|
|
|
|
}
|
|
|
|
|
2013-12-10 19:36:50 +00:00
|
|
|
public void createXml(OutputStream os) throws TransformerException, IOException {
|
|
|
|
if (images.size() == 0) {
|
|
|
|
createXmlInternal(os);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
|
|
|
createXmlInternal(baos);
|
|
|
|
String s = new String(baos.toByteArray());
|
|
|
|
for (Map.Entry<String, String> ent : images.entrySet()) {
|
2020-03-18 10:50:02 +00:00
|
|
|
final String k = "<" + ent.getKey() + "/>";
|
|
|
|
s = s.replace(k, ent.getValue());
|
2013-12-10 19:36:50 +00:00
|
|
|
}
|
|
|
|
os.write(s.getBytes());
|
|
|
|
}
|
|
|
|
|
|
|
|
private void createXmlInternal(OutputStream os) throws TransformerException {
|
|
|
|
// // Add lines
|
|
|
|
// for (Line l : lines) {
|
|
|
|
// l.drawNow();
|
|
|
|
// }
|
2010-11-15 20:35:36 +00:00
|
|
|
|
|
|
|
// Get a DOMSource object that represents the
|
|
|
|
// Document object
|
|
|
|
final DOMSource source = new DOMSource(document);
|
|
|
|
|
2013-12-10 19:36:50 +00:00
|
|
|
final int maxXscaled = (int) (maxX * scale);
|
|
|
|
final int maxYscaled = (int) (maxY * scale);
|
|
|
|
String style = "width:" + maxXscaled + "px;height:" + maxYscaled + "px;";
|
2011-04-19 16:50:40 +00:00
|
|
|
if (backcolor != null) {
|
|
|
|
style += "background:" + backcolor + ";";
|
|
|
|
}
|
2018-03-09 21:37:34 +00:00
|
|
|
if (svgDimensionStyle) {
|
|
|
|
root.setAttribute("style", style);
|
|
|
|
root.setAttribute("width", format(maxX) + "px");
|
|
|
|
root.setAttribute("height", format(maxY) + "px");
|
|
|
|
}
|
2013-12-10 19:36:50 +00:00
|
|
|
root.setAttribute("viewBox", "0 0 " + maxXscaled + " " + maxYscaled);
|
2016-08-25 20:45:37 +00:00
|
|
|
root.setAttribute("zoomAndPan", "magnify");
|
2019-11-03 17:40:03 +00:00
|
|
|
root.setAttribute("preserveAspectRatio", preserveAspectRatio);
|
2016-08-25 20:45:37 +00:00
|
|
|
root.setAttribute("contentScriptType", "application/ecmascript");
|
|
|
|
root.setAttribute("contentStyleType", "text/css");
|
2013-12-10 19:36:50 +00:00
|
|
|
|
|
|
|
if (pendingBackground != null) {
|
|
|
|
pendingBackground.setAttribute("width", format(maxX));
|
|
|
|
pendingBackground.setAttribute("height", format(maxY));
|
|
|
|
|
|
|
|
}
|
2011-04-19 16:50:40 +00:00
|
|
|
|
2010-11-15 20:35:36 +00:00
|
|
|
// Get a StreamResult object that points to the
|
|
|
|
// screen. Then transform the DOM sending XML to
|
|
|
|
// the screen.
|
|
|
|
final StreamResult scrResult = new StreamResult(os);
|
|
|
|
getTransformer().transform(source, scrResult);
|
|
|
|
}
|
|
|
|
|
2013-12-10 19:36:50 +00:00
|
|
|
public void svgPath(double x, double y, UPath path, double deltaShadow) {
|
|
|
|
manageShadow(deltaShadow);
|
|
|
|
ensureVisible(x, y);
|
2010-11-15 20:35:36 +00:00
|
|
|
final StringBuilder sb = new StringBuilder();
|
|
|
|
for (USegment seg : path) {
|
|
|
|
final USegmentType type = seg.getSegmentType();
|
|
|
|
final double coord[] = seg.getCoord();
|
|
|
|
if (type == USegmentType.SEG_MOVETO) {
|
2011-09-08 10:42:27 +00:00
|
|
|
sb.append("M" + format(coord[0] + x) + "," + format(coord[1] + y) + " ");
|
2013-12-10 19:36:50 +00:00
|
|
|
ensureVisible(coord[0] + x + 2 * deltaShadow, coord[1] + y + 2 * deltaShadow);
|
2010-11-15 20:35:36 +00:00
|
|
|
} else if (type == USegmentType.SEG_LINETO) {
|
2011-09-08 10:42:27 +00:00
|
|
|
sb.append("L" + format(coord[0] + x) + "," + format(coord[1] + y) + " ");
|
2013-12-10 19:36:50 +00:00
|
|
|
ensureVisible(coord[0] + x + 2 * deltaShadow, coord[1] + y + 2 * deltaShadow);
|
2010-11-15 20:35:36 +00:00
|
|
|
} else if (type == USegmentType.SEG_QUADTO) {
|
2013-12-10 19:36:50 +00:00
|
|
|
sb.append("Q" + format(coord[0] + x) + "," + format(coord[1] + y) + " " + format(coord[2] + x) + ","
|
|
|
|
+ format(coord[3] + y) + " ");
|
|
|
|
ensureVisible(coord[0] + x + 2 * deltaShadow, coord[1] + y + 2 * deltaShadow);
|
|
|
|
ensureVisible(coord[2] + x + 2 * deltaShadow, coord[3] + y + 2 * deltaShadow);
|
2011-08-08 17:48:29 +00:00
|
|
|
} else if (type == USegmentType.SEG_CUBICTO) {
|
2013-12-10 19:36:50 +00:00
|
|
|
sb.append("C" + format(coord[0] + x) + "," + format(coord[1] + y) + " " + format(coord[2] + x) + ","
|
|
|
|
+ format(coord[3] + y) + " " + format(coord[4] + x) + "," + format(coord[5] + y) + " ");
|
|
|
|
ensureVisible(coord[0] + x + 2 * deltaShadow, coord[1] + y + 2 * deltaShadow);
|
|
|
|
ensureVisible(coord[2] + x + 2 * deltaShadow, coord[3] + y + 2 * deltaShadow);
|
|
|
|
ensureVisible(coord[4] + x + 2 * deltaShadow, coord[5] + y + 2 * deltaShadow);
|
2015-04-07 18:18:37 +00:00
|
|
|
} else if (type == USegmentType.SEG_ARCTO) {
|
2018-01-28 22:08:15 +00:00
|
|
|
// A25,25 0,0 5,395,40
|
|
|
|
sb.append("A" + format(coord[0]) + "," + format(coord[1]) + " " + formatBoolean(coord[2]) + " "
|
|
|
|
+ formatBoolean(coord[3]) + " " + formatBoolean(coord[4]) + " " + format(coord[5] + x) + ","
|
2015-04-07 18:18:37 +00:00
|
|
|
+ format(coord[6] + y) + " ");
|
|
|
|
ensureVisible(coord[5] + coord[0] + x + 2 * deltaShadow, coord[6] + coord[1] + y + 2 * deltaShadow);
|
2010-11-15 20:35:36 +00:00
|
|
|
} else if (type == USegmentType.SEG_CLOSE) {
|
|
|
|
// Nothing
|
|
|
|
} else {
|
2019-03-29 22:14:07 +00:00
|
|
|
Log.println("unknown3 " + seg);
|
2010-11-15 20:35:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
2013-12-10 19:36:50 +00:00
|
|
|
if (hidden == false) {
|
|
|
|
final Element elt = (Element) document.createElement("path");
|
|
|
|
elt.setAttribute("d", sb.toString());
|
|
|
|
elt.setAttribute("style", getStyle());
|
|
|
|
elt.setAttribute("fill", fill);
|
2017-04-26 17:48:37 +00:00
|
|
|
final String id = path.getComment();
|
|
|
|
if (id != null) {
|
|
|
|
elt.setAttribute("id", id);
|
|
|
|
}
|
2016-07-04 19:06:50 +00:00
|
|
|
addFilterShadowId(elt, deltaShadow);
|
2013-12-10 19:36:50 +00:00
|
|
|
getG().appendChild(elt);
|
|
|
|
}
|
2010-11-15 20:35:36 +00:00
|
|
|
}
|
|
|
|
|
2016-07-04 19:06:50 +00:00
|
|
|
private void addFilterShadowId(final Element elt, double deltaShadow) {
|
|
|
|
if (deltaShadow > 0) {
|
|
|
|
elt.setAttribute("filter", "url(#" + shadowId + ")");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-11-15 20:35:36 +00:00
|
|
|
private StringBuilder currentPath = null;
|
|
|
|
|
|
|
|
public void newpath() {
|
|
|
|
currentPath = new StringBuilder();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
public void moveto(double x, double y) {
|
|
|
|
currentPath.append("M" + format(x) + "," + format(y) + " ");
|
2011-04-19 16:50:40 +00:00
|
|
|
ensureVisible(x, y);
|
2010-11-15 20:35:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public void lineto(double x, double y) {
|
|
|
|
currentPath.append("L" + format(x) + "," + format(y) + " ");
|
2011-04-19 16:50:40 +00:00
|
|
|
ensureVisible(x, y);
|
2010-11-15 20:35:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public void closepath() {
|
|
|
|
currentPath.append("Z ");
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
public void curveto(double x1, double y1, double x2, double y2, double x3, double y3) {
|
2011-04-19 16:50:40 +00:00
|
|
|
currentPath.append("C" + format(x1) + "," + format(y1) + " " + format(x2) + "," + format(y2) + " " + format(x3)
|
|
|
|
+ "," + format(y3) + " ");
|
|
|
|
ensureVisible(x1, y1);
|
|
|
|
ensureVisible(x2, y2);
|
|
|
|
ensureVisible(x3, y3);
|
2010-11-15 20:35:36 +00:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
public void quadto(double x1, double y1, double x2, double y2) {
|
|
|
|
currentPath.append("Q" + format(x1) + "," + format(y1) + " " + format(x2) + "," + format(y2) + " ");
|
2011-04-19 16:50:40 +00:00
|
|
|
ensureVisible(x1, y1);
|
|
|
|
ensureVisible(x2, y2);
|
2010-11-15 20:35:36 +00:00
|
|
|
}
|
2011-04-19 16:50:40 +00:00
|
|
|
|
2013-12-10 19:36:50 +00:00
|
|
|
private String format(double x) {
|
2019-03-29 22:14:07 +00:00
|
|
|
return TikzGraphics.format(x * scale);
|
2010-11-15 20:35:36 +00:00
|
|
|
}
|
|
|
|
|
2018-01-28 22:08:15 +00:00
|
|
|
private String formatBoolean(double x) {
|
|
|
|
return x == 0 ? "0" : "1";
|
|
|
|
}
|
|
|
|
|
2010-11-15 20:35:36 +00:00
|
|
|
public void fill(int windingRule) {
|
2013-12-10 19:36:50 +00:00
|
|
|
if (hidden == false) {
|
|
|
|
final Element elt = (Element) document.createElement("path");
|
|
|
|
elt.setAttribute("d", currentPath.toString());
|
|
|
|
// elt elt.setAttribute("style", getStyle());
|
|
|
|
getG().appendChild(elt);
|
|
|
|
}
|
2010-11-15 20:35:36 +00:00
|
|
|
currentPath = null;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2013-12-10 19:36:50 +00:00
|
|
|
public void svgImage(BufferedImage image, double x, double y) throws IOException {
|
|
|
|
if (hidden == false) {
|
|
|
|
final Element elt = (Element) document.createElement("image");
|
|
|
|
elt.setAttribute("width", format(image.getWidth()));
|
|
|
|
elt.setAttribute("height", format(image.getHeight()));
|
|
|
|
elt.setAttribute("x", format(x));
|
|
|
|
elt.setAttribute("y", format(y));
|
|
|
|
final String s = toBase64(image);
|
2019-04-21 20:40:01 +00:00
|
|
|
elt.setAttribute("xlink:href", "data:image/png;base64," + s);
|
2013-12-10 19:36:50 +00:00
|
|
|
getG().appendChild(elt);
|
|
|
|
}
|
|
|
|
ensureVisible(x, y);
|
|
|
|
ensureVisible(x + image.getWidth(), y + image.getHeight());
|
|
|
|
}
|
2011-09-08 10:42:27 +00:00
|
|
|
|
2013-12-10 19:36:50 +00:00
|
|
|
private final Map<String, String> images = new HashMap<String, String>();
|
2011-09-08 10:42:27 +00:00
|
|
|
|
2017-04-05 17:37:42 +00:00
|
|
|
public void svgImage(SvgString image, double x, double y) {
|
2013-12-10 19:36:50 +00:00
|
|
|
if (hidden == false) {
|
2017-04-05 17:37:42 +00:00
|
|
|
String svg = manageScale(image);
|
2013-12-10 19:36:50 +00:00
|
|
|
final String pos = "<svg x=\"" + format(x) + "\" y=\"" + format(y) + "\">";
|
|
|
|
svg = pos + svg.substring(5);
|
2020-03-18 10:50:02 +00:00
|
|
|
final String key = "imagesvginlined" + image.getMD5Hex() + images.size();
|
2013-12-10 19:36:50 +00:00
|
|
|
final Element elt = (Element) document.createElement(key);
|
|
|
|
getG().appendChild(elt);
|
|
|
|
images.put(key, svg);
|
2011-09-08 10:42:27 +00:00
|
|
|
}
|
2013-12-10 19:36:50 +00:00
|
|
|
ensureVisible(x, y);
|
2017-04-05 17:37:42 +00:00
|
|
|
ensureVisible(x + image.getData("width"), y + image.getData("height"));
|
|
|
|
}
|
|
|
|
|
|
|
|
private String manageScale(SvgString svg) {
|
|
|
|
final double svgScale = svg.getScale();
|
|
|
|
if (svgScale * scale == 1) {
|
2018-07-27 21:56:46 +00:00
|
|
|
return svg.getSvg(false);
|
2017-04-05 17:37:42 +00:00
|
|
|
}
|
|
|
|
final String s1 = "\\<g\\b";
|
|
|
|
final String s2 = "<g transform=\"scale(" + format(svgScale) + "," + format(svgScale) + ")\" ";
|
2018-07-27 21:56:46 +00:00
|
|
|
return svg.getSvg(false).replaceFirst(s1, s2);
|
2011-09-08 10:42:27 +00:00
|
|
|
}
|
|
|
|
|
2013-12-10 19:36:50 +00:00
|
|
|
private String toBase64(BufferedImage image) throws IOException {
|
|
|
|
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
|
|
|
ImageIO.write(image, "png", baos);
|
|
|
|
final byte data[] = baos.toByteArray();
|
|
|
|
return new String(Base64Coder.encode(data));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Shadow
|
|
|
|
|
|
|
|
private boolean withShadow = false;
|
|
|
|
|
|
|
|
private void manageShadow(double deltaShadow) {
|
|
|
|
if (deltaShadow != 0) {
|
|
|
|
if (withShadow == false) {
|
|
|
|
// <filter id="f1" x="0" y="0" width="120%" height="120%">
|
|
|
|
final Element filter = (Element) document.createElement("filter");
|
2016-07-04 19:06:50 +00:00
|
|
|
filter.setAttribute("id", shadowId);
|
2013-12-10 19:36:50 +00:00
|
|
|
filter.setAttribute("x", "-1");
|
|
|
|
filter.setAttribute("y", "-1");
|
|
|
|
filter.setAttribute("width", "300%");
|
|
|
|
filter.setAttribute("height", "300%");
|
|
|
|
addFilter(filter, "feGaussianBlur", "result", "blurOut", "stdDeviation", "" + (2 * scale));
|
|
|
|
addFilter(filter, "feColorMatrix", "type", "matrix", "in", "blurOut", "result", "blurOut2", "values",
|
|
|
|
"0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 .4 0");
|
2020-03-18 10:50:02 +00:00
|
|
|
addFilter(filter, "feOffset", "result", "blurOut3", "in", "blurOut2", "dx", "" + (4 * scale), "dy",
|
|
|
|
"" + (4 * scale));
|
2013-12-10 19:36:50 +00:00
|
|
|
addFilter(filter, "feBlend", "in", "SourceGraphic", "in2", "blurOut3", "mode", "normal");
|
|
|
|
defs.appendChild(filter);
|
|
|
|
|
|
|
|
}
|
|
|
|
withShadow = true;
|
2011-09-08 10:42:27 +00:00
|
|
|
}
|
|
|
|
}
|
2013-12-10 19:36:50 +00:00
|
|
|
|
|
|
|
private void addFilter(Element filter, String name, String... data) {
|
2019-02-09 21:56:24 +00:00
|
|
|
assert data.length % 2 == 0;
|
2013-12-10 19:36:50 +00:00
|
|
|
final Element elt = (Element) document.createElement(name);
|
|
|
|
for (int i = 0; i < data.length; i += 2) {
|
|
|
|
elt.setAttribute(data[i], data[i + 1]);
|
2011-09-08 10:42:27 +00:00
|
|
|
}
|
2013-12-10 19:36:50 +00:00
|
|
|
filter.appendChild(elt);
|
2011-09-08 10:42:27 +00:00
|
|
|
}
|
|
|
|
|
2013-12-10 19:36:50 +00:00
|
|
|
private boolean hidden;
|
|
|
|
|
|
|
|
public void setHidden(boolean hidden) {
|
|
|
|
this.hidden = hidden;
|
|
|
|
}
|
2011-09-08 10:42:27 +00:00
|
|
|
|
2019-09-14 18:12:04 +00:00
|
|
|
public static final String MD5_HEADER = "<!--MD5=[";
|
|
|
|
|
|
|
|
public static String getMD5Hex(String comment) {
|
|
|
|
return SignatureUtils.getMD5Hex(comment);
|
|
|
|
}
|
|
|
|
|
2016-07-25 19:25:28 +00:00
|
|
|
public void addComment(String comment) {
|
2019-09-14 18:12:04 +00:00
|
|
|
final String signature = getMD5Hex(comment);
|
|
|
|
comment = "MD5=[" + signature + "]\n" + comment;
|
2016-07-25 19:25:28 +00:00
|
|
|
final Comment commentElement = document.createComment(comment);
|
|
|
|
getG().appendChild(commentElement);
|
|
|
|
}
|
|
|
|
|
2020-05-17 21:15:50 +00:00
|
|
|
public void openLink(String url, String title, String target) {
|
|
|
|
if (url == null) {
|
|
|
|
throw new IllegalArgumentException();
|
|
|
|
}
|
|
|
|
// javascript: security issue
|
2020-05-30 15:20:23 +00:00
|
|
|
if (SecurityUtils.getJavascriptUnsecure() == false && url.toLowerCase().startsWith("javascript")) {
|
2020-05-17 21:15:50 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// if (pendingAction.size() > 0) {
|
|
|
|
// closeLink();
|
|
|
|
// }
|
|
|
|
|
|
|
|
pendingAction.add(0, (Element) document.createElement("a"));
|
|
|
|
pendingAction.get(0).setAttribute("target", target);
|
|
|
|
pendingAction.get(0).setAttribute(XLINK_HREF1, url);
|
|
|
|
pendingAction.get(0).setAttribute(XLINK_HREF2, url);
|
|
|
|
pendingAction.get(0).setAttribute("xlink:type", "simple");
|
|
|
|
pendingAction.get(0).setAttribute("xlink:actuate", "onRequest");
|
|
|
|
pendingAction.get(0).setAttribute("xlink:show", "new");
|
|
|
|
if (title == null) {
|
|
|
|
pendingAction.get(0).setAttribute(XLINK_TITLE1, url);
|
|
|
|
pendingAction.get(0).setAttribute(XLINK_TITLE2, url);
|
|
|
|
} else {
|
|
|
|
title = formatTitle(title);
|
|
|
|
pendingAction.get(0).setAttribute(XLINK_TITLE1, title);
|
|
|
|
pendingAction.get(0).setAttribute(XLINK_TITLE2, title);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private String formatTitle(String title) {
|
|
|
|
final Pattern p = Pattern.compile("\\<U\\+([0-9A-Fa-f]+)\\>");
|
|
|
|
final Matcher m = p.matcher(title);
|
|
|
|
final StringBuffer sb = new StringBuffer();
|
|
|
|
while (m.find()) {
|
|
|
|
final String num = m.group(1);
|
|
|
|
final char c = (char) Integer.parseInt(num, 16);
|
|
|
|
m.appendReplacement(sb, "" + c);
|
|
|
|
}
|
|
|
|
m.appendTail(sb);
|
|
|
|
|
|
|
|
title = sb.toString().replaceAll("\\\\n", "\n");
|
|
|
|
return title;
|
|
|
|
}
|
|
|
|
|
|
|
|
public void closeLink() {
|
|
|
|
if (pendingAction.size() > 0) {
|
|
|
|
final Element element = pendingAction.get(0);
|
|
|
|
pendingAction.remove(0);
|
|
|
|
if (element.getFirstChild() != null) {
|
|
|
|
// Empty link
|
|
|
|
getG().appendChild(element);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public void startGroup(String groupId) {
|
|
|
|
pendingAction.add(0, (Element) document.createElement("g"));
|
|
|
|
pendingAction.get(0).setAttribute("id", groupId);
|
|
|
|
}
|
|
|
|
|
|
|
|
public void closeGroup() {
|
|
|
|
closeLink();
|
|
|
|
}
|
|
|
|
|
2010-11-15 20:35:36 +00:00
|
|
|
}
|