Skip to main content

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


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)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

md2image-0.1.4-py3-none-any.whl (14.5 kB view details)

Uploaded Python 3

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

Hashes for md2image-0.1.4.tar.gz
Algorithm Hash digest
SHA256 9c8b139ef10592bd93363c832a9adfd97606f2cacee637c19e48ca09a2cd3aef
MD5 65f237945307ba4df4e8af5b4b60e349
BLAKE2b-256 3d17573957b431657540371c677d81d5d907d6cf09cc58cba1640d3e7a19702f

See more details on using hashes here.

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

Hashes for md2image-0.1.4-py3-none-any.whl
Algorithm Hash digest
SHA256 5b76e0fd0782ec1002b6754bcd68bd1565e3614e308effc3acb97188584dea35
MD5 1f6b2ab49d1812d29b2c4663a4e55bab
BLAKE2b-256 79b95635d954fca0a57e2a462a293b4a4a14ea30f6ab9bb361d5757989cb34b0

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page