DrawIo mxGraph:使用XmlToSvg会丢失形状信息

时间:2017-05-25 11:39:34

标签: mxgraph

我正在尝试使用Java将XML转换为SVG,但看起来形状信息在此过程中丢失了。

给出一个简单的draw.io图表:

drawio

运行XmlToSvg.java后,我得到:

converted

我将其保存为未压缩的XML。我正在使用mxGraph Repo

中的mxgraph-all.jar

您知道是否有隐藏设置可以保留形状和颜色吗?

2 个答案:

答案 0 :(得分:3)

简短版

看起来尽管在GitHub页面上有声明,但除了JavaScript之外没有任何实现真正功能齐全且生产就绪。特别是Java实现(以及.Net和PHP服务器端实现)并不支持" Cube"开箱即用。

更多详情

<强> 颜色

您没有提供您的示例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不包含任何有关颜色的信息。因此关于&#34;保留颜色的整体想法&#34;是错的。在Java实现中,您可以配置&#34;默认颜色&#34;使用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.createDefaultVertexStylemxStylesheet.createDefaultEdgeStyle了解详细信息。

<强> 形状

&#34; 椭圆&#34;形状处理不正确,因为没有代码可以解析"ellipse;whiteSpace=wrap;html=1;"并且理解形状应该是&#34;椭圆&#34; (将此字母指向&#34;双椭圆&#34;样式"ellipse;shape=doubleEllipse;whiteSpace=wrap;html=1;aspect=fixed;",其中包含显式shape值。在JS实现中,样式的第一部分似乎选择了一个处理函数,它将处理字符串的其余部分并进行实际工作。在Java implmenetation中似乎没有这样的功能。您可以使用&#34;命名样式&#34;来解决这个问题。功能和定义相应的&#34;处理程序的默认形状&#34;在像这样的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_的列表并不包含&#34; 多维数据集 &#34;。在JS实现&#34; cube&#34;是一个复合形状,由examples/grapheditor/www/js/Shape.js中的专用处理程序处理,它不是核心库的一部分!这意味着如果你想在Java代码中支持这种高级形状,你必须自己推出代码来自己处理它。

P.S。通过所有这些更改(黑客),我在第一个代码段中使用来自XML的Java代码获得的图像是:

Image for the simple graph

答案 1 :(得分:0)

有一个XML文件,其中包含最通用形状的参数。您应该将其加载到样式表中,以使图像看起来与在编辑器中绘制的图像完全一样。默认样式表为default.xml

因此,首先让您的代码获得两件事:样式表和图表内容。

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

接下来,创建SVG图像的最简单方法是利用mxgraph-core.jar中的类。看起来像这样

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;
}

但是,正如SergGr所指出的,mxgraph库的Java实现不包含一些有用的形状。它们的绘制规则由Shape.js中的JavaScript函数描述。

我试图在Java标准库附带的ScriptEngine中执行JavaScript。不幸的是,这个想法没有用,因为内部深处的JavaScript代码与浏览器交互。

但是,如果我们在浏览器中运行代码,则效果很好。我用HtmlUnit成功做到了。

编写一个JavaScript函数以从Java调用:

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();

将此文本加载到String中并运行以下代码

<Directory Id="VS2017_IDE_DIR">
  <Directory Id="VS2017_EXTENSIONS_DIR">
    <Directory Id="VS2017_MYCOMPANY_EX" Name="MYCOMPANY">
      <Directory Id="VS2017_AUTORUNNER_EX" Name="MYCOMPANY Extension">
        <Directory Id="VS2017_AUTORUNNER_EX_VERSION" Name="$(var.MajorAndMinorVersion)">
          <Component Id="VS2017_AUTORUNNER_EXTENSIONSHORTCUTS" Guid="">

            <Condition>VS2017DEVENV</Condition>
            <CreateFolder />
            <util:RestartResource Path="[VS2017DEVENV]"/>

            <RemoveFolder Id="REMOVE_VS_VERSION_VS2017" On="uninstall" Directory="VS2017_MYCOMPANY_EX" />
            <RemoveFolder Id="REMOVE_VS2017_EXTENSIONS" On="uninstall" Directory="VS2017_AUTORUNNER_EX" />
            <RemoveFolder Id="REMOVE_VS2017_MYCOMPANY_EX" On="uninstall" Directory="VS2017_AUTORUNNER_EX_VERSION" />

            <File Source="$(var.ManagedBinariesDir)VisualStudioExtension\extension.vsixmanifest" Name="extension.vsixmanifest"
                    Id="extension.vsixmanifest_VS2017" />
            <File Source="$(var.ManagedBinariesDir)VisualStudioExtension\MYCOMPANY.VSExtension.pkgdef"
                    Name="MYCOMPANY.VSExtension.pkgdef" Id="MYCOMPANY.AutoRunner.pkgdef_VS2017" />
          </Component>
        </Directory>
      </Directory>
    </Directory>
  </Directory>
</Directory>

上面的代码对我来说似乎很好。