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

CrateWhat it isWhat it can use
the libraryThe conversion, at the root of the workspaceNothing. No files, clock, random numbers, threads or environment
accent-draw-cliThe accent-draw commandFiles, arguments, exit codes
accent-draw-wasmThe binding for the browserThe data that the page gives it
accent-draw-miroThe Miro command, for foldersFiles

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

  1. SVG to .drawio - this project. The first conversion loses some data, and the page tells you this.
  2. 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}.
  3. Back to SVG with {action: "export", format: "xmlsvg"}. The result is an SVG image with the diagram in the content attribute 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.