mermaid-render 0.7.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. mermaid_render-0.7.0/.gitignore +19 -0
  2. mermaid_render-0.7.0/.python-version +1 -0
  3. mermaid_render-0.7.0/LICENSE +25 -0
  4. mermaid_render-0.7.0/PKG-INFO +184 -0
  5. mermaid_render-0.7.0/README.md +164 -0
  6. mermaid_render-0.7.0/examples/connected_graph.py +11 -0
  7. mermaid_render-0.7.0/examples/example.mmd +7 -0
  8. mermaid_render-0.7.0/hatch_build.py +31 -0
  9. mermaid_render-0.7.0/pyproject.toml +42 -0
  10. mermaid_render-0.7.0/scripts/build_platform_wheel.py +70 -0
  11. mermaid_render-0.7.0/scripts/check_lightweight_dist.py +28 -0
  12. mermaid_render-0.7.0/scripts/smoke_offline.py +52 -0
  13. mermaid_render-0.7.0/scripts/test_wheel_install.py +29 -0
  14. mermaid_render-0.7.0/scripts/wheel_platform.py +59 -0
  15. mermaid_render-0.7.0/src/mermaid_render/__init__.py +6 -0
  16. mermaid_render-0.7.0/src/mermaid_render/__main__.py +3 -0
  17. mermaid_render-0.7.0/src/mermaid_render/api.py +220 -0
  18. mermaid_render-0.7.0/src/mermaid_render/assets.py +143 -0
  19. mermaid_render-0.7.0/src/mermaid_render/capture.js +139 -0
  20. mermaid_render-0.7.0/src/mermaid_render/cli.py +48 -0
  21. mermaid_render-0.7.0/src/mermaid_render/graph_capture.js +95 -0
  22. mermaid_render-0.7.0/src/mermaid_render/semantic.py +231 -0
  23. mermaid_render-0.7.0/src/mermaid_render/vendor.py +67 -0
  24. mermaid_render-0.7.0/src/mermaid_render/vsdx.py +194 -0
  25. mermaid_render-0.7.0/tests/test_assets.py +101 -0
  26. mermaid_render-0.7.0/tests/test_browser.py +74 -0
  27. mermaid_render-0.7.0/tests/test_bundling.py +96 -0
  28. mermaid_render-0.7.0/tests/test_cli.py +32 -0
  29. mermaid_render-0.7.0/tests/test_formats.py +143 -0
  30. mermaid_render-0.7.0/tests/test_semantic.py +152 -0
  31. mermaid_render-0.7.0/tests/test_vendor.py +48 -0
  32. mermaid_render-0.7.0/tests/test_vsdx.py +69 -0
