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.
- mermaid_render-0.7.0/.gitignore +19 -0
- mermaid_render-0.7.0/.python-version +1 -0
- mermaid_render-0.7.0/LICENSE +25 -0
- mermaid_render-0.7.0/PKG-INFO +184 -0
- mermaid_render-0.7.0/README.md +164 -0
- mermaid_render-0.7.0/examples/connected_graph.py +11 -0
- mermaid_render-0.7.0/examples/example.mmd +7 -0
- mermaid_render-0.7.0/hatch_build.py +31 -0
- mermaid_render-0.7.0/pyproject.toml +42 -0
- mermaid_render-0.7.0/scripts/build_platform_wheel.py +70 -0
- mermaid_render-0.7.0/scripts/check_lightweight_dist.py +28 -0
- mermaid_render-0.7.0/scripts/smoke_offline.py +52 -0
- mermaid_render-0.7.0/scripts/test_wheel_install.py +29 -0
- mermaid_render-0.7.0/scripts/wheel_platform.py +59 -0
- mermaid_render-0.7.0/src/mermaid_render/__init__.py +6 -0
- mermaid_render-0.7.0/src/mermaid_render/__main__.py +3 -0
- mermaid_render-0.7.0/src/mermaid_render/api.py +220 -0
- mermaid_render-0.7.0/src/mermaid_render/assets.py +143 -0
- mermaid_render-0.7.0/src/mermaid_render/capture.js +139 -0
- mermaid_render-0.7.0/src/mermaid_render/cli.py +48 -0
- mermaid_render-0.7.0/src/mermaid_render/graph_capture.js +95 -0
- mermaid_render-0.7.0/src/mermaid_render/semantic.py +231 -0
- mermaid_render-0.7.0/src/mermaid_render/vendor.py +67 -0
- mermaid_render-0.7.0/src/mermaid_render/vsdx.py +194 -0
- mermaid_render-0.7.0/tests/test_assets.py +101 -0
- mermaid_render-0.7.0/tests/test_browser.py +74 -0
- mermaid_render-0.7.0/tests/test_bundling.py +96 -0
- mermaid_render-0.7.0/tests/test_cli.py +32 -0
- mermaid_render-0.7.0/tests/test_formats.py +143 -0
- mermaid_render-0.7.0/tests/test_semantic.py +152 -0
- mermaid_render-0.7.0/tests/test_vendor.py +48 -0
- 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,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()
|