【问题标题】:DrawIo mxGraph: Using XmlToSvg loses shapes informationDrawIo mxGraph:使用 XmlToSvg 会丢失形状信息
【发布时间】:2017-05-25 11:39:34
【问题描述】:

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

给定一个简单的draw.io 图:

运行XmlToSvg.java后我得到:

我将它保存为未压缩的 XML。我正在使用来自mxGraph Repomxgraph-all.jar

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

【问题讨论】:

    标签: mxgraph


    【解决方案1】:

    短版

    看起来,尽管 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.createDefaultVertexStylemxStylesheet.createDefaultEdgeStyle 了解一些详细信息。

    形状

    椭圆”形状未正确处理,因为没有代码可解析"ellipse;whiteSpace=wrap;html=1;" 并了解该形状应为“椭圆”(比较此为“双椭圆”样式@ 987654333@ 包含显式 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_ 的列表不包含“cube”。在 JS 实现中,“立方体”是由examples/grapheditor/www/js/Shape.js 中的专用处理程序处理的复合形状,它不是核心库的一部分!这意味着如果你想在你的 Java 代码中支持这些高级形状,你必须自己推出代码来处理它。

    附:通过所有这些更改(黑客),我在第一个 sn-p 中使用 XML 中的 Java 代码得到的图像是:

    【讨论】:

    • 非常感谢这次调查和技巧。您是否知道一种将 XML 显示为 SVG 的 JavaScript 方式,以避免完全客户端加载(它会在实例化期间尝试加载远程资源)?
    • 这个答案是正确的。 mxGraph 是 JavaScript 客户端库,Java、.NET 等不是核心 mxGraph 的一部分。当您说“看起来尽管 GitHub 页面上有声明,但除了 JavaScript 之外,没有任何实现真正功能齐全且已准备好生产。”你能指出我们在哪里提出这些误导性的说法,我们会纠正它们。谢谢。
    • @David,我认为这句话“这是支持您在 draw.io 中看到的绘图功能的底层技术。”和“还提供了 Java 和 .NET 中用于持久性(打开和保存)功能的服务器端功能,以及 服务器端图像生成”@987654325 @ 和 NPM page 非常具有误导性,这个问题是一个结果。我认为对于许多读者来说,第二句话暗示您可以使用 .Net 或 Java 实现从 draw.io 的 XML 生成图像。
    • 对于第一个,我们必须同意不同意:)。 draw.io 显然是 mxGraph 的超集,您可以在 draw.io 和 mxGraph 中创建图表的想法我觉得是合理的。第二个,好。我的意思是“为无法在客户端生成图像的浏览器生成服务器端图像”。它不是独立的服务器端图像生成,客户端必须将图形基元发送到服务器。我会尽力澄清这一点。
    • @David,我认为问题出在两个句子中。每个单独的都可以。但是在同一个“介绍”部分中,它们具有误导性。我认为您需要澄清的不是基元必须由客户端发送的事实,而是核心 mxGraph 库是 draw.io 的子集的事实,因此服务器端实现仅支持所有 draw.io 的子集功能。
    【解决方案2】:

    有一个 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 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();
    

    上面的代码似乎对我很有效。

    【讨论】:

      猜你喜欢
      • 2016-11-20
      • 2016-05-05
      • 1970-01-01
      • 1970-01-01
      • 2012-12-24
      • 2017-09-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多