javascript - 从 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;
}
问题是:
- 新画布的大小适合图表,但上面还没有图表。
- 我需要弄清楚如何将 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 不够熟悉,无法从我的画布中获取它需要的节点。
解决方案
对于第 1 点,您可以查看提供有关如何设置画布的详细信息的 draw.io 代码。
- https://github.com/jgraph/drawio/blob/v13.2.3/src/main/webapp/js/diagramly/EditorUi.js#L1756
- https://github.com/jgraph/drawio/blob/v13.2.3/src/main/webapp/js/mxgraph/Graph.js#L7780
但是此代码旨在用于浏览器,因此在依赖 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中的更多详细信息
推荐阅读
- reactjs - 抛出错误给我 API 错误,而不是我写的
- parsing - 为 wiki 制作一个更简单的解析器函数
- c++ - 如何在 C++ 中描述文件的绝对路径?
- codesynthesis - 你只能用可解的类型合成吗?
- google-cloud-platform - 改进语音到文本的识别
- javascript - 如何使用 Electron 创建一个真正的自定义安装程序?
- python - 将 python 函数部署到 Azure 函数
- python - 如何在单元测试中传递函数
- python - 提取“全名”字段中最后一个单词的首字母。- Python
- python - 设置 matplotlib 子图的大小