Skip to content

Repository files navigation

md-to-latex

Python License

Convert Markdown into beautiful, compilable LaTeX documents — no pandoc required.

md2latex reads CommonMark and GitHub-Flavored Markdown and produces a complete, standalone .tex document that compiles with pdflatex (or XeLaTeX/LuaLaTeX when custom fonts are configured). Headings become \section and friends, lists become enumerate/itemize, GFM tables render with booktabs, fenced code blocks use listings, and links, images, and footnotes map to their LaTeX equivalents. Raw HTML is parsed and rendered (tables, CSS borders, inline tags, SVG) rather than stripped. The output is a self-contained document — preamble through body — that compiles with packages from a standard TeX Live distribution.

Features

Markdown LaTeX
Headings (1–6) \section* … \subparagraph* (configurable numbering)
Unordered / ordered lists itemize / enumerate
Task lists Checked/unchecked boxes via \textsquare / \textifsymbol
GFM tables booktabs with per-side borders, colors, padding, and presets
Fenced code blocks lstlisting with syntax highlighting (20+ languages)
Inline code \texttt
Bold / italic / strikethrough \textbf / \textit / \sout
Links & images \href / \includegraphics
Footnotes \footnote
Blockquotes mdquote environment
Thematic breaks \hrulefill
Page breaks \newpage / \pagebreak
HTML blocks & inline tags Parsed via justhtml — div, p, strong, em, a, img, table, td styling, CSS colors, SVG
YAML front matter Stripped (never rendered)
Design tokens TOML config for fonts, spacing, colors, tables, code blocks, headers, footers
Custom fonts Google Fonts download via md2fonts (50+ families in catalog)
SVG images Converted to PDF (requires svg2pdf-py)

Quick start

pip install md-to-latex
md2latex README.md -o README.tex

To also compile to PDF:

md2latex README.md -o README.tex --pdf

That's it. pdflatex is auto-detected and runs twice (to resolve page counts).

Installation

Python 3.10 or later is required.

From PyPI:

pip install md-to-latex

SVG support (optional — converts inline SVG and .svg images to PDF for inclusion):

pip install md-to-latex[svg]

Development install (editable, with test and lint tooling):

python3 -m venv .venv && .venv/bin/pip install -e ".[dev]"

This installs md2latex and md2fonts console scripts, plus pytest and ruff.

Usage

Basic conversion

md2latex input.md -o output.tex

Without -o, the LaTeX document is written to stdout:

md2latex input.md

Read from stdin via /dev/stdin:

echo '# Hello' | md2latex /dev/stdin

PDF output

md2latex input.md -o output.tex --pdf      # writes output.tex + output.pdf
md2latex input.md --pdf                    # .tex → stdout, PDF path on stderr

Design tokens

Pass a TOML config file to customize the document's appearance — margins, fonts, colors, table styling, code blocks, and more:

md2latex input.md -c design.toml -o output.tex --pdf

Every key is optional; anything omitted falls back to the built-in default. A fully commented template is at config.toml.example. See Configuration below for details.

CLI reference

md2latex input.md [-c TOML] [-o OUTPUT] [--pdf] [--title TITLE]
Flag Description Default
input_file Path to input Markdown file (use /dev/stdin for pipes) required
-o, --output Write LaTeX to this file stdout
-c, --config Path to TOML design-tokens file built-in defaults
--pdf Compile .tex → PDF with pdflatex (runs twice) off
--title Document title (\title{…}) input file stem
--author Document author (\author{…}) (empty)
--date Document date (\date{…}) \today

Exit codes: 0 on success, 1 on I/O failure or pdflatex compile error (including when pdflatex is not found). Warnings (raw HTML, unknown language tags) go to stderr.

Note: \title, \author, and \date are set in the preamble for downstream packages that need them (e.g. hyperref). There is no \maketitle block — documents start directly with the body.

Configuration

Design tokens live in a TOML file passed with -c / --config. Copy config.toml.example to design.toml, edit to taste, and:

md2latex input.md -c design.toml -o output.tex

Every key is optional — omitting a section or key falls back to the built-in default. Setting only [spacing].parskip changes just that one value and leaves everything else byte-identical to a no-config run.

Section Configures
[document] document class (article, report, …) and base font size (10pt, 11pt, 12pt)
[page] page geometry: margin and paper size
[fonts] custom font families and paths (requires XeLaTeX/LuaLaTeX)
[spacing] paragraph indent, paragraph skip, line stretch
[colors] text color and hyperlink color
[tables] borders, colors, padding, presets (plain, striped, colored-header, minimal), column gaps
[code] code block font size, line numbers, frame, background color
[images] default image width
[footer] footer page numbers (on/off, position)
[headings] heading numbering (true → numbered \section, false → \section*)

A missing or unreadable config, or malformed TOML, prints an error to stderr and exits 1. Unknown keys are warned and ignored rather than treated as fatal.

Fonts with md2fonts

md2fonts is the sibling CLI bundled alongside md2latex. It downloads font families from the public Google Fonts catalog and prints the fontspec block needed in the [fonts] section of your config. No API key, no extra dependencies — everything goes over the GitHub API.

# Download Inter and print its fontspec block to stdout
md2fonts add "Inter"

# List all 50+ available families
md2fonts list

# Filter by name
md2fonts list --query mono

The block md2fonts add prints can be pasted directly into a [fonts] TOML section, or you can just set the family name:

[fonts]
main_font = "Inter"

End to end:

md2fonts add "Inter"
md2latex input.md -c design.toml -o output.tex --pdf

Custom fonts require XeLaTeX or LuaLaTeX. When [fonts] keys are set, the preamble loads fontspec and the engine auto-switches to lualatex.

Engine compatibility

Config Compiler Notes
No [fonts] keys pdflatex Default — all packages from TeX Live
Any [fonts] key set lualatex Auto-detected; fontspec loaded
SVG images ([svg] extra) pdflatex or lualatex Inline SVG → PDF via svg2pdf-py

Output structure

The generated document includes:

  • A complete preamble with all required \usepackage declarations
  • Right-aligned page numbers ("1 / total") in the footer via lastpage
  • Full-width horizontal rules for thematic breaks
  • Block-level \paragraph/\subparagraph headings (not inline)
  • Flush-left paragraphs with no first-line indent
  • Indented lists (bullet and numbered)
  • Task-list checkboxes using amssymb

Documents start directly with the body. The \title, \author, and \date commands are emitted in the preamble for downstream compatibility, but no \maketitle or title block is rendered.

Limitations

  • No math support: $ is a literal character, escaped as \$.
  • No smart typography: straight quotes stay straight, -- stays --.
  • YAML front matter is stripped entirely and never rendered.
  • Custom fonts require XeLaTeX/LuaLaTeX — pdflatex cannot process fontspec.

Development

# Clone and install
git clone https://github.com/mihok/md-to-latex.git
cd md-to-latex
python3 -m venv .venv && .venv/bin/pip install -e ".[dev]"

# Run tests
.venv/bin/pytest

# Lint
.venv/bin/ruff check .

pytest runs golden fixture tests (compare output against committed .tex fixtures) plus compile tests. The compile tests require pdflatex and are skipped automatically when it's not on PATH.

Contributing

Bug reports and pull requests are welcome. Before opening a PR:

  1. Run pytest and ensure all tests pass (or are skipped due to missing tools).
  2. Run ruff check . to maintain style consistency.
  3. Add or update golden fixtures if your change affects .tex output.

License

This project is licensed under the MIT License — see LICENSE for details.

About

Markdown to LaTeX (and PDF) command line tool

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages