Skip to main content

Browser-free Markdown to PNG renderer in pure Python

Project description

md2png-lite

English · 中文

Pure Python Markdown renderer that outputs PNG images.

Goals

  • No browser dependency
  • No WeasyPrint dependency
  • Cross-platform pip install
  • Configurable themes
  • Markdown + syntax highlight + basic math support

Rendering approach

  • It parses Markdown with markdown-it-py + dollarmath, maps tokens into a small document model, then lays out headings, paragraphs, tables, quotes, images, and code blocks directly on a Pillow canvas. No browser and no HTML screenshot step.
  • Font selection is based on glyph coverage instead of one fixed font. It auto-discovers system/custom fonts, scores coverage with matplotlib.ft2font, preselects body/heading/code fonts, then falls back per text run for CJK and missing glyphs.
  • Emoji are detected as Unicode sequences and rendered through cached Twemoji PNG assets, so skin-tone modifiers and common ZWJ sequences do not depend on local color-font availability.
  • There are now two separate font routes: the default package stays on local system fonts, while the optional NotoSans extra syncs a curated Noto pack into a user cache directory and prefers that pack at render time.
  • Math is rendered with matplotlib.mathtext into transparent bitmaps, with a light LaTeX sanitizing pass and a readable plain-text fallback for unsupported syntax.
  • Syntax highlight comes from Pygments token streams, while wrapping and painting are handled by the renderer itself, so code blocks do not depend on browser CSS/JS.

Install

pip install md2png-lite

# local development
uv sync

System-font route:

pip install md2png-lite

Curated Noto route:

pip install 'md2png-lite[NotoSans]'

The NotoSans extra does not embed fonts into the wheel. It installs the sync helpers and downloads the curated Noto pack into the local cache on first use.

CLI

uv run md2png-lite input.md -o output.png --theme paper

Choose the font route explicitly at call time:

uv run md2png-lite input.md -o output.png --font-pack system
uv run md2png-lite input.md -o output.png --font-pack noto

Load custom fonts explicitly:

uv run md2png-lite input.md -o output.png \
  --font-path ./fonts/NotoSansCJKsc-Regular.otf \
  --font-dir ./fonts

Stress Test

python3 scripts/benchmark_examples.py --repeat 3 --keep

This renders the bundled stress samples and prints timing / output size.

Visual Check Samples

Render the smaller inspection-focused samples:

python3 scripts/render_examples.py

Use a custom glob when needed:

python3 scripts/render_examples.py --pattern 'sample_*.md'

Python

from md2png_lite import render_markdown_image

payload = render_markdown_image("# Hello\n\n```python\nprint('hi')\n```")

Choose the route per call:

payload = render_markdown_image(markdown, font_pack="system")
payload = render_markdown_image(markdown, font_pack="noto")

Returned payload shape:

{
    "ok": True,
    "renderer": "md2png-lite",
    "mime_type": "image/png",
    "base64": "...",
}

Supported syntax

  • Headings
  • Paragraphs
  • Bullet / ordered lists
  • Block quotes
  • Horizontal rules
  • Fenced code blocks
  • Inline code
  • Tables
  • Links / emphasis / strong / strikethrough
  • Inline and block math via matplotlib.mathtext
  • Local / data: / remote images

Themes

  • paper: current warm reading style; legacy alias 阅读器 remains available
  • github-light: GitHub-like daytime theme
  • github-dark: GitHub-like dark theme
  • solarized-light: classic Solarized light theme
  • graphite: existing dark editorial theme

Font discovery

  • Auto-discovers system fonts on macOS / Windows / Linux
  • Supports two separate routes: system and synced noto
  • Chooses fonts per text run instead of forcing one global font
  • Prioritizes CJK-capable fonts for Chinese / Japanese / Korean text
  • font_pack="system" stays on platform fonts only
  • font_pack="noto" syncs the curated Noto pack into the local cache and prefers it
  • Supports custom fonts through CLI: --font-path, --font-dir
  • Supports route selection through CLI: --font-pack auto|system|noto
  • Supports provider config: md2png_lite.font_paths, md2png_lite.font_dirs, md2png_lite.font_pack
  • Supports environment variables: MD2PNG_LITE_FONT_PATHS, MD2PNG_LITE_FONT_DIRS, MD2PNG_LITE_FONT_PACK
  • Supports emoji cache control through environment variables: MD2PNG_LITE_EMOJI_CACHE_DIR, MD2PNG_LITE_EMOJI_SOURCE

Synced Noto font license note:

  • licenses/NOTO_CJK_LICENSE.txt

Boundaries

  • Math support follows matplotlib.mathtext, not full LaTeX
  • HTML blocks are rendered as plain text
  • Deeply nested lists / tables are supported, but layout remains image-oriented rather than browser-perfect

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

md2png_lite-0.2.2.tar.gz (37.3 kB view details)

Uploaded Source

Built Distribution

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

md2png_lite-0.2.2-py3-none-any.whl (41.0 kB view details)

Uploaded Python 3

File details

Details for the file md2png_lite-0.2.2.tar.gz.

File metadata

  • Download URL: md2png_lite-0.2.2.tar.gz
  • Upload date:
  • Size: 37.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for md2png_lite-0.2.2.tar.gz
Algorithm Hash digest
SHA256 c5152d8ab01a9b326491d5e840a14c38faaf042c122c23bc3ed7186180df0c85
MD5 003e88363dad1ca080ba31f70125eb5f
BLAKE2b-256 5b395e21d0cb04d6e66960ff00030b22dfad2899f6cb9d630f3e69f758f190dd

See more details on using hashes here.

Provenance

The following attestation bundles were made for md2png_lite-0.2.2.tar.gz:

Publisher: workflow.yml on kumoSleeping/md2png-lite

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file md2png_lite-0.2.2-py3-none-any.whl.

File metadata

  • Download URL: md2png_lite-0.2.2-py3-none-any.whl
  • Upload date:
  • Size: 41.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for md2png_lite-0.2.2-py3-none-any.whl
Algorithm Hash digest
SHA256 cceabc72a771803f7d09526ea55aba4c7c11225cc3fd96b5f24d6b03c74ee091
MD5 c86d4adeaf31de645a5899dbd591fd1a
BLAKE2b-256 076b77cd00cba0f97a4c3e4ce8d0d46fcd813904c6992e0c063ebfa4cb474fc1

See more details on using hashes here.

Provenance

The following attestation bundles were made for md2png_lite-0.2.2-py3-none-any.whl:

Publisher: workflow.yml on kumoSleeping/md2png-lite

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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