merman: 无头 Mermaid.js,使用 Rust 实现
摘要
Merman 是 Mermaid.js 的一个无头 Rust 实现,用于解析、布局和渲染图表(SVG、光栅图、ASCII),无需浏览器或 JavaScript 运行时,目标是与 Mermaid.js v11.15.0 功能一致。
查看缓存全文
缓存时间: 2026/06/11 17:35
Latias94/merman
来源:https://github.com/Latias94/merman
merman
Mermaid.js(https://mermaid.js.org/)的无头 Rust 实现。
CI(https://github.com/Latias94/merman/actions/workflows/ci.yml) Crates.io(https://crates.io/crates/merman) 文档(https://docs.rs/merman) Crates.io 下载量(https://crates.io/crates/merman) Rust 构建(https://www.rust-lang.org) 许可证:MIT(https://opensource.org/licenses/MIT) 许可证:Apache 2.0(https://opensource.org/licenses/Apache-2.0)
Mermaid.js(https://mermaid.js.org/)是一个 JavaScript 图表工具,可将 flowchart TD; A-->B; 等文本转换为 Markdown、文档和 Web 应用的图表。Merman 是一个专注于一致性的无头 Rust 实现,用于解析、布局和无浏览器渲染。它针对 [email protected],可输出语义 JSON、布局 JSON、SVG、光栅格式以及 ASCII/Unicode 输出,且无需启动浏览器或 JavaScript 运行时即可渲染图表。
在浏览器中试用:Merman 游乐场(https://frankorz.com/merman/)。
一致性通过金语义/布局快照和上游 SVG DOM 基线进行强制保证,因此影响语义、布局或渲染的更改都会被捕获并审查。
项目说明:Merman 与 Mermaid 项目(https://github.com/mermaid-js/mermaid)或其维护者无关联、未经其认可或赞助。它是一个由 Mermaid 用户开发的独立兼容性实现。本仓库中的许多示例和测试夹具均摘自 Mermaid 文档或测试,或原封不动使用,或针对本地上下文稍作更新;有关 Mermaid 许可证和出处说明,请参见 THIRD_PARTY_NOTICES.md(https://github.com/Latias94/merman/blob/main/THIRD_PARTY_NOTICES.md)。
选择你的入口
| 你想… | 起点 | 说明 |
|---|---|---|
| 在浏览器中试用或分享 Mermaid 图表 | Merman 游乐场(https://frankorz.com/merman/) | 由 wasm 网页包驱动的静态在线编辑器。 |
| 从 Rust 渲染 Mermaid | merman(https://crates.io/crates/merman) | 启用 render 可获得 SVG,ascii 可获得终端文本,raster 可获得 PNG/JPG/PDF。 |
| 使用命令行工具 | merman-cli(https://crates.io/crates/merman-cli) | 检测、解析、布局、渲染 SVG、渲染光栅格式以及渲染 ASCII/Unicode 文本。 |
| 在 Rust API 文档中渲染图表 | merman-rustdoc(https://crates.io/crates/merman-rustdoc) | 用于 rustdoc 的过程宏集成,可将 Mermaid 代码围栏转换为内联无头 SVG。 |
| 嵌入浏览器或 TypeScript 应用 | @mermanjs/web(https://github.com/Latias94/merman/tree/main/platforms/web#readme) | wasm-bindgen 输出加 TypeScript 辅助函数,用于 SVG、JSON、验证、元数据和 DOM 渲染。 |
| 构建 Typst 插件/包 | merman-typst-plugin(https://github.com/Latias94/merman/tree/main/crates/merman-typst-plugin#readme) | 实验性 wasm-minimal-protocol 传输,适用于 Typst 兼容的 WASM 宿主。 |
| 解析 Mermaid 或生成语义 JSON | merman-core(https://crates.io/crates/merman-core) | 解析器、元数据、语义 JSON 以及类型化渲染模型,无布局/渲染依赖。 |
| 从 C、C++、Swift、Kotlin、Dart、Python 或其他原生宿主嵌入 | merman-ffi(https://crates.io/crates/merman-ffi) | 稳定的 C ABI 加平台封装器。参见 FFI 协议(https://github.com/Latias94/merman/blob/main/docs/bindings/FFI_PROTOCOL.md)、Android(https://github.com/Latias94/merman/blob/main/docs/bindings/ANDROID_JNI.md)、Apple(https://github.com/Latias94/merman/blob/main/docs/bindings/APPLE_SWIFT.md)、Flutter/Dart(https://github.com/Latias94/merman/blob/main/docs/bindings/FLUTTER_DART_FFI.md)和 Python UniFFI(https://github.com/Latias94/merman/blob/main/docs/bindings/PYTHON_UNIFFI.md)。 |
| 处理布局/渲染内部实现 | merman-render(https://crates.io/crates/merman-render) | 底层布局和 SVG 堆栈,由公共 merman 外观使用。 |
Merman 的输出
- Mermaid 图表的语义 JSON。
- 带有计算几何和路径的布局 JSON。
- 来自完全无头 Rust 渲染器的类 Mermaid SVG。
- 适用于终端、日志和文档片段的 ASCII/Unicode 图表。
- 通过 SVG 栅格化/转换生成的 PNG、JPG 和 PDF。
图表覆盖范围和当前一致性状态位于 docs/alignment/STATUS.md(https://github.com/Latias94/merman/blob/main/docs/alignment/STATUS.md)。
示例输出
| 架构图 | 思维导图 | 桑基图 |
|---|---|---|
这些图像由 merman-cli 从 Mermaid 测试夹具无头渲染。请参见 展示 获取源图表及更多渲染示例。
性能
merman 包含一个基于测试语料库的基准测试工具,用于比较原生 merman、mermaid-rs-renderer 和上游 Mermaid JS v11.15.0。在 Apple M4 上本地运行的“标准”暖渲染套件中,merman 测量了所有 34 个请求的测试夹具,在成功的 Mermaid JS 案例中,渲染时间约为 Mermaid JS 的 1.8% 到 23.0%,大致快 4.3 倍到 56.4 倍,中位数加速比约为 15.8 倍。性能数据不能替代 SVG 一致性。缺失、跳过、出错以及质量比较结果由基准测试工具单独报告。有关方法和命令,请参见 docs/performance/BENCHMARKING.md(https://github.com/Latias94/merman/blob/main/docs/performance/BENCHMARKING.md)。
安装
# 命令行工具
cargo install merman-cli --version 0.8.0-alpha.1
# Rust 库:SVG 渲染
cargo add [email protected] --features render
# Rust 库:ASCII/Unicode 文本输出
cargo add [email protected] --features ascii
# Rust 库:SVG + PNG/JPG/PDF
cargo add [email protected] --features raster
# Rustdoc 集成
cargo add [email protected] --optional
# 浏览器 / TypeScript 包
npm install @mermanjs/web
# Flutter 包
flutter pub add merman
# Python 包(实验性 UniFFI wheels)
pip install merman
关于 rustdoc 功能设置和示例,请参见 crates/merman-rustdoc/README.md。
从本地检出:
cargo install --path crates/merman-cli
cargo build -p merman-ffi --release
使用 crates/merman-ffi/include/merman.h(https://github.com/Latias94/merman/blob/main/crates/merman-ffi/include/merman.h)并链接来自 target/release 的平台特定库构件,用于原生嵌入。
MSRV 为 rust-version = 1.95。
目录
- 选择你的入口
- Merman 的输出
- 示例输出
- 性能
- 安装
- 快速入门(库)
- Rust 示例
- 快速入门(CLI)
- 库 API 详情
- 快速入门(FFI 和原生宿主)
- 数学标签
- ASCII/Unicode 文本输出
- 开发
- 展示
- 一致性与覆盖率
- 质量门
- 局限性
- 功能面
- 架构说明
- 工作区 crate
- 星历史
- 链接
快速入门(库)
对于大多数 Rust 应用,从 merman::render::HeadlessRenderer 开始:
use merman::render::HeadlessRenderer;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let renderer = HeadlessRenderer::new().with_diagram_id("readme-example");
let svg = renderer
.render_svg_sync("flowchart TD\nA[Start] --> B[Done]")?
.unwrap();
println!("{svg}");
Ok(())
}
当你需要与 Mermaid 一致性 SVG 时,使用 render_svg_sync()。当结果将被栅格化或由不支持 <use> 的 SVG 引擎显示时,使用 render_svg_resvg_safe_sync()。使用 ascii 功能和 merman::ascii::HeadlessAsciiRenderer 获取终端文本输出。
Rust 示例
crates/merman/examples 中的程序按渐进式 Rust 集成路径排序。每个示例在提供 Mermaid 源时从 stdin 读取,否则回退到一个小的内置图表。当 stdin 是交互式终端时,示例 01 到 08 和 11 不会等待输入;它们会向 stderr 打印一条简短说明并渲染其内置示例。请参见 crates/merman/examples 目录及其 README.md 获取可复制命令,包括自定义 stdin 和输出文件。
| 步骤 | 目标 | 功能 | 命令 |
|---|---|---|---|
| 01 | 使用高级外观渲染 SVG | render | cargo run -p merman --features render --example example_01_svg_basic > out.svg |
| 02 | 将 Mermaid 解析为语义 JSON | 无 | cargo run -p merman --example example_02_semantic_json |
| 03 | 生成布局 JSON | render | cargo run -p merman --features render --example example_03_layout_json |
| 04 | 渲染终端文本 | ascii | cargo run -p merman --features ascii --example example_04_ascii_output |
| 05 | 从 Rust 渲染 PNG | raster | cargo run -p merman --features raster --example example_05_raster_output -- target/example.png |
| 06 | 应用 SVG 输出流水线 | render | cargo run -p merman --features render --example example_06_svg_pipeline > pipeline.svg |
| 07 | 使用 Mermaid 主题变量和 themeCSS | render | cargo run -p merman --features render --example example_07_theme_css > themed.svg |
| 08 | 使时间敏感的甘特图解析确定性 | 无 | cargo run -p merman --example example_08_deterministic_gantt |
| 09 | 内联多个图表而不发生 SVG id 冲突 | render | cargo run -p merman --features render --example example_09_multiple_diagrams |
| 10 | 通过 egui 与桌面 GUI 宿主集成 | egui-example | cargo run -p merman --features egui-example --example example_10_integration_egui |
| 11 | 构建自定义宿主输出环境 | render | cargo run -p merman --features render --example example_11_custom_output_environment > host-preview.svg |
egui 示例故意设计为主机集成骨架而非完整游乐场:它保留一个长生命周期的渲染器,编辑 Mermaid 源,预览光栅纹理,报告渲染错误,并保存 SVG/PNG 输出。
快速入门(CLI)
# 检测图表类型
merman-cli detect path/to/diagram.mmd
# 解析 -> 语义 JSON
merman-cli parse path/to/diagram.mmd --pretty
# 布局 -> 布局 JSON
merman-cli layout path/to/diagram.mmd --pretty
# 渲染 SVG
merman-cli render path/to/diagram.mmd --out out.svg
# 渲染终端文本输出
merman-cli render --format unicode path/to/diagram.mmd
merman-cli render --format ascii path/to/diagram.mmd
# 终端文本支持常见流程图方向、标签、形状和简单子图
printf "flowchart TB\nsubgraph one\nA((Start)) -- go --> B[(DB)]\nend\n" | merman-cli render --format ascii -
# 渲染光栅格式
merman-cli render --format png --out out.png path/to/diagram.mmd
merman-cli render --format jpg --out out.jpg path/to/diagram.mmd
merman-cli render --format pdf --out out.pdf path/to/diagram.mmd
最小端到端示例:
cat > example.mmd <<'EOF'
flowchart TD
A[Start] --> B{Decision}
B -->|Yes| C[Do thing]
B -->|No| D[Do other thing]
EOF
merman-cli render example.mmd --out example.svg
merman-cli render --format ascii example.mmd
@'
flowchart TD
A[Start] --> B{Decision}
B -->|Yes| C[Do thing]
B -->|No| D[Do other thing]
'@ | Set-Content -Encoding utf8 example.mmd
merman-cli render example.mmd --out example.svg
库 API 详情
merman(https://crates.io/crates/merman)crate 是 merman-core(https://crates.io/crates/merman-core)(解析)和输出 crate(如 merman-render(https://crates.io/crates/merman-render)(布局 + SVG)和 merman-ascii(https://crates.io/crates/merman-ascii)(ASCII/Unicode 文本))的便捷包装器。当你需要布局 + SVG 时启用 render 功能,需要文本输出时启用 ascii,需要从 Rust 获取 PNG/JPG/PDF 时启用 raster(无需 CLI)。
use merman_core::{Engine, ParseOptions};
use merman::render::{
headless_layout_options, render_svg_sync, sanitize_svg_id, SvgRenderOptions,
};
fn main() -> Result<(), Box<dyn std::error::Error>> {
let engine = Engine::new();
let layout = headless_layout_options();
// 对于内联多个图表的 UI,设置每个图表的 SVG id 以避免内部 `<use>`
// 和可访问性 id 冲突。
let svg_opts = SvgRenderOptions {
diagram_id: Some(sanitize_svg_id("example-diagram")),
..SvgRenderOptions::default()
};
// 无需执行器的同步入口点(工作为 CPU 密集型,不执行 I/O)。
let svg = render_svg_sync(
&engine,
"flowchart TD; A-->B;",
ParseOptions::default(),
&layout,
&svg_opts,
)?
.unwrap();
println!("{svg}");
Ok(())
}
如果你更喜欢捆绑的“流水线”而非每次调用传递多个选项结构体,请使用 merman::render::HeadlessRenderer。如果你已经知道图表类型(例如来自 Markdown 围栏信息字符串),请优先使用 Engine::parse_diagram_with_type_sync(...) 以跳过类型检测。
如果你的下游渲染器不支持 SVG <use>(常见于栅格化器),请优先使用 HeadlessRenderer::render_svg_resvg_safe_sync()。当你希望保留原始 <use> 节点并添加尽力而为的 <text> 后备覆盖时,请使用 HeadlessRenderer::render_svg_readable_sync()。
当你启用 raster 功能时,PNG/JPG 转换是感知目标并有预算的。Mermaid SVG 可能合法地具有非常大的 viewBox;浏览器预览通常在该矢量 SVG 内绘制一个较小的容器,而无头 PNG/JPG 路径必须分配具体的像素图。使用 RasterOptions::with_fit_to(...) 获取预览大小的输出,使用 scale 获取设备像素比,使用 RasterSizeLimit 获取最终像素图预算。默认 PNG/JPG 预算将输出限制为每边 8192px 和 8192*8192 像素;受信任的超大导出可以调用 RasterOptions::with_unbounded_size()。
可运行的光栅示例:
cargo run -p merman --features raster --example example_05_raster_output
printf "flowchart LR\nA --> B\n" | \
cargo run -p merman --features raster --example example_05_raster_output -- target/example.png
这种分离是有意为之的:
render_svg_sync用于 Mermaid 一致性快照和需要原始 SVG 契约的调用者。render_svg_readable_sync用于可以保留<use>但仍需要可读后备文本的内联预览。render_svg_resvg_safe_sync或SvgPipeline::resvg_safe()用于 PNG/JPG/PDF 导出以及基于resvg/usvg构建的工具。SvgPostprocessor和ScopedCssPostprocessor用于需要在内置预设之后进行产品特定主题或清理操作的宿主应用。
render_svg_sync 默认保持 Mermaid 一致性。对于面向消费者的输出,请使用显式 SVG 流水线:
use merman::render::{
CssOverridePolicy, HeadlessRenderer, ScopedCssPostprocessor, SvgPipeline,
};
let renderer = HeadlessRenderer::new().with_diagram_id("readme-diagram");
let pipeline = SvgPipeline::resvg_safe().with_postprocessor(
ScopedCssPostprocessor::new(
r#"
.node rect { stroke: #2563eb; stroke-width: 2px; }
.merman-foreignobject-fallback-text { fill: #111827; }
"#,
)
.with_override_policy(CssOverridePolicy::StripExistingImportant),
);
let svg = renderer
.render_svg_with_pipeline_sync("flowchart TD; A[Layer 7\\nHTTP]-->B;", &pipeline)?
.unwrap();
# Ok::<(), Box<dyn std::error::Error>>(())
有关预设行为、可读取图表类型/标题/svg id 的自定义后处理器以及作用域 CSS 示例,请参见 docs/rendering/SVG_OUTPUT_PIPELINE.md(https://github.com/Latias94/merman/blob/main/docs/rendering/SVG_OUTPUT_PIPELINE.md)。
可运行示例:
cargo run -p merman --features render --example example_06_svg_pipeline < fixtures/flowchart/basic.mmd > out.svg
cargo run -p merman --features render --example example_11_custom_output_environment > host-preview.svg
快速入门(FFI 和原生宿主)
merman-ffi(https://crates.io/crates/merman-ffi)crate 为非 Rust 宿主提供了稳定的 C ABI。当前 FF
(注:原文在“当前 FF”处截断,但根据上下文应继续翻译。由于用户只提供了英文原文的这部分,我按原文翻译至此。如果后续内容缺失,可能原文不完整,但翻译已完成到提供的文本末尾。)
相似文章
Mermaid 转 Unicode 框线艺术 (grok-mermaid)
Simon Willison 使用 WebAssembly 构建了一个基于浏览器的工具,用于将 Mermaid 图表转换为 Unicode 框线艺术,灵感来自 Grok CLI 编码代理的开源 Rust 组件。
Mermaid转ASCII艺术 (mermaid-ascii)
一个Web工具,将Mermaid图表语法转换为ASCII和Unicode框线艺术,完全在浏览器中运行,使用编译为WebAssembly的Go库实现。支持流程图、时序图、颜色和可自定义输出。
@wsl8297: Mermaid 画流程图、时序图确实省事,但默认渲染谈不上好看,更别说在终端里几乎没法用。 我在 GitHub 挖到一个新开源项目:beautiful-mermaid,专门把 Mermaid 的渲染这件事做漂亮、做实用——既能导出精致的 …
beautiful-mermaid 是一个纯 TypeScript 实现的开源工具,能将 Mermaid 图表渲染为精美的 SVG 或终端友好的 ASCII 字符画,支持 15 套主题和零 DOM 依赖,适合在 AI 辅助编程中使用。
Show HN:免费Mermaid图表编辑器
一款免费、无需注册的Mermaid图表编辑器,支持实时预览、即时导出SVG/PNG,以及完全在浏览器中运行的分享链接。可选连接GitHub以从源代码自动生成架构图。
Show HN:Line9 —— 拥有自有布局算法的 Mermaid 渲染引擎
Line9 是一款新的 Mermaid 渲染引擎,拥有自己的布局算法,可作为 CLI 和图表编辑器使用。