Architecture
One library without I/O, three hosts with I/O, and a scene in the middle. The scene contains no SVG data and no draw.io data.
The structure
SVG text ─parse─▶ resolved elements ─scene─▶ Scene ─layout─▶ Scene ─emit─▶ mxfile XML
The scene in the middle is the important part. It contains no SVG data and no draw.io data. Thus you can test two parts separately: the part that reads a drawing, and the part that decides what the drawing shows. A profile can also make the scene directly, and it does not have to write draw.io data itself.
The crates
| Crate | What it is | What it can use |
|---|---|---|
| the library | The conversion, at the root of the workspace | Nothing. No files, clock, random numbers, threads or environment |
accent-draw-cli | The accent-draw command | Files, arguments, exit codes |
accent-draw-wasm | The binding for the browser | The data that the page gives it |
accent-draw-miro | The Miro command, for folders | Files |
A lint enforces the limit of the library. The lint configuration does not let the library use the standard library for these five things. A host that needs them has its own configuration that lets it use them. Thus the WebAssembly build works without shims, and the same input gives the same bytes in a browser and in a shell.
Why not usvg
usvg is the usual crate to read SVG in Rust, but it is not correct for this
work. It is for renderers. It changes rect, circle and ellipse into paths.
It changes markers into plain lines. It must load fonts to keep text. But a
diagram converter must keep exactly this data.
Thus accent-draw uses roxmltree for the tree, and svgtypes for path data,
transforms, colors and lengths. It uses simplecss for the style sheet rules.
The style rules and the transforms are code in this project. The tests use
resvg as the reference rasterizer, which is a different job.
Output that does not change
The cell ids are stable, and all numbers have one format. Thus:
- Two conversions of the same SVG file give the same file, byte for byte.
- A change to the converter gives a diff over the full corpus that you can read.
- We can compare the browser and the command line exactly. For each change, a test converts the corpus with the WebAssembly build in a WASI runtime. Then it compares the result with the native build.
The round trip
- SVG to
.drawio- this project. The first conversion loses some data, and the page tells you this. - Edit in draw.io, in a web page. The editor is an iframe on the origin that
you set. The page sends it
{action: "load", xml}. - Back to SVG with
{action: "export", format: "xmlsvg"}. The result is an SVG image with the diagram in thecontentattribute of its root.
Thus accent-draw does not convert the image in an SVG file that contains a diagram. It gives back that diagram without a change. The first conversion is an approximation, and each round trip after it is exact.
Edit with a different draw.io
The draw.io documentation tells you that embed mode works only on
embed.diagrams.net. But we tested a draw.io 31.4.6 on our own server. The full
procedure worked without a change: open, load, edit, export and download. It
worked in three configurations:
- from the official Docker image
- from the webapp in that image, as static files
- from those static files, under a path prefix
Thus you can use your own draw.io, but obey these two cautions. First, nothing
tells you that the next draw.io release keeps this function. Use a fixed
version, and test the editor again after each upgrade. Second, the image does
not send a header that tells which pages can show it in a frame. Its policy is
in a <meta> tag, and a <meta> tag cannot control this. Thus your server must send
Content-Security-Policy: frame-ancestors <the origin of your page>.
We also examined a port of the editor to Rust and WebAssembly. We did not do the port. draw.io has about 200,000 lines of JavaScript without its shape libraries. The converter has a few thousand lines. draw.io also had about 24 releases in the three months of the study.
Our recommendation: do not make a port. Use your
own draw.io when the content must stay in your organization. Keep
embed.diagrams.net as the default.
How we test it
We look at the result. For each change, we convert the full corpus and show each source SVG file next to its diagram. We found many problems in this project that we did not see in the XML. We saw them immediately in an image. This is the reason for this method.
We count. A selfcheck run gives these data for each file:
- the number of cells
- the shapes that moved
- the style keys that it added or removed
- the edges that lost their connection
"No shape moved in the full corpus" is a strong statement. "It looks correct in one file" is not.
We compare with other tools. First, we ran two other converters on the same corpus: one in JavaScript, one in Python. Their numbers are the minimum that accent-draw must reach. We count the arrows that connect to real shapes, and the labels in the shapes that they belong to. An arrow that connects to the page and not to a shape does not count. If you move the page, such an arrow moves too.
We compare with expected data. One of those projects publishes its scene for each of its samples. The scene tells which arrow connects which shape, which text became which label, and which shape is in which frame. These data are the expected results for the layout stage, and a test checks the layout against them.