merman: 无头 Mermaid.js,使用 Rust 实现

Lobsters Hottest 工具

摘要

Merman 是 Mermaid.js 的一个无头 Rust 实现,用于解析、布局和渲染图表(SVG、光栅图、ASCII),无需浏览器或 JavaScript 运行时,目标是与 Mermaid.js v11.15.0 功能一致。

<p><a href="https://lobste.rs/s/qchywn/merman_mermaid_js_headless_rust">评论</a></p>
查看原文
查看缓存全文

缓存时间: 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 渲染 Mermaidmerman(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 或生成语义 JSONmerman-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。

目录

快速入门(库)

对于大多数 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使用高级外观渲染 SVGrendercargo 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生成布局 JSONrendercargo run -p merman --features render --example example_03_layout_json
04渲染终端文本asciicargo run -p merman --features ascii --example example_04_ascii_output
05从 Rust 渲染 PNGrastercargo run -p merman --features raster --example example_05_raster_output -- target/example.png
06应用 SVG 输出流水线rendercargo run -p merman --features render --example example_06_svg_pipeline > pipeline.svg
07使用 Mermaid 主题变量和 themeCSSrendercargo 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 冲突rendercargo run -p merman --features render --example example_09_multiple_diagrams
10通过 egui 与桌面 GUI 宿主集成egui-examplecargo run -p merman --features egui-example --example example_10_integration_egui
11构建自定义宿主输出环境rendercargo 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's Blog

Simon Willison 使用 WebAssembly 构建了一个基于浏览器的工具,用于将 Mermaid 图表转换为 Unicode 框线艺术,灵感来自 Grok CLI 编码代理的开源 Rust 组件。

Mermaid转ASCII艺术 (mermaid-ascii)

Simon Willison's Blog

一个Web工具,将Mermaid图表语法转换为ASCII和Unicode框线艺术,完全在浏览器中运行,使用编译为WebAssembly的Go库实现。支持流程图、时序图、颜色和可自定义输出。

@wsl8297: Mermaid 画流程图、时序图确实省事,但默认渲染谈不上好看,更别说在终端里几乎没法用。 我在 GitHub 挖到一个新开源项目:beautiful-mermaid,专门把 Mermaid 的渲染这件事做漂亮、做实用——既能导出精致的 …

X AI KOLs Timeline

beautiful-mermaid 是一个纯 TypeScript 实现的开源工具,能将 Mermaid 图表渲染为精美的 SVG 或终端友好的 ASCII 字符画,支持 15 套主题和零 DOM 依赖,适合在 AI 辅助编程中使用。

Show HN:免费Mermaid图表编辑器

Hacker News Top

一款免费、无需注册的Mermaid图表编辑器,支持实时预览、即时导出SVG/PNG,以及完全在浏览器中运行的分享链接。可选连接GitHub以从源代码自动生成架构图。