首页 > 解决方案 > 从 mxGraph 输出 SVG 文件

问题描述

我和我的团队正在研究使用 mxGraph 以编程方式生成图表。我们创建了一个图表并将其保存为 XML 文件。我们如何从那里转到 SVG 文件?

我了解 mxGraph 本身使用 SVG 文件进行显示,并且它可以编写其中包含 XML 编码的 SVG 文件,以便可以在 diagrams.net 中重新打开它们。如何让 mxGraph 将我们的图形放在 SVG 画布上,然后将其序列化到磁盘?

// from https://stackoverflow.com/a/57829704
const jsdom = require("jsdom");
const { JSDOM } = jsdom;
const dom = new JSDOM();
const fs = require('fs');

global.window = dom.window;
global.document = window.document;
global.XMLSerializer = window.XMLSerializer;
global.navigator = window.navigator;

const mxgraph = require("mxgraph")({
    mxImageBasePath: "./src/images",
    mxBasePath: "./src"
});


const {mxGraph, mxCodec, mxUtils, mxConstants, mxSvgCanvas2D} = mxgraph;

function makeHelloWorld() {
  // Extracted from https://github.com/jgraph/mxgraph/blob/master/javascript/examples/helloworld.html
  const graph = new mxGraph();

  // Gets the default parent for inserting new cells. This
  // is normally the first child of the root (ie. layer 0).
  var parent = graph.getDefaultParent();

  // Adds cells to the model in a single step
  graph.getModel().beginUpdate();
  try {
    var v1 = graph.insertVertex(parent, null, 'Hello,', 20, 20, 80, 30);
    var v2 = graph.insertVertex(parent, null, 'World!', 200, 150, 80, 30);
    var e1 = graph.insertEdge(parent, null, '', v1, v2);
  } finally {
    // Updates the display
    graph.getModel().endUpdate();
  }
  return graph;
}

const helloWorldGraph = makeHelloWorld();

function graphToXML(graph) {
    var encoder = new mxCodec();
    var result = encoder.encode(graph.getModel());
    return mxUtils.getXml(result);
}

const xml = graphToXML(helloWorldGraph);

fs.writeFileSync('./graph.xml', xml);

到目前为止一切正常——我们可以输出一个 XML 文件。现在我们必须从那里得到一个 SVG。

我创建了一个 SVG 画布,如下所示:

function createSvgCanvas(graph) {
    const svgDoc = mxUtils.createXmlDocument();
    const root = (svgDoc.createElementNS != null) ? svgDoc.createElementNS(mxConstants.NS_SVG, 'svg') : svgDoc.createElement('svg');
    if (svgDoc.createElementNS == null) {
        root.setAttribute('xmlns', mxConstants.NS_SVG);
        root.setAttribute('xmlns:xlink', mxConstants.NS_XLINK);
    } else {
        root.setAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:xlink', mxConstants.NS_XLINK);
    }
    const bounds = graph.getGraphBounds();
    root.setAttribute('width', (bounds.x + bounds.width + 4) + 'px');
    root.setAttribute('height', (bounds.y + bounds.height + 4) + 'px');
    root.setAttribute('version', '1.1');
    svgDoc.appendChild(root);
    const svgCanvas = new mxSvgCanvas2D(root);
    return svgCanvas;
}

问题是:

  1. 新画布的大小适合图表,但上面还没有图表。
  2. 我需要弄清楚如何将 SVG 画布转换为磁盘上的 SVG 文件。

编辑:我在程序末尾添加了以下内容:

const canvas = createSvgCanvas(helloWorldGraph);
const imgExport = new mxImageExport();
imgExport.drawState(helloWorldGraph.getView().getState(helloWorldGraph.model.root), canvas); // adapted from https://jgraph.github.io/mxgraph/docs/js-api/files/util/mxImageExport-js.html

const xml2 = mxUtils.getXml(canvas)

const svgString = '<?xml version="1.0" encoding="UTF-8"?>\n'
+ '<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">\n'
+ xml2
fs.writeFileSync('./graph.svg', svgString);

这让我得到“未捕获的 TypeError:无法在 'XMLSerializer' 上执行 'serializeToString':参数 1 不是 'Node' 类型。” 我尝试了一些东西,但我对 mxGraph 不够熟悉,无法从我的画布中获取它需要的节点。

标签: javascriptsvgmxgraph

解决方案


对于第 1 点,您可以查看提供有关如何设置画布的详细信息的 draw.io 代码。

但是此代码旨在用于浏览器,因此在依赖 JSDOM 的脚本中运行时可能会遇到问题。

您必须使用 mxImageExport 才能使用画布。然后将生成的节点包裹起来,生成svg字符串(下面svgRoot是自定义代码生成的,mxImageExport更新的Element)

'<?xml version="1.0" encoding="UTF-8"?>\n'
 + '<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">\n'
 + mxUtils.getXml(svgRoot)

对于脚本中的第 2 点, 一旦有了 svg 字符串,就可以将其直接写入文件,就像将 mxgraph 模型存储在 xml 文件中一样。

对于浏览器中的第 2 点, 您可以使用 Anchor 元素的下载和 href 属性(https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a#Attributes

1、生成一个href数据,其中包含点1产生的svg字符串的编码值

'data:image/svg+xml' + encodeURIComponent(svg)

然后使用此 href 数据创建锚点,并在单击按钮时触发它。https://ourcodeworld.com/articles/read/189/how-to-create-a-file-and-generate-a-download-with-javascript-in-the-browser-without-a-server中的更多详细信息


推荐阅读