Render Markdown/Image to PIL Images with flexible layout composition
Project description
md2img
Render Markdown or Image to PIL Images with flexible layout composition.
Features
- Render Markdown to PNG: headings, bold/italic, code blocks, lists, tables, blockquotes, links
- Compose multiple images horizontally or vertically with optional Markdown panels and description bars
- Three built-in themes:
default,dark,warm - Pure Python (PIL), no heavy dependencies
Install
pip install md2image
Requires Python 3.8+ and Pillow >= 9.0.
Quick Start
from md2image import render_md, compose_h, compose_v
# Pure Markdown rendering
img = render_md("# Hello\n\n**Bold** and *italic* text.", width=800, theme="dark")
img.save("output.png")
# Compose images horizontally with a Markdown header
from PIL import Image
imgs = [Image.new("RGB", (300, 200), c) for c in ["#4F46E5", "#059669", "#DC2626"]]
result = compose_h(imgs, top_md="# Experiment Results\nThree runs compared.", target_height=220)
result.save("comparison.png")
API
from md2image import render_md, compose_h, compose_v
# Render markdown string to image
render_md(markdown, width=800, theme="default", padding=40)
# Horizontal composition
compose_h(images, target_height=240, top_md=..., bottom_md=..., top_desc=..., bottom_desc=..., theme="default")
# Vertical composition
compose_v(images, target_width=440, left_md=..., right_md=..., md_panel_width=280, theme="default")
Themes: default | dark | warm
Examples
import os
from PIL import Image, ImageDraw
from md2image import (
render_md,
compose_h,
compose_v,
MarkdownRenderer,
ImageCompositor,
THEMES,
)
OUT = "./output"
os.makedirs(OUT, exist_ok=True)
# ─────────────────────────────────────────────
# Helper: generate a solid-color placeholder image
# ─────────────────────────────────────────────
def make_placeholder(w: int, h: int, color: str, label: str) -> Image.Image:
img = Image.new("RGB", (w, h), color)
draw = ImageDraw.Draw(img)
from md_image_renderer import get_font, fw, fh
font = get_font("bold", 20)
tw, th = int(fw(font, label)), fh(font)
draw.text(((w - tw) // 2, (h - th) // 2), label, font=font, fill="#FFFFFF")
return img
# ─────────────────────────────────────────────
# 1. Pure Markdown rendering (all element types)
# ─────────────────────────────────────────────
FULL_MD = """\
# Markdown 渲染演示
## 文字样式
普通段落文字。**粗体文字**,*斜体文字*,***粗斜体***,`行内代码`,
以及 [超链接](https://example.com) 都支持。
## 标题层级
### H3 三级标题
#### H4 四级标题
##### H5 五级标题
###### H6 六级标题
---
## 列表
### 无序列表
- 苹果 — 富含维生素C
- 香蕉 — 富含钾元素
- 香蕉船
- 香蕉蛋糕
- 橙子 — 富含维生素C
### 有序列表
1. 第一步:安装依赖
2. 第二步:配置环境
3. 第三步:运行程序
- 子步骤 A
- 子步骤 B
## 引用块
> **引用示例**
>
> 这是一段 *blockquote* 文字,支持内部的 **Markdown** 格式。
> 可以换行继续引用内容。
## 代码块
```python
def fibonacci(n: int) -> list[int]:
a, b = 0, 1
result = []
for _ in range(n):
result.append(a)
a, b = b, a + b
return result
print(fibonacci(10))
```
## 表格
| 语言 | 类型 | 用途 | 流行度 |
| :--------- | :----: | ----------: | :----- |
| Python | 解释型 | AI/数据分析 | ★★★★★ |
| JavaScript | 解释型 | Web前端 | ★★★★★ |
| Rust | 编译型 | 系统编程 | ★★★★ |
| Go | 编译型 | 后端服务 | ★★★★ |
| TypeScript | 解释型 | 大型项目 | ★★★★ |
"""
print("生成 1. 完整 Markdown 渲染图 (default / dark / warm theme)...")
for theme*name in ("default", "dark", "warm"):
img = render_md(FULL_MD, width=860, theme=theme_name, padding=48)
img.save(f"{OUT}/01_full_md*{theme*name}.png")
print(f" ✓ 01_full_md*{theme_name}.png ({img.width}×{img.height})")
# ─────────────────────────────────────────────
# 2. 横向拼接 — 顶部 MD + 底部描述
# ─────────────────────────────────────────────
print("\n生成 2. 横向拼接图 (顶部MD + 底部描述)...")
imgs_h = [
make_placeholder(320, 220, "#4F46E5", "图片 A"),
make_placeholder(280, 220, "#059669", "图片 B"),
make_placeholder(300, 220, "#DC2626", "图片 C"),
]
TOP_MD = """\
## 横向拼接示例
三张图片**横向排列**,顶部放置渲染后的 Markdown 说明文字。
支持 `inline code`、_斜体_、**粗体** 等格式。
"""
result_h1 = compose_h(
imgs_h,
theme="default",
gap=12,
outer_padding=20,
target_height=240,
top_md=TOP_MD,
bottom_desc="图 1:三张横向拼接图片 — 顶部 Markdown 说明 + 底部描述栏",
)
result_h1.save(f"{OUT}/02_horizontal_top_md_bottom_desc.png")
print(
f" ✓ 02_horizontal_top_md_bottom_desc.png ({result_h1.width}×{result_h1.height})"
)
# 3. 横向拼接 — 顶部描述 + 底部 MD
print("\n生成 3. 横向拼接图 (顶部描述 + 底部MD)...")
BOTTOM_MD = """\
### 数据分析结果
| 指标 | 数值 | 说明 |
| :------- | ----: | :--------- |
| 准确率 | 96.4% | 测试集表现 |
| 召回率 | 94.1% | 正样本覆盖 |
| F1 Score | 0.952 | 综合评分 |
> 模型在验证集上的综合表现 **优秀**,可以部署。
> """
result_h2 = compose_h(
imgs_h,
theme="dark",
gap=16,
outer_padding=24,
target_height=200,
top_desc="实验组 A · B · C 对比结果",
bottom_md=BOTTOM_MD,
)
result_h2.save(f"{OUT}/03_horizontal_top_desc_bottom_md.png")
print(
f" ✓ 03_horizontal_top_desc_bottom_md.png ({result_h2.width}×{result_h2.height})"
)
# 4. 横向拼接 — 全配置(顶部MD + 顶部描述 + 底部描述 + 底部MD)
print("\n生成 4. 横向拼接全配置...")
result_h3 = compose_h(
imgs_h,
theme="warm",
gap=10,
target_height=180,
top_md="## 顶部 Markdown 标题\n项目**对比图**,三组实验并排展示。",
top_desc="⬆ 实验说明",
bottom_desc="⬇ 图 4:横向全配置 Demo",
bottom_md="### 小结\n- 图A:基线模型\n- 图B:改进版本\n- 图C:最终方案",
)
result_h3.save(f"{OUT}/04_horizontal_full.png")
print(f" ✓ 04_horizontal_full.png ({result_h3.width}×{result_h3.height})")
# ─────────────────────────────────────────────
# 5. 竖向拼接 — 左侧MD + 右侧描述
# ─────────────────────────────────────────────
print("\n生成 5. 竖向拼接图 (左侧MD + 右侧描述)...")
imgs_v = [
make_placeholder(400, 180, "#7C3AED", "Step 1"),
make_placeholder(400, 180, "#1D4ED8", "Step 2"),
make_placeholder(400, 180, "#0F766E", "Step 3"),
]
LEFT_MD = """\
## 流程说明
本图展示三个
处理步骤。
**Step 1** — 数据预处理
_Step 2_ — 模型训练
`Step 3` — 结果评估
---
> 每步之间有
> **严格依赖**关系
> """
result_v1 = compose_v(
imgs_v,
theme="default",
gap=12,
outer_padding=20,
target_width=440,
left_md=LEFT_MD,
right_desc="图 5:三步处理流程竖向拼接 — 左侧 Markdown 说明",
md_panel_width=260,
)
result_v1.save(f"{OUT}/05_vertical_left_md_right_desc.png")
print(f" ✓ 05_vertical_left_md_right_desc.png ({result_v1.width}×{result_v1.height})")
# 6. 竖向拼接 — 左侧描述 + 右侧MD
print("\n生成 6. 竖向拼接图 (左侧描述 + 右侧MD)...")
RIGHT_MD = """\
## 参数配置
```python
config = {
"lr": 1e-4,
"epochs": 50,
"batch": 32,
}
```
| 参数 | 值 |
| :----- | ---: |
| lr | 1e-4 |
| epochs | 50 |
| batch | 32 |
"""
result_v2 = compose_v(
imgs_v,
theme="dark",
gap=10,
outer_padding=20,
target_width=420,
left_desc="实验日志 2024-01",
right_md=RIGHT_MD,
md_panel_width=270,
)
result_v2.save(f"{OUT}/06_vertical_left_desc_right_md.png")
print(f" ✓ 06_vertical_left_desc_right_md.png ({result_v2.width}×{result_v2.height})")
# 7. 竖向拼接 — 双侧MD
print("\n生成 7. 竖向拼接全配置 (左侧描述+MD + 右侧MD+描述)...")
result_v3 = compose_v(
imgs_v,
theme="warm",
gap=14,
target_width=380,
left_desc="← 左侧面板",
left_md="## 左侧说明\n\n- **输入**:原始图像\n- **输出**:特征图\n\n> 注意图像分辨率",
right_md="## 右侧注记\n\n1. 验证阶段\n2. 测试阶段\n\n`accuracy > 95%`",
right_desc="右侧面板 →",
md_panel_width=240,
)
result_v3.save(f"{OUT}/07_vertical_full.png")
print(f" ✓ 07_vertical_full.png ({result_v3.width}×{result_v3.height})")
# 8. 单张图片 + MD
print("\n生成 8. 单张图片横向(顶MD+底描述)...")
single = make_placeholder(600, 400, "#B45309", "单张图片")
result_s = compose_h(
[single],
theme="default",
top_md="# 单张图片示例\n\n这是一张**单独**的图片,顶部有 Markdown 标题,底部有描述栏。\n\n- 特性 A\n- 特性 B\n- 特性 C",
bottom_desc="图 8:单张图片 + 顶部 Markdown + 底部描述",
)
result_s.save(f"{OUT}/08_single_image_h.png")
print(f" ✓ 08_single_image_h.png ({result_s.width}×{result_s.height})")
print("\n✅ 所有示例图片已生成至:", OUT)
print("文件列表:")
for f in sorted(os.listdir(OUT)):
if f.endswith(".png"):
path = os.path.join(OUT, f)
size = os.path.getsize(path)
print(f" {f} ({size//1024} KB)")
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
md2image-0.1.4.tar.gz
(18.7 kB
view details)
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
md2image-0.1.4-py3-none-any.whl
(14.5 kB
view details)
File details
Details for the file md2image-0.1.4.tar.gz.
File metadata
- Download URL: md2image-0.1.4.tar.gz
- Upload date:
- Size: 18.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9c8b139ef10592bd93363c832a9adfd97606f2cacee637c19e48ca09a2cd3aef
|
|
| MD5 |
65f237945307ba4df4e8af5b4b60e349
|
|
| BLAKE2b-256 |
3d17573957b431657540371c677d81d5d907d6cf09cc58cba1640d3e7a19702f
|
File details
Details for the file md2image-0.1.4-py3-none-any.whl.
File metadata
- Download URL: md2image-0.1.4-py3-none-any.whl
- Upload date:
- Size: 14.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5b76e0fd0782ec1002b6754bcd68bd1565e3614e308effc3acb97188584dea35
|
|
| MD5 |
1f6b2ab49d1812d29b2c4663a4e55bab
|
|
| BLAKE2b-256 |
79b95635d954fca0a57e2a462a293b4a4a14ea30f6ab9bb361d5757989cb34b0
|