我正在尝试使用 Java 将 XML 转换为 SVG,但看起来形状信息在此过程中丢失了。
给定一个简单的draw.io
图表:
运行XmlToSvg.java后,我得到:
我将它保存为未压缩的 XML。我正在使用mxgraph-all.jar
来自mxGraph 回购
您是否知道是否有隐藏设置可启用以保留形状和颜色?
我正在尝试使用 Java 将 XML 转换为 SVG,但看起来形状信息在此过程中丢失了。
给定一个简单的draw.io
图表:
运行XmlToSvg.java后,我得到:
我将它保存为未压缩的 XML。我正在使用mxgraph-all.jar
来自mxGraph 回购
您是否知道是否有隐藏设置可启用以保留形状和颜色?
精简版
看起来尽管 GitHub 页面上有声明,但除了 JavaScript 之外,没有任何实现真正功能齐全并且可以投入生产。特别是 Java 实现(以及 .Net 和 PHP 服务器端的)不支持开箱即用的“立方体”形状。
更多细节
颜色
您没有提供您的示例 XML,但是当我生成类似的图表时,我得到了类似的东西
<?xml version="1.0" encoding="UTF-8"?>
<mxGraphModel dx="1426" dy="816" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="850"
pageHeight="1100" background="#ffffff" math="0" shadow="0">
<root>
<mxCell id="0"/>
<mxCell id="1" parent="0"/>
<mxCell id="2" value="" style="ellipse;whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry x="445" y="60" width="230" height="150" as="geometry"/>
</mxCell>
<mxCell id="3" value="" style="ellipse;shape=doubleEllipse;whiteSpace=wrap;html=1;aspect=fixed;" parent="1" vertex="1">
<mxGeometry x="500" y="320" width="120" height="120" as="geometry"/>
</mxCell>
<mxCell id="4" value="" style="endArrow=classic;html=1;" parent="1" source="3" target="2" edge="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="430" y="510" as="sourcePoint"/>
<mxPoint x="480" y="460" as="targetPoint"/>
</mxGeometry>
</mxCell>
<mxCell id="5" value="" style="shape=cube;whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry x="80" y="320" width="170" height="110" as="geometry"/>
</mxCell>
</root>
</mxGraphModel>
这里重要的是这个 XML 不包含任何关于颜色的信息。因此,关于“保留颜色”的整个想法是错误的。在 Java 实现中,您可以使用类的实例配置“默认颜色”mxStylesheet
并使用它来初始化mxGraph
对象。例如,要将颜色更改为黑色和白色,您可以执行以下操作:
mxStylesheet stylesheet = new mxStylesheet();
// configure "figures" aka "vertex"
{
Map<String, Object> style = stylesheet.getDefaultVertexStyle();
style.put(mxConstants.STYLE_FILLCOLOR, "#FFFFFF");
style.put(mxConstants.STYLE_STROKECOLOR, "#000000");
style.put(mxConstants.STYLE_FONTCOLOR, "#000000");
}
// configure "lines" aka "edges"
{
Map<String, Object> style = stylesheet.getDefaultEdgeStyle();
style.put(mxConstants.STYLE_STROKECOLOR, "#000000");
style.put(mxConstants.STYLE_FONTCOLOR, "#000000");
}
mxGraph graph = new mxGraph(stylesheet);
您可以查看mxStylesheet.createDefaultVertexStyle和mxStylesheet.createDefaultEdgeStyle了解一些详细信息。
形状
“椭圆”形状没有正确处理,因为没有代码可以解析"ellipse;whiteSpace=wrap;html=1;"
和理解该形状应该是“椭圆”(将此与"ellipse;shape=doubleEllipse;whiteSpace=wrap;html=1;aspect=fixed;"
包含显式shape
值的“双椭圆”样式相比较)。在 JS 实现中,样式的第一部分似乎选择了一个处理函数来处理字符串的其余部分并执行实际工作。Java 实现中似乎根本没有这样的功能。mxStylesheet
您可以通过使用“命名样式”功能来解决此问题,并为同一对象中的相应“处理程序”定义默认形状,如下所示:
// I just copied the whole list of mxConstants.SHAPE_ here
// you probably should filter it by removing non-primitive shapes
// such as mxConstants.SHAPE_DOUBLE_ELLIPSE
String[] shapes = new String[] {
mxConstants.SHAPE_RECTANGLE,
mxConstants.SHAPE_ELLIPSE,
mxConstants.SHAPE_DOUBLE_RECTANGLE,
mxConstants.SHAPE_DOUBLE_ELLIPSE,
mxConstants.SHAPE_RHOMBUS,
mxConstants.SHAPE_LINE,
mxConstants.SHAPE_IMAGE,
mxConstants.SHAPE_ARROW,
mxConstants.SHAPE_CURVE,
mxConstants.SHAPE_LABEL,
mxConstants.SHAPE_CYLINDER,
mxConstants.SHAPE_SWIMLANE,
mxConstants.SHAPE_CONNECTOR,
mxConstants.SHAPE_ACTOR,
mxConstants.SHAPE_CLOUD,
mxConstants.SHAPE_TRIANGLE,
mxConstants.SHAPE_HEXAGON,
};
Map<String, Map<String, Object>> styles = stylesheet.getStyles();
for (String sh : shapes)
{
Map<String, Object> style = new HashMap<>();
style.put(mxConstants.STYLE_SHAPE, sh);
styles.put(sh, style);
}
您仍然可能注意到列表中mxConstants.SHAPE_
不包含“立方体”。在 JS 实现中,“立方体”是一个复合形状,由examples/grapheditor/www/js/Shape.js中的专用处理程序处理,它不是核心库的一部分!这意味着如果您想在您的 Java 代码中支持这些高级形状,您将不得不推出代码来自己处理它。
PS 通过所有这些更改(黑客),我在第一个片段中使用 XML 中的 Java 代码获得的图像是:
有一个 XML 文件,其中包含最通用形状的参数。您应该将其加载到样式表中,以使图像看起来与在编辑器中绘制的完全相同。默认样式表是default.xml。
所以首先让你的代码得到两件事:样式表和图表内容。
String diagramText = getAsString(diagramPath);
String stylesheetText = getAsString(stylesheetPath);
接下来,创建 SVG 图像的最简单方法是利用mxgraph-core.jar中的类。看起来像这样
mxStylesheet stylesheet = new mxStylesheet(); // mxgraph-core.jar
InputSource is = new InputSource(new StringReader(stylesheetText));
Document document = documentBuilder.parse(is);
mxCodec codec = new mxCodec(document);
codec.decode(document.getDocumentElement(), stylesheet);
mxIGraphModel model = new mxGraphModel();
mxGraph graph = new mxGraph(model, context.stylesheet);
is = new InputSource(new StringReader(diagramText));
document = documentBuilder.parse(new InputSource(is));
codec = new mxCodec(document);
codec.decode(document.getDocumentElement(), model);
final Document svgDocument = documentBuilder.newDocument();
mxCellRenderer.drawCells(
graph,
null,
1d,
null,
new mxCellRenderer.CanvasFactory() {
@Override
public mxICanvas createCanvas(int width, int height) {
Element root = output.createElement("svg");
String w = Integer.toString(width);
String h = Integer.toString(height);
root.setAttribute("width", w);
root.setAttribute("height", h);
root.setAttribute("viewBox", "0 0 " + w + " " + h);
root.setAttribute("version", "1.1");
root.setAttribute("xmlns", "http://www.w3.org/2000/svg");
root.setAttribute("xmlns:xlink", "http://www.w3.org/1999/xlink");
output.appendChild(root);
mxSvgCanvas canvas = new mxSvgCanvas(svgDocument);
canvas.setEmbedded(true);
return canvas;
}
});
return svgDocument; // this is the result
然而,正如SergGr 所指出的,mxgraph 库的 Java 实现不包含一些有用的形状。它们的绘制规则由Shape.js中的 JavaScript 函数描述。
我试图在 Java 标准库中提供的 ScriptEngine 中执行该 JavaScript。不幸的是,这个想法没有奏效,因为内部深处的 JavaScript 代码与浏览器交互。
但如果我们在浏览器中运行代码,它运行良好。我用HtmlUnit成功地做到了。
编写一个 JavaScript 函数以从 Java 调用:
function convertToSVG(diagramText, stylesheetText) {
var stylesheet = new mxStylesheet();
var doc = mxUtils.parseXml(stylesheetText);
var stylesheetRoot = doc.documentElement;
var stylesheetCodec = new mxCodec(doc);
var dom = document.implementation;
stylesheetCodec.decode(stylesheetRoot, stylesheet);
doc = dom.createDocument(null, "div", null);
var model = new mxGraphModel();
var graph = new mxGraph(doc.documentElement, model, "exact", stylesheet);
doc = new DOMParser().parseFromString(diagram, "text/xml");
var codec = new mxCodec(doc);
codec.decode(doc.documentElement, model);
doc = dom.createDocument("http://www.w3.org/2000/svg", "svg", null);
var svgRoot = doc.documentElement;
var bounds = graph.getGraphBounds();
svgRoot.setAttribute("xmlns", "http://www.w3.org/2000/svg");
svgRoot.setAttribute("xmlns:xlink", "http://www.w3.org/1999/xlink");
svgRoot.setAttribute("width", bounds.width);
svgRoot.setAttribute("height", bounds.height);
svgRoot.setAttribute("viewBox", "0 0 " + bounds.width + " " + bounds.height);
svgRoot.setAttribute("version", "1.1");
var svgCanvas = new mxSvgCanvas2D(svgRoot);
svgCanvas.translate(-bounds.x, -bounds.y);
var exporter = new mxImageExport();
var state = graph.getView().getState(model.root);
exporter.drawState(state, svgCanvas);
var result = new XMLSerializer().serializeToString(doc);
return result;
}
将此文本加载到 String 并运行以下代码
String jsFunction = getAsString("convertToSVG.js");
Path file = Files.createTempFile("44179673-", ".html"); // do not forget to delete it
String hmltText = "<html xmlns=\"http://www.w3.org/1999/xhtml\">"
+ "<head><title>Empty file</title></head><body/></html>";
Files.write(file, Arrays.asList(htmlText));
WebClient webClient = new WebClient(); // net.sourceforge.htmlunit:htmlunit
HtmlPage page = webClient.getPage(file.toUri().toString());
String initScript = ""
+ "var mxLoadResources = false;"
+ "var mxLoadStylesheets = false;"
+ "var urlParams = new Object();";
page.executeJavaScript(initScript);
page.executeJavaScript(getAsString("mxClient.min.js"));
page.executeJavaScript(getAsString("Graph.js")); // Shape.js depends on it
page.executeJavaScript(getAsString("Shapes.js"));
ScriptResult scriptResult = page.executeJavaScript(jsFunction);
Object convertFunc = scriptResult.getJavaScriptResult();
Object args[] = new Object[]{ diagramText, stylesheetText };
scriptResult = page.executeJavaScriptFunction(convertFunc, null, args, null);
String svg = scriptResult.getJavaScriptResult().toString();
上面的代码似乎对我很有效。