Skip to content

perrette/texmark

Repository files navigation

texmark

texmark

pypi python tests

Write scientific articles in markdown — and submit them to any journal.

  • Instant preview. Markdown renders live in most editors (VS Code, Cursor, JetBrains, vim) and on GitHub itself, so you see your draft as you type. No more wait-on-pdflatex cycles every time you change a sentence.
  • Git + GitHub as a paper backend. Branches, pull requests, issues, blame, and diffs work the way they were designed to. Collaborators review changes with the same tooling they already use for code.
  • One source, any journal. texmark compiles your markdown to any of the supported LaTeX templates. Start writing first, decide on a target journal later, and switch mid-way (or after rejection) by flipping a single yaml field — no rewriting needed.
  • No lock-in. The intermediate .tex is right there next to the PDF. Unplug texmark whenever you want and continue the manuscript in plain LaTeX — useful for the final polish that journals usually demand.

Installation

pip install texmark

External dependencies

texmark itself is pure Python, but it shells out to a few external tools to produce the final PDF.

  • pandoc — the markdown → tex engine. The easiest install is pip install pypandoc_binary, which ships the pandoc binary as a PyPI wheel (no system package manager, no sudo, same on Linux/macOS/Windows, recent pandoc 3.x). If you'd rather have a single shared install across venvs, use your system package manager instead (see below) — texmark picks up either.
  • A LaTeX distribution providing pdflatex, bibtex, and latexmk (texmark's default driver) plus the standard package set (hyperref, natbib, amsmath, graphicx, geometry, microtype, booktabs, caption, mathptmx, newtxtext, newtxmath, apacite, draftwatermark, mdframed, tikz, xcolor, appendix, lineno, epstopdf, …). Optionally tectonic as a single-binary alternative (see Build backends).

On Debian / Ubuntu:

sudo apt install pandoc \
    texlive-latex-extra texlive-bibtex-extra \
    texlive-publishers texlive-fonts-extra

…or just texlive-full for the easy answer.

On macOS (Homebrew):

brew install pandoc
brew install --cask mactex     # or basictex + tlmgr install <packages>

A handful of LaTeX packages that aren't in TeX Live's smaller installs (notably trackchanges, algorithm / algorithmicx, jabbrv) are bundled with texmark under texmark/templates/<journal>/ and copied into the build directory automatically, so you don't need to install them separately.

Example

See example.md for a sample markdown file with yaml metadata in the header.

The command to convert the markdow to tex is:

texmark example.md

And to convert to PDF

texmark example.md --pdf

For another journal, it is enough to change the journal -> template' field in the yaml metadata. For testing it is also possible to pass -jfor--journal-template`:

texmark example.md --pdf -j science -o build/example-science.pdf --tex build/example-science.tex

See the example tex and pdf results in build

Build backends and engines

texmark separates the driver (what orchestrates LaTeX passes and bibtex) from the engine (the actual TeX program). Both have sensible defaults and both are configurable from the CLI or YAML.

--backend What it does Honors --engine?
latexmk (default) Reads .aux/.fls/.bbl between passes and reruns only what's stale. Drives bibtex automatically. Typically 2–3× faster on incremental edits than the old fixed 3-pass sequence. yes
raw Runs engine → bibtex → engine → engine unconditionally — the original behaviour. Use when latexmk isn't available. yes
tectonic Standalone Rust rewrite of the TeX stack: engine + driver + bibtex-equivalent in one binary. Auto-fetches missing packages on first run. no — uses its own XeTeX-derived engine

--engine selects the TeX program when the backend honors it:

  • pdflatex (default) — broadest package compatibility, fastest cold start.
  • xelatex — native Unicode + system OpenType fonts (fontspec).
  • lualatex — LuaTeX scripting + modern fontspec.

YAML frontmatter equivalents:

backend: latexmk          # or raw, tectonic
engine: pdflatex          # or xelatex, lualatex

CLI flags win over YAML.

Unicode and HTML in bibliographies / body text — texmark transparently rewrites non-ASCII Unicode and CrossRef-style HTML markup (<i>δ</i><sup>18</sup>O) into LaTeX commands at build time, so pdflatex doesn't silently drop scientific characters from your references. Default behaviour is engine-aware (--rewrite-unicode auto): on for pdflatex, off for lualatex/xelatex; pass on/off to override. See docs/encoding.md for the full strategy, performance numbers, and how the engine choice affects it.

Live preview (--watch)

texmark sources/main.md --pdf --watch

Rebuilds whenever the input markdown, bibliography, or template changes. Combine with an auto-reloading PDF viewer (zathura, evince, okular) for a live-preview workflow — the output PDF is rewritten in place so viewers that follow inode changes keep your scroll position.

In multi-file projects --watch also tracks every embedded chapter and every companion document (plus each companion's bibliography and template).

Multi-file projects

Split a long manuscript across files by embedding chapters directly in the markdown body:

![](chapters/introduction.md)
![](chapters/methods.md)
![](chapters/results.md)

Or use the GitHub-clean link form (renders as a plain link on GitHub):

[Introduction](chapters/introduction.md){.include}

Alternatively, declare the chapter set in YAML:

chapters:
  - chapters/introduction.md
  - chapters/methods.md
  - chapters/results.md

texmark compiles each chapter to a body-only .tex and wires them together with \input{...} (article-class templates) or \include{...} (book-family templates). From LaTeX's perspective it is one document, so all labels and cross-references work natively.

For book-family templates, --only chapter.md injects \includeonly{chapter} for fast single-chapter iteration.

See docs/multi-file.md for the full reference.

Main paper + supplementary information

Declare a companion document (separate PDF) in the root YAML:

companions:
  - si.md

Reference a label in the companion using a #anchor markdown link:

As shown in [Fig.~S1](si.md#fig:noise), the signal is clear.

texmark rewrites this to \ref{si:fig:noise} using xr-hyper's \externaldocument machinery. Cross-references are validated at compile time; a renamed label in the SI breaks the main paper's build, not just the rendered output.

Give the companion independent figure numbering via its own YAML:

# si.md
prefix: S

texmark loops the compile cycle (up to 4 passes) until all .aux files stabilize, so xr-hyper cross-document references resolve correctly.

See docs/multi-file.md for the full reference, including prefix:/per-counter overrides and watch-mode coverage.

Custom preamble

Inject \newcommand, theorem environments, or package configuration into the preamble without forking the template:

preamble: |
  \newcommand{\degC}{^\circ\mathrm{C}}
  \DeclareMathOperator{\std}{std}

Also accepts a file path (preamble: macros.tex) or a list of paths. Works across all templates. See docs/preamble.md.

Journal templates

Pick the template that matches your target journal and add it to the yaml header of your markdown:

journal:
    template: ametsoc          # template name, see table below
    options: twocol            # optional, per-template — see the docs page

Article-class templates (single PDF, any journal):

template covers example docs
copernicus (aliases cp, esd, ...) Copernicus / EGU: ACP, BG, CP, ESD, HESS, NHESS, TC, ... PDF docs
science AAAS Science, Science Advances PDF docs
ametsoc (aliases jclim, jas, mwr, bams, ...) AMS: J. Climate, JAS, MWR, BAMS, ... PDF docs
arxiv (alias preprint) arXiv preprint, generic article-class PDF docs
elsarticle (alias elsevier) Elsevier: QSR, EPSL, GPC, Earth-Science Reviews, Cell, Lancet, ... PDF docs
agujournal (aliases agu, jgr, grl, james, wrr, ...) AGU: JGR family, GRL, Earth's Future, JAMES, ... PDF docs
springernature (aliases nature, naturecomms, natclimchange, natgeoscience, scirep) Springer Nature: Nature, Nature Communications, Nature Climate Change, Nature Geoscience, Scientific Reports, ... PDF docs
pnas PNAS PDF docs

Book-family templates (theses, monographs, multi-chapter documents):

template document class structure docs
book \documentclass{book} \frontmatter / \mainmatter / \backmatter docs
report \documentclass{report} No front/main/back matter macros docs
memoir \documentclass{memoir} \frontmatter / \mainmatter / \backmatter; configurable chapter styles docs
classicthesis \documentclass{scrreprt} + bundled classicthesis.sty Bringhurst-inspired typography; no front/main/back matter macros docs

Presentation templates (Beamer slide decks):

template aliases covers docs
beamer slides, presentation Lecture slides, conference presentations, seminar talks docs

Book-family templates support \include{...} for chapters (enabling --only for selective recompilation), front-matter YAML keys (dedication:, list_of_figures:, etc.), and bibliography_per_chapter: true for per-chapter biblatex bibliographies.

Each template ships a default journal.options value chosen to produce a publication-style PDF (typeset 2-column journal look, no draft watermarks). For peer-review submission you usually want to switch to the publisher's submission options (e.g. preprint,12pt for Elsevier, draft for AGU, the empty default for AMS 1.5-spacing). The per-journal docs page lists the alternatives.

Partial support only. Before submitting, you will likely need to hand-edit the final LaTeX — appendix structure, special sections (Methods Online, Extended Data, Significance, …) and journal-specific cross-reference macros are not all wired up automatically. Use the example PDF to confirm the layout looks reasonable, then run texmark --tex out.tex and finish the manuscript in LaTeX directly.

Common yaml fields (all templates)

title: "Paper title"
authors:
  - firstname: Mahé
    lastname: Perrette
    affiliation: 1              # integer index into `affiliations` (most templates)
    email: mahe.perrette@gmail.com
  - firstname: Co
    lastname: Author
    affiliation: 2
affiliations:
  - "Alfred Wegener Institute, ..."
  - "Another Institution"
date: "2026-05-26"
bibliography: references.bib
journal:
    template: <name>            # required: picks the journal template + filters
    options: <str or list>      # optional: class options (per-template default)
collect_figures_and_tables: true       # optional: see section below
figure-width: 80%                      # optional: pandoc figure default
figure-span: full                      # optional: wraps in figure* (full text width)
                                       #   per-figure override: ![cap](img){figure-span=full}
copy_figures: false                    # optional: see "Figure paths" below
figure_folders: [images, ../shared]    # optional: see "Figure paths" below
project_root: .                        # optional: see "Figure paths" below
# Multi-file keys (see docs/multi-file.md)
chapters: [chapters/intro.md, ...]    # optional: alternative to body embed syntax
companions: [si.md]                   # optional: companion documents (separate PDFs)
prefix: S                             # optional: counter prefix for companion docs
# Book-family template keys
dedication: "To my advisor"           # optional
list_of_figures: true                 # optional
list_of_tables: true                  # optional
bibliography_per_chapter: true        # optional: biblatex per-chapter refs
chapter-style: veelo                  # optional: memoir only
classicthesis-options: "parts"        # optional: classicthesis only
# Custom LaTeX preamble (see docs/preamble.md)
preamble: macros.tex                  # optional: file path or inline block scalar

Section-style metadata can also be given as markdown # ... headings. Any of # Abstract, # Acknowledgments, # Data Availability, # Appendix, # Supplementary Material, # Significance, # Capsule, # Key Points, # Plain Language Summary, # Author Contributions, # Competing Interests, # Materials and Methods, # Funding, # Highlights, # Keywords will be extracted out of the body and injected into the right LaTeX command for the target journal. Book-family templates additionally recognise # Preface and # Foreword. The exact list each template recognises is in texmark/filters/main.py.

Figure paths

texmark interprets ![](path) URLs by the same rules as GitHub's markdown renderer:

  • No leading slash — relative to the markdown file's directory (the standard markdown spec).
  • Leading slash — relative to the project root. By default the project root is detected via git rev-parse --show-toplevel, run from the markdown's directory so submodules and worktrees resolve to their own root rather than the outer repo's. For non-git projects, the invocation directory (CWD) is used. You can override either by passing --project-root <path> on the CLI (or project_root: <path> in the yaml front-matter).

Once resolved, each URL is rewritten in the generated .tex to be relative to the build directory. The figure files stay where they are on disk; nothing is copied.

If you would rather have short paths in the .tex (e.g. eof.png instead of ../images/eof.png), pass --figure-folders <dir> [<dir> ...] on the CLI (yaml: figure_folders: [<dir>, ...]). Each folder is interpreted relative to the current working directory and feeds LaTeX's \graphicspath. Figures that live under any of these folders get short URLs in the .tex; figures elsewhere keep the relative-to-build-dir form. Folder search order is respected (first match wins, matching pdflatex's own resolution).

For a self-contained build (e.g. to hand the .tex + figures to a journal portal), pass --copy-figures on the CLI (yaml: copy_figures: true). In that mode every referenced figure is copied flat into <build>/figures/:

  • Files keep their basename when unique.
  • When two figures share a basename but have different contents, both are renamed to <stem>-<short-content-hash><ext> for disambiguation.
  • Same file referenced from multiple paths collapses to a single bundled copy.
  • A .texmark-figures manifest in <build>/figures/ records which files texmark wrote, so the next build can delete only files it owns; files you put there by hand are preserved.

--figure-folders is ignored when --copy-figures is set (every figure ends up in <build>/figures/ either way).

Remote (http(s)://) figure URLs are always downloaded into <build>/figures/<hash>/<basename> by the texmark-download-images filter, regardless of these settings.

Collect figures and tables at the end of the document

Just add

collect_figures_and_tables: true

to your markdown yaml metadata.

Advanced: latex template

The templates are written in jinja2.

Just copy from e.g. texmark/templates/science/template.tex to your own, e.g. custom_template.tex And run again with:

texmark example.md --pdf -j science -o build/example-science.pdf --tex build/example-science.tex --template custom_template.tex

The -j journal template option (here science) is still used to set custom filters (e.g. only \cite for Science, no \citet ; extract specific sections as metadata to be injected as {{section}} instead of {{body}} etc). The machinery is defined in texmark/filters.py and can in principle be extended or copied. Two approaches are possible:

  • just add more filters via the --filters command or in the yaml metadata.
  • extend the existing filters in a module, e.g. custom_filter.py, that extends the filters dict from the texmark.filters module (see the source code to check the details). And then pass it via --filters-module custom_filter parameter (or custom_filter in the metadata) to prompt the texmark filter to load that module and make it available via -j your-custom-name. Note that will require you to explicitly pass --template as well. Unless you overwrite an existing filter.

Ackowledgements

This project benefited greatly from AI support to extend the initial list of supported journals and further extend this package capability.

About

Write scientific articles in markdown

Resources

License

Stars

Watchers

Forks

Sponsor this project

 

Packages

 
 
 

Contributors