@@ -0,0 +1,19 @@
1
+ .venv/
2
+ __pycache__/
3
+ .pytest_cache/
4
+ *.py[cod]
5
+ dist/
6
+ build/
7
+ *.egg-info/
8
+ .mypy_cache/
9
+ .ruff_cache/
10
+ .DS_Store
11
+ src/mermaid_render/runtime/**
12
+ !src/mermaid_render/runtime/README.txt
13
+
14
+ src/mermaid_render/browsers/**
15
+ !src/mermaid_render/browsers/README.txt
16
+
17
+ src/mermaid_render/browsers
18
+
19
+ mermaid-dist
@@ -0,0 +1 @@
1
+ 3.13
@@ -0,0 +1,25 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Mermaid Visio Python port contributors
4
+
5
+ This project is a Python adaptation of the Mermaid-to-Visio implementation:
6
+ Copyright (c) 2026 Freddy Beltran <freddy.beltran@klyra.tech>, Klyra (https://klyra.tech),
7
+ and the Stratos platform (https://mystratos.ai)
8
+
9
+ Permission is hereby granted, free of charge, to any person obtaining a copy
10
+ of this software and associated documentation files (the "Software"), to deal
11
+ in the Software without restriction, including without limitation the rights
12
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13
+ copies of the Software, and to permit persons to whom the Software is
14
+ furnished to do so, subject to the following conditions:
15
+
16
+ The above copyright notice and this permission notice shall be included in all
17
+ copies or substantial portions of the Software.
18
+
19
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25
+ SOFTWARE.
@@ -0,0 +1,184 @@
1
+ Metadata-Version: 2.5
2
+ Name: mermaid-render
3
+ Version: 0.7.0
4
+ Summary: Render Mermaid diagrams to SVG, PNG, PDF or connected Visio VSDX
5
+ Author: Python port contributors
6
+ License-Expression: MIT
7
+ License-File: LICENSE
8
+ Keywords: diagram,mermaid,pdf,png,svg,visio,vsdx
9
+ Classifier: Operating System :: MacOS
10
+ Classifier: Operating System :: Microsoft :: Windows
11
+ Classifier: Operating System :: POSIX :: Linux
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3 :: Only
14
+ Classifier: Topic :: Multimedia :: Graphics :: Graphics Conversion
15
+ Requires-Python: >=3.10
16
+ Requires-Dist: filelock<4,>=3.16
17
+ Requires-Dist: platformdirs<5,>=4
18
+ Requires-Dist: playwright==1.63.0
19
+ Description-Content-Type: text/markdown
20
+
21
+ # mermaid-render
22
+
23
+ Render **Mermaid → SVG, PNG and PDF**, and **Mermaid flowcharts → connected Visio VSDX** on Linux, Windows, and macOS. Python uses the official Mermaid.js renderer inside Chromium Headless Shell, controlled through Playwright. Node.js, a browser installation, and Microsoft Visio are **not required on the target machine** when installing a platform-specific **bundled wheel**.
24
+
25
+ The converter is adapted from the MIT-licensed [FBklyra/mermaid-to-visio](https://github.com/FBklyra/mermaid-to-visio). Mermaid itself is MIT licensed; Chromium and Playwright have their own third-party notices and licenses. Keep those notices with redistributed binaries.
26
+
27
+ ## Prebuilt offline wheels via GitHub Actions
28
+
29
+ The workflow [`.github/workflows/bundled-wheels.yml`](.github/workflows/bundled-wheels.yml) builds two distributions using uv:
30
+
31
+ - **PyPI:** a small universal Python wheel and source archive, without Mermaid.js or browser binaries. The first render automatically installs the pinned runtime support files.
32
+ - **GitHub Releases:** the exact PyPI artifacts plus four bundled wheels for Linux x86-64, Windows x86-64, macOS Apple Silicon, and macOS Intel. Bundled wheels contain Mermaid **12.1.0** and the Chromium Headless Shell matching Playwright **1.63.0**, so rendering needs no runtime downloads.
33
+
34
+ A matching version tag (for example `v0.7.0`) attaches all six artifacts to a GitHub Release and publishes only the lightweight artifacts to PyPI through Trusted Publishing. Pull requests, pushes to `main`, and manual runs build and test without publishing.
35
+
36
+ ### Runtime support files
37
+
38
+ Each asset is resolved independently: first from its bundled location inside the installed package (`mermaid_render/runtime` and `mermaid_render/browsers`), then from persistent per-user application data:
39
+
40
+ | OS | Support directory |
41
+ |---|---|
42
+ | macOS | `~/Library/Application Support/mermaid-render` |
43
+ | Linux | `$XDG_DATA_HOME/mermaid-render`, default `~/.local/share/mermaid-render` |
44
+ | Windows | `%LOCALAPPDATA%\mermaid-render` |
45
+
46
+ These are support files, not temporary or cache files. Mermaid uses a versioned `mermaid/<version>` subdirectory; Chromium uses `browsers/playwright-<version>-<os>-<architecture>`. Upgrades keep versions separate. Downloads are staged and validated before installation, and concurrent processes share an installation lock. Rendering reuses existing files without downloading them again.
47
+
48
+ Missing assets install automatically on render. Optionally run `mermaid-render setup` beforehand. Runtime paths are managed internally; the CLI and rendering API have no Mermaid or browser path overrides. If automatic setup fails, the error links directly to the [GitHub Releases](https://github.com/himbeles/mermaid-render/releases) bundled wheels and explains how to install one for offline use.
49
+
50
+ ### Building from GitHub
51
+
52
+ 1. Push this repository to GitHub (including `.github/workflows/bundled-wheels.yml`).
53
+ 2. Select **Actions → mermaid-render distributions → Run workflow**.
54
+ 3. Download your platform's wheel from the run artifacts, or get all four wheels from the GitHub Release created when you push a `v*` tag.
55
+
56
+ ### Install and use (after downloading your platform wheel)
57
+
58
+ ```bash
59
+ uv tool install ./mermaid_render-*-py3-none-macosx_*_arm64.whl
60
+ mermaid-render diagram.mmd -o diagram.svg
61
+ mermaid-render diagram.mmd -o diagram.png --scale 2
62
+ mermaid-render diagram.mmd -o diagram.pdf
63
+ mermaid-render diagram.mmd -o diagram.vsdx
64
+ ```
65
+
66
+ The example installs the macOS Apple Silicon wheel. Choose the wheel matching your operating system and architecture.
67
+
68
+ For the lightweight PyPI installation:
69
+
70
+ ```bash
71
+ uv tool install mermaid-render
72
+ mermaid-render diagram.mmd -o diagram.vsdx
73
+ ```
74
+
75
+ ```python
76
+ from mermaid_render import convert
77
+
78
+ source = """flowchart LR
79
+ A[Input] --> B{Valid?}
80
+ B -->|Yes| C[Process]
81
+ B -->|No| D[Reject]
82
+ """
83
+
84
+ convert(source, "diagram.svg")
85
+ convert(source, "diagram.png", scale=2, background="transparent")
86
+ convert(source, "diagram.pdf")
87
+ convert(source, "diagram.vsdx")
88
+ ```
89
+
90
+ Visio VSDX generation creates editable flowchart shapes and *native connection relationships*, not generic disconnected SVG paths. Only flowcharts currently support connected VSDX export; other Mermaid diagram types can be exported as SVG, PNG or PDF. Connection behavior after editing/moving nodes in desktop Visio is not yet independently verified.
91
+
92
+ ## Output formats
93
+
94
+ `mermaid-render` infers the format from the output extension; use `-f/--format`
95
+ to choose explicitly (`svg`, `png`, `pdf`, `visio`/`vsdx`). The API returns
96
+ bytes and optionally writes the output file:
97
+
98
+ ```bash
99
+ mermaid-render diagram.mmd -o diagram.svg
100
+ mermaid-render diagram.mmd -o diagram.png --scale 2 --background transparent
101
+ mermaid-render diagram.mmd -o diagram.pdf --background white
102
+ mermaid-render diagram.mmd -o diagram.vsdx
103
+ ```
104
+
105
+ - **SVG:** Original Mermaid-generated SVG markup, without rasterization.
106
+ - **PNG:** Chromium screenshot at the Mermaid SVG viewBox size. `--scale 2`
107
+ doubles pixel dimensions (96 to 192 pixels per CSS inch). `--background`
108
+ accepts a CSS color or `transparent`.
109
+ - **PDF:** Chromium print-to-PDF with a tight single diagram-sized page, zero
110
+ margins, and vector SVG content where supported. The `--scale` option applies
111
+ only to PNG. PDF transparency is not guaranteed by Chromium; use SVG or PNG
112
+ when an alpha channel is needed.
113
+ - **VSDX:** Native Visio shapes and connection relationships for Mermaid flowcharts.
114
+ Other Mermaid types deliberately fail rather than writing disconnected shapes.
115
+
116
+ All four formats use the *same official Mermaid.js runtime in headless Chromium*.
117
+ No Node.js process or installed system browser is used by the offline wheels.
118
+
119
+ ## Development using uv
120
+
121
+ ```bash
122
+ uv sync
123
+ uv run pytest -q
124
+ uv run mermaid-render examples/example.mmd --format png -o diagram.png
125
+ ```
126
+
127
+ `uv sync` creates a `.venv` from the committed lockfile. Rendering and browser tests automatically prepare missing runtime support files. The project uses Hatchling with a PEP 621 `pyproject.toml` and `src/` layout.
128
+
129
+ Default builds are always lightweight, even if the checkout contains old bundled files:
130
+
131
+ ```bash
132
+ uv build --out-dir dist/pypi
133
+ uv run python scripts/check_lightweight_dist.py dist/pypi
134
+ ```
135
+
136
+ ### Local platform-specific wheel build
137
+
138
+ On a machine with internet access, with the same OS/architecture as your intended wheel:
139
+
140
+ ```bash
141
+ uv sync
142
+ uv run python scripts/build_platform_wheel.py --outdir dist/bundled
143
+ uv run python scripts/test_wheel_install.py dist/bundled/*.whl
144
+ ```
145
+
146
+ The platform tag is detected automatically. For Linux, install system browser libraries first (`uv run python -m playwright install-deps chromium`). The script downloads the pinned Mermaid distribution, installs the matching headless shell into temporary staging, writes a relative executable manifest, and builds a platform-specific wheel directly with `uv build` and a Hatchling hook. Staging is removed afterward and the source tree is never populated with runtime assets. The wheel retains native executable permissions and includes upstream license assets distributed in the payload.
147
+
148
+ **Linux compatibility:** Linux wheels are available through GitHub Releases only. They use the `linux_x86_64` tag and bundle Chromium without vendoring its system libraries. Install compatible browser dependencies on the target system (`python -m playwright install-deps chromium`). The wheel is tested on Ubuntu 24.04 and is not audited for manylinux compatibility.
149
+
150
+ **macOS caveat:** The wheel tag derives its minimum version from the bundled Mach-O binaries using `otool`; there is no project-defined macOS minimum. CI tests on macOS 15, so older versions are not independently tested. If Gatekeeper imposes restrictions on downloaded unsigned binaries, local signing or organizational policy may be needed.
151
+
152
+ ### Configure PyPI publishing
153
+
154
+ Commit and push the release changes. Set `project.version` to the intended release version, then push the matching tag, for example:
155
+
156
+ ```bash
157
+ git tag v0.7.0
158
+ git push origin v0.7.0
159
+ ```
160
+
161
+ The workflow also runs builds on pull requests and main-branch pushes, but those runs do not publish to PyPI. If a publish run stops after a partial upload, rerun the failed job: uv checks PyPI and skips identical files already uploaded. Published versions cannot be overwritten; use a new version for changed artifacts.
162
+
163
+ After the first successful publication, on any supported operating system:
164
+
165
+ ```bash
166
+ uv tool install mermaid-render
167
+ mermaid-render examples/example.mmd -o diagram.vsdx
168
+ ```
169
+
170
+ ## Development/project structure
171
+
172
+ - `src/mermaid_render/api.py` — Python API and SVG/PNG/PDF rendering
173
+ - `src/mermaid_render/assets.py` — bundled lookup and persistent runtime support installation
174
+ - `src/mermaid_render/graph_capture.js` — Mermaid graph semantics and SVG geometry extraction
175
+ - `src/mermaid_render/semantic.py` — connected Visio shapes/edges with Glue formula references
176
+ - `src/mermaid_render/vsdx.py` — geometry-only VSDX exporter for arbitrary SVG
177
+ - `scripts/build_platform_wheel.py` — real platform wheel build with bundled Mermaid/Chromium
178
+ - `hatch_build.py` and `scripts/wheel_platform.py` — native wheel metadata and binary-derived platform tags
179
+ - `scripts/test_wheel_install.py` and `scripts/smoke_offline.py` — clean-venv install, offline SVG/Visio smoke test
180
+ - `.github/workflows/bundled-wheels.yml` — four-platform build/test/release workflow
181
+
182
+ ## License
183
+
184
+ MIT for the Python port and its source adaptation; see `LICENSE`. Mermaid and Chromium remain third-party projects with their own notices and copyright holders.
@@ -0,0 +1,164 @@
1
+ # mermaid-render
2
+
3
+ Render **Mermaid → SVG, PNG and PDF**, and **Mermaid flowcharts → connected Visio VSDX** on Linux, Windows, and macOS. Python uses the official Mermaid.js renderer inside Chromium Headless Shell, controlled through Playwright. Node.js, a browser installation, and Microsoft Visio are **not required on the target machine** when installing a platform-specific **bundled wheel**.
4
+
5
+ The converter is adapted from the MIT-licensed [FBklyra/mermaid-to-visio](https://github.com/FBklyra/mermaid-to-visio). Mermaid itself is MIT licensed; Chromium and Playwright have their own third-party notices and licenses. Keep those notices with redistributed binaries.
6
+
7
+ ## Prebuilt offline wheels via GitHub Actions
8
+
9
+ The workflow [`.github/workflows/bundled-wheels.yml`](.github/workflows/bundled-wheels.yml) builds two distributions using uv:
10
+
11
+ - **PyPI:** a small universal Python wheel and source archive, without Mermaid.js or browser binaries. The first render automatically installs the pinned runtime support files.
12
+ - **GitHub Releases:** the exact PyPI artifacts plus four bundled wheels for Linux x86-64, Windows x86-64, macOS Apple Silicon, and macOS Intel. Bundled wheels contain Mermaid **12.1.0** and the Chromium Headless Shell matching Playwright **1.63.0**, so rendering needs no runtime downloads.
13
+
14
+ A matching version tag (for example `v0.7.0`) attaches all six artifacts to a GitHub Release and publishes only the lightweight artifacts to PyPI through Trusted Publishing. Pull requests, pushes to `main`, and manual runs build and test without publishing.
15
+
16
+ ### Runtime support files
17
+
18
+ Each asset is resolved independently: first from its bundled location inside the installed package (`mermaid_render/runtime` and `mermaid_render/browsers`), then from persistent per-user application data:
19
+
20
+ | OS | Support directory |
21
+ |---|---|
22
+ | macOS | `~/Library/Application Support/mermaid-render` |
23
+ | Linux | `$XDG_DATA_HOME/mermaid-render`, default `~/.local/share/mermaid-render` |
24
+ | Windows | `%LOCALAPPDATA%\mermaid-render` |
25
+
26
+ These are support files, not temporary or cache files. Mermaid uses a versioned `mermaid/<version>` subdirectory; Chromium uses `browsers/playwright-<version>-<os>-<architecture>`. Upgrades keep versions separate. Downloads are staged and validated before installation, and concurrent processes share an installation lock. Rendering reuses existing files without downloading them again.
27
+
28
+ Missing assets install automatically on render. Optionally run `mermaid-render setup` beforehand. Runtime paths are managed internally; the CLI and rendering API have no Mermaid or browser path overrides. If automatic setup fails, the error links directly to the [GitHub Releases](https://github.com/himbeles/mermaid-render/releases) bundled wheels and explains how to install one for offline use.
29
+
30
+ ### Building from GitHub
31
+
32
+ 1. Push this repository to GitHub (including `.github/workflows/bundled-wheels.yml`).
33
+ 2. Select **Actions → mermaid-render distributions → Run workflow**.
34
+ 3. Download your platform's wheel from the run artifacts, or get all four wheels from the GitHub Release created when you push a `v*` tag.
35
+
36
+ ### Install and use (after downloading your platform wheel)
37
+
38
+ ```bash
39
+ uv tool install ./mermaid_render-*-py3-none-macosx_*_arm64.whl
40
+ mermaid-render diagram.mmd -o diagram.svg
41
+ mermaid-render diagram.mmd -o diagram.png --scale 2
42
+ mermaid-render diagram.mmd -o diagram.pdf
43
+ mermaid-render diagram.mmd -o diagram.vsdx
44
+ ```
45
+
46
+ The example installs the macOS Apple Silicon wheel. Choose the wheel matching your operating system and architecture.
47
+
48
+ For the lightweight PyPI installation:
49
+
50
+ ```bash
51
+ uv tool install mermaid-render
52
+ mermaid-render diagram.mmd -o diagram.vsdx
53
+ ```
54
+
55
+ ```python
56
+ from mermaid_render import convert
57
+
58
+ source = """flowchart LR
59
+ A[Input] --> B{Valid?}
60
+ B -->|Yes| C[Process]
61
+ B -->|No| D[Reject]
62
+ """
63
+
64
+ convert(source, "diagram.svg")
65
+ convert(source, "diagram.png", scale=2, background="transparent")
66
+ convert(source, "diagram.pdf")
67
+ convert(source, "diagram.vsdx")
68
+ ```
69
+
70
+ Visio VSDX generation creates editable flowchart shapes and *native connection relationships*, not generic disconnected SVG paths. Only flowcharts currently support connected VSDX export; other Mermaid diagram types can be exported as SVG, PNG or PDF. Connection behavior after editing/moving nodes in desktop Visio is not yet independently verified.
71
+
72
+ ## Output formats
73
+
74
+ `mermaid-render` infers the format from the output extension; use `-f/--format`
75
+ to choose explicitly (`svg`, `png`, `pdf`, `visio`/`vsdx`). The API returns
76
+ bytes and optionally writes the output file:
77
+
78
+ ```bash
79
+ mermaid-render diagram.mmd -o diagram.svg
80
+ mermaid-render diagram.mmd -o diagram.png --scale 2 --background transparent
81
+ mermaid-render diagram.mmd -o diagram.pdf --background white
82
+ mermaid-render diagram.mmd -o diagram.vsdx
83
+ ```
84
+
85
+ - **SVG:** Original Mermaid-generated SVG markup, without rasterization.
86
+ - **PNG:** Chromium screenshot at the Mermaid SVG viewBox size. `--scale 2`
87
+ doubles pixel dimensions (96 to 192 pixels per CSS inch). `--background`
88
+ accepts a CSS color or `transparent`.
89
+ - **PDF:** Chromium print-to-PDF with a tight single diagram-sized page, zero
90
+ margins, and vector SVG content where supported. The `--scale` option applies
91
+ only to PNG. PDF transparency is not guaranteed by Chromium; use SVG or PNG
92
+ when an alpha channel is needed.
93
+ - **VSDX:** Native Visio shapes and connection relationships for Mermaid flowcharts.
94
+ Other Mermaid types deliberately fail rather than writing disconnected shapes.
95
+
96
+ All four formats use the *same official Mermaid.js runtime in headless Chromium*.
97
+ No Node.js process or installed system browser is used by the offline wheels.
98
+
99
+ ## Development using uv
100
+
101
+ ```bash
102
+ uv sync
103
+ uv run pytest -q
104
+ uv run mermaid-render examples/example.mmd --format png -o diagram.png
105
+ ```
106
+
107
+ `uv sync` creates a `.venv` from the committed lockfile. Rendering and browser tests automatically prepare missing runtime support files. The project uses Hatchling with a PEP 621 `pyproject.toml` and `src/` layout.
108
+
109
+ Default builds are always lightweight, even if the checkout contains old bundled files:
110
+
111
+ ```bash
112
+ uv build --out-dir dist/pypi
113
+ uv run python scripts/check_lightweight_dist.py dist/pypi
114
+ ```
115
+
116
+ ### Local platform-specific wheel build
117
+
118
+ On a machine with internet access, with the same OS/architecture as your intended wheel:
119
+
120
+ ```bash
121
+ uv sync
122
+ uv run python scripts/build_platform_wheel.py --outdir dist/bundled
123
+ uv run python scripts/test_wheel_install.py dist/bundled/*.whl
124
+ ```
125
+
126
+ The platform tag is detected automatically. For Linux, install system browser libraries first (`uv run python -m playwright install-deps chromium`). The script downloads the pinned Mermaid distribution, installs the matching headless shell into temporary staging, writes a relative executable manifest, and builds a platform-specific wheel directly with `uv build` and a Hatchling hook. Staging is removed afterward and the source tree is never populated with runtime assets. The wheel retains native executable permissions and includes upstream license assets distributed in the payload.
127
+
128
+ **Linux compatibility:** Linux wheels are available through GitHub Releases only. They use the `linux_x86_64` tag and bundle Chromium without vendoring its system libraries. Install compatible browser dependencies on the target system (`python -m playwright install-deps chromium`). The wheel is tested on Ubuntu 24.04 and is not audited for manylinux compatibility.
129
+
130
+ **macOS caveat:** The wheel tag derives its minimum version from the bundled Mach-O binaries using `otool`; there is no project-defined macOS minimum. CI tests on macOS 15, so older versions are not independently tested. If Gatekeeper imposes restrictions on downloaded unsigned binaries, local signing or organizational policy may be needed.
131
+
132
+ ### Configure PyPI publishing
133
+
134
+ Commit and push the release changes. Set `project.version` to the intended release version, then push the matching tag, for example:
135
+
136
+ ```bash
137
+ git tag v0.7.0
138
+ git push origin v0.7.0
139
+ ```
140
+
141
+ The workflow also runs builds on pull requests and main-branch pushes, but those runs do not publish to PyPI. If a publish run stops after a partial upload, rerun the failed job: uv checks PyPI and skips identical files already uploaded. Published versions cannot be overwritten; use a new version for changed artifacts.
142
+
143
+ After the first successful publication, on any supported operating system:
144
+
145
+ ```bash
146
+ uv tool install mermaid-render
147
+ mermaid-render examples/example.mmd -o diagram.vsdx
148
+ ```
149
+
150
+ ## Development/project structure
151
+
152
+ - `src/mermaid_render/api.py` — Python API and SVG/PNG/PDF rendering
153
+ - `src/mermaid_render/assets.py` — bundled lookup and persistent runtime support installation
154
+ - `src/mermaid_render/graph_capture.js` — Mermaid graph semantics and SVG geometry extraction
155
+ - `src/mermaid_render/semantic.py` — connected Visio shapes/edges with Glue formula references
156
+ - `src/mermaid_render/vsdx.py` — geometry-only VSDX exporter for arbitrary SVG
157
+ - `scripts/build_platform_wheel.py` — real platform wheel build with bundled Mermaid/Chromium
158
+ - `hatch_build.py` and `scripts/wheel_platform.py` — native wheel metadata and binary-derived platform tags
159
+ - `scripts/test_wheel_install.py` and `scripts/smoke_offline.py` — clean-venv install, offline SVG/Visio smoke test
160
+ - `.github/workflows/bundled-wheels.yml` — four-platform build/test/release workflow
161
+
162
+ ## License
163
+
164
+ MIT for the Python port and its source adaptation; see `LICENSE`. Mermaid and Chromium remain third-party projects with their own notices and copyright holders.
@@ -0,0 +1,11 @@
1
+ """Cross-platform experimental native Visio connector example (no COM)."""
2
+ from pathlib import Path
3
+ from mermaid_render import build_connected_vsdx
4
+
5
+ nodes = [
6
+ {"id": "A", "text": "Input", "x": 20, "y": 30, "width": 120, "height": 55},
7
+ {"id": "B", "text": "Processing", "x": 230, "y": 30, "width": 145, "height": 55},
8
+ {"id": "C", "text": "Output", "x": 470, "y": 30, "width": 120, "height": 55},
9
+ ]
10
+ edges = [{"source": "A", "target": "B"}, {"source": "B", "target": "C"}]
11
+ Path("connected_graph.vsdx").write_bytes(build_connected_vsdx(nodes, edges))
@@ -0,0 +1,7 @@
1
+ flowchart TD
2
+ A[Christmas] -->|Get money| B(Go shopping)
3
+ B --> C{Let me think}
4
+ C -->|One| D[Laptop]
5
+ C -->|Two| E[iPhone]
6
+ C -->|Three| F[Car]
7
+
@@ -0,0 +1,31 @@
1
+ """Write platform wheel metadata directly when a browser is bundled."""
2
+ from __future__ import annotations
3
+
4
+ import sys
5
+ import os
6
+ from pathlib import Path
7
+
8
+ from hatchling.builders.hooks.plugin.interface import BuildHookInterface
9
+
10
+
11
+ class CustomBuildHook(BuildHookInterface):
12
+ def initialize(self, version, build_data):
13
+ # Default builds remain lightweight even in a populated checkout.
14
+ staging = os.environ.get('MERMAID_RENDER_BUNDLE_DIR')
15
+ if not staging:
16
+ return
17
+ staging = Path(staging).resolve()
18
+ browsers = staging / 'browsers'
19
+ runtime = staging / 'runtime'
20
+ if not (browsers / 'BROWSER_INFO.json').is_file() or not (runtime / 'VERSION.json').is_file():
21
+ raise RuntimeError('Bundled build requires complete staged browser and Mermaid assets')
22
+ sys.path.insert(0, str(Path(self.root) / 'scripts'))
23
+ try:
24
+ from wheel_platform import platform_tag
25
+ tag = platform_tag(browsers)
26
+ finally:
27
+ sys.path.pop(0)
28
+ build_data['pure_python'] = False
29
+ build_data['tag'] = f'py3-none-{tag}'
30
+ build_data['force_include'][str(browsers)] = 'mermaid_render/browsers'
31
+ build_data['force_include'][str(runtime)] = 'mermaid_render/runtime'
@@ -0,0 +1,42 @@
1
+ [project]
2
+ name = "mermaid-render"
3
+ version = "0.7.0"
4
+ description = "Render Mermaid diagrams to SVG, PNG, PDF or connected Visio VSDX"
5
+ readme = "README.md"
6
+ requires-python = ">=3.10"
7
+ license = "MIT"
8
+ license-files = ["LICENSE"]
9
+ authors = [{ name = "Python port contributors" }]
10
+ keywords = ["mermaid", "visio", "vsdx", "svg", "png", "pdf", "diagram"]
11
+ classifiers = [
12
+ "Programming Language :: Python :: 3",
13
+ "Programming Language :: Python :: 3 :: Only",
14
+ "Operating System :: Microsoft :: Windows",
15
+ "Operating System :: MacOS",
16
+ "Operating System :: POSIX :: Linux",
17
+ "Topic :: Multimedia :: Graphics :: Graphics Conversion",
18
+ ]
19
+ dependencies = ["playwright==1.63.0", "platformdirs>=4,<5", "filelock>=3.16,<4"]
20
+
21
+ [project.scripts]
22
+ mermaid-render = "mermaid_render.cli:main"
23
+
24
+ [dependency-groups]
25
+ dev = ["pytest>=8,<10"]
26
+
27
+ [build-system]
28
+ requires = ["hatchling>=1.27,<2"]
29
+ build-backend = "hatchling.build"
30
+
31
+ [tool.hatch.build.targets.wheel]
32
+ packages = ["src/mermaid_render"]
33
+ exclude = ["**/browsers/**", "**/runtime/**"]
34
+
35
+ [tool.hatch.build.targets.wheel.hooks.custom]
36
+
37
+ [tool.hatch.build.targets.sdist]
38
+ exclude = ["/src/mermaid_render/runtime/**", "/src/mermaid_render/browsers/**"]
39
+ include = ["/src", "/tests", "/examples", "/scripts", "/hatch_build.py", "/.python-version", "/.gitignore"]
40
+
41
+ [tool.pytest.ini_options]
42
+ testpaths = ["tests"]
@@ -0,0 +1,70 @@
1
+ """Build a platform wheel with Mermaid.js + Chromium Headless Shell inside.
2
+
3
+ Run with `uv run python scripts/build_platform_wheel.py` on
4
+ EACH corresponding native platform. No Node.js, npm, or system Visio required.
5
+ """
6
+ from __future__ import annotations
7
+
8
+ import argparse
9
+ import subprocess
10
+ import os
11
+ import tempfile
12
+ from pathlib import Path
13
+ from zipfile import ZipFile
14
+
15
+ PROJECT = Path(__file__).resolve().parents[1]
16
+ from mermaid_render.assets import find_shell, install_browser
17
+ from mermaid_render.vendor import DEFAULT_VERSION
18
+
19
+
20
+ def build(version: str, outdir: Path) -> Path:
21
+ from mermaid_render.vendor import download_mermaid
22
+ from wheel_platform import platform_tag as detect_platform_tag
23
+
24
+ outdir.mkdir(parents=True, exist_ok=True)
25
+ with tempfile.TemporaryDirectory(prefix="mermaid-bundle-") as temporary:
26
+ staging = Path(temporary)
27
+ runtime = staging / "runtime"
28
+ browsers = staging / "browsers"
29
+ print(f"Downloading Mermaid {version}...", flush=True)
30
+ download_mermaid(runtime, version=version)
31
+ print("Installing Chromium Headless Shell with Playwright...", flush=True)
32
+ install_browser(browsers)
33
+ executable = find_shell(browsers)
34
+ platform_tag = detect_platform_tag(browsers)
35
+ relative = executable.relative_to(browsers).as_posix()
36
+ env = os.environ.copy()
37
+ env["MERMAID_RENDER_BUNDLE_DIR"] = str(staging)
38
+ subprocess.run(["uv", "build", "--wheel", "--out-dir", str(outdir)],
39
+ cwd=PROJECT, env=env, check=True)
40
+ wheels = list(outdir.glob(f"mermaid_render-*-py3-none-{platform_tag}.whl"))
41
+ if len(wheels) != 1:
42
+ raise RuntimeError(f"Expected exactly one newly built wheel, got: {wheels}")
43
+ result = wheels[0]
44
+ with ZipFile(result) as z:
45
+ names = set(z.namelist())
46
+ required = {
47
+ "mermaid_render/runtime/mermaid.esm.min.mjs",
48
+ "mermaid_render/runtime/VERSION.json",
49
+ "mermaid_render/runtime/MERMAID_LICENSE.txt",
50
+ "mermaid_render/browsers/BROWSER_INFO.json",
51
+ "mermaid_render/browsers/" + relative,
52
+ }
53
+ missing = required - names
54
+ if missing:
55
+ raise RuntimeError(f"Wheel missing required runtime files: {sorted(missing)}")
56
+ if z.testzip() is not None:
57
+ raise RuntimeError("Corrupted output wheel")
58
+ print(f"Built offline wheel: {result} ({result.stat().st_size / 1024**2:.1f} MiB)")
59
+ return result
60
+
61
+
62
+ def main():
63
+ parser = argparse.ArgumentParser(description=__doc__)
64
+ parser.add_argument("--outdir", type=Path, default=PROJECT / "dist")
65
+ opts = parser.parse_args()
66
+ build(DEFAULT_VERSION, opts.outdir.resolve())
67
+
68
+
69
+ if __name__ == "__main__":
70
+ main()
@@ -0,0 +1,28 @@
1
+ """Reject runtime payloads or platform wheels in distributions destined for PyPI."""
2
+ import sys
3
+ import tarfile
4
+ from pathlib import Path
5
+ from zipfile import ZipFile
6
+
7
+
8
+ def check(directory: Path) -> None:
9
+ wheels = list(directory.glob('*.whl'))
10
+ sources = list(directory.glob('*.tar.gz'))
11
+ if len(wheels) != 1 or len(sources) != 1 or not wheels[0].name.endswith('-py3-none-any.whl'):
12
+ raise SystemExit('Expected one universal wheel and one source archive')
13
+ for artifact in wheels + sources:
14
+ if artifact.stat().st_size > 10 * 1024**2:
15
+ raise SystemExit(f'Unexpectedly large lightweight distribution: {artifact}')
16
+ if artifact.suffix == '.whl':
17
+ with ZipFile(artifact) as archive:
18
+ names = archive.namelist()
19
+ else:
20
+ with tarfile.open(artifact) as archive:
21
+ names = archive.getnames()
22
+ if any('/mermaid_render/runtime/' in '/' + name or '/mermaid_render/browsers/' in '/' + name for name in names):
23
+ raise SystemExit(f'Runtime assets leaked into {artifact}')
24
+ print(f'{artifact.name}: {artifact.stat().st_size / 1024:.1f} KiB, no runtime payload')
25
+
26
+
27
+ if __name__ == '__main__':
28
+ check(Path(sys.argv[1]))
@@ -0,0 +1,52 @@
1
+ """Run in a clean environment after installing the built wheel.
2
+
3
+ Only local files are allowed: any external HTTP request is rejected by the
4
+ converter's browser request routing when the bundled Mermaid runtime is present.
5
+ """
6
+ import importlib.metadata
7
+ import tempfile
8
+ from io import BytesIO
9
+ from pathlib import Path
10
+ from zipfile import ZipFile
11
+
12
+ from mermaid_render import convert
13
+ import sys
14
+ import mermaid_render.assets as assets
15
+
16
+ root = Path(importlib.metadata.distribution('mermaid-render').locate_file('mermaid_render'))
17
+ lightweight = '--lightweight' in sys.argv
18
+ support = tempfile.TemporaryDirectory()
19
+ assets.data_root = lambda: Path(support.name) / "support"
20
+ if lightweight:
21
+ assert not (root / 'browsers' / 'BROWSER_INFO.json').exists()
22
+ assert not (root / 'runtime' / 'mermaid.esm.min.mjs').exists()
23
+ else:
24
+ assert assets.bundled_browser() is not None
25
+ assert (root / 'runtime' / 'mermaid.esm.min.mjs').is_file()
26
+
27
+
28
+ def no_download(*args, **kwargs):
29
+ raise AssertionError('Rendering tried to download already available runtime assets')
30
+
31
+
32
+ if not lightweight:
33
+ assets.download_mermaid = assets.install_browser = no_download
34
+
35
+ src = 'flowchart LR\n A[Alpha] --> B[Beta]\n B --> C[Gamma]'
36
+ svg = convert(src, format='svg')
37
+ assert b'<svg' in svg
38
+ # The first lightweight render installs missing assets. All later renders must
39
+ # work without any setup downloads, just like a bundled installation.
40
+ assets.download_mermaid = assets.install_browser = no_download
41
+ png = convert(src, format='png', scale=2)
42
+ assert png.startswith(b'\x89PNG\r\n\x1a\n')
43
+ pdf = convert(src, format='pdf')
44
+ assert pdf.startswith(b'%PDF-')
45
+ vsdx = convert(src, format='vsdx')
46
+ with ZipFile(BytesIO(vsdx)) as z:
47
+ assert z.testzip() is None
48
+ page = z.read('visio/pages/page1.xml')
49
+ assert b'<Connects>' in page and b'<Connect ' in page
50
+ print('Offline wheel smoke test passed: SVG, PNG, PDF and connected VSDX.')
51
+
52
+ support.cleanup()