Skip to main content

md2plain

CI License: MIT Python 3.10+ Platform: macOS

Markdown in, plain-text email out.

md2plain renders Markdown as a plain-text email and copies it to the macOS clipboard as text/plain. Paste it into Gmail's Plain text mode and the message is sent as a real text/plain message: hard-wrapped, with no HTML, no fonts, and no client-side reflow.

Demo

Input:

# Quarterly Update

This is a **short** note with a [link](https://example.com).

* one
* two

> quoted

Output:

Quarterly Update
================

This is a short note with a link (https://example.com).

* one
* two

> quoted

Why

  • Gmail's normal compose always sends HTML, even when you paste plain text.
  • Rich text copied from Typora carries its own CSS, which mail clients mangle.
  • Plain text has no automatic reflow, so the sender has to control line width.
  • A 75-column limit leaves room for > quote prefixes and stays within RFC 5322's recommended 78 characters.

Requirements

  • macOS (the clipboard is written through AppKit)
  • Python 3.10 or newer

Install

With pipx:

pipx install .

Or from a checkout, in a virtual environment:

python3 -m venv .venv
.venv/bin/pip install -e .

Usage

md2plain note.md

Then in Gmail: open the compose window, click the three-dot menu, choose Plain text mode, and press Cmd+V.

To verify, send a message to yourself and use Show original. The message should contain only:

Content-Type: text/plain; charset="UTF-8"

No multipart/alternative, no text/html.

Options

Option Description
--width N Line width in columns (default: 75)
--watch Update the clipboard whenever the file is saved
--print Write to stdout instead of the clipboard
-o, --output FILE Also write the plain text to a file
--soft-breaks Treat single newlines as spaces
--version Print the version and exit

Output format

Markdown Output
# Heading heading + ==== underline
## Heading heading + ---- underline
### Heading heading + ~~~~ underline
Paragraph hard-wrapped at 75 columns (CJK counts as two)
- item * item, with hanging indent
1. item 1. item
> quote > quote
Code block indented by four spaces
[text](url) text (url)
Table column-aligned plain text
Single newline kept (signatures and dates stay on their own lines)

How it works

  1. markdown-it-py parses CommonMark plus tables, strikethrough, and autolinks.
  2. src/md2plain/render.py wraps text by display width (CJK counts as two columns), renders headings, lists, quotes, and tables.
  3. src/md2plain/cli.py reads UTF-8 (BOM tolerant) and writes exactly one clipboard type: text/plain.

Limitations

  • macOS only; the clipboard backend is AppKit.
  • Plain text has no bold, italics, or clickable links. Link URLs are kept as text, and links inside headings or table cells lose the URL.
  • Images become alt text. YAML front matter, footnotes, math, and Mermaid diagrams are not converted.
  • Tables are aligned with spaces, so proportional fonts break the alignment.
  • Gmail may collapse > quote blocks behind a three-dot control; this is display only and the content stays intact.

Development

python3 -m venv .venv
.venv/bin/pip install -e ".[dev]"
.venv/bin/python -m unittest discover -s tests -v
.venv/bin/ruff check src tests
.venv/bin/black --check src tests

The ./md2plain script in the repository root is a development launcher; it runs the package from .venv. Installed users get the md2plain command from pyproject.toml.

License

MIT. See CHANGELOG.md for release history.

Metadata

Release files for md2plain 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for md2plain 0.1.0
File Size Uploaded Provenance
md2plain-0.1.0.tar.gz 12.6 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for md2plain 0.1.0
File Interpreter ABI Platform
md2plain-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 22.7 kB

Release files / md2plain-0.1.0.tar.gz

Download URL md2plain-0.1.0.tar.gz
Size 12.6 kB
Tags Source
SHA-256 checksum
How to use checksums
2f05f94696ee3331683b6485b221243e8d27b88625c9f0b4324c0301ba9ba472
BLAKE2b-256 checksum
How to use checksums
5d6e06e01aac4828056d6d685eca173b09a5ac861bd633965016157d2bc6fb82
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.5

Release files / md2plain-0.1.0-py3-none-any.whl

Download URL md2plain-0.1.0-py3-none-any.whl
Size 10.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
8966f6abdd98c3fc76de4b61fab3b28069c8a8987aa323e0e9ce15e7a53a7944
BLAKE2b-256 checksum
How to use checksums
f3f144f3967cec0e2555049e0c7306b25fb8867eee126f72ecc7fbf77d4eb8fd
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.5

Security for md2plain 0.1.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page