Help us shape the future of Go! Take the 2026 Go Developer Survey

markout

command module
v1.10.0-rc6 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 16, 2026 License: GPL-3.0 Imports: 15 Imported by: 0

README

markout

CI Release Latest release Go

A fast Markdown → DOCX / PDF converter with an interactive terminal UI (TUI).

No browser, no webview, no GUI toolkit — it runs entirely in your terminal, as a single binary with the fonts built in.

It reads Markdown the way the platform you wrote it for does: pick GitHub, GitLab, YouTrack, Azure DevOps, Obsidian, Pandoc or one of the other flavors, and that dialect's alerts, task lists, footnotes, tables of contents and the rest are understood. Formulas are typeset and Mermaid flowcharts are drawn (see Limits for what is not).

Install

From a release binary (no Go required)

Grab the binary for your platform from the latest release, or with curl (example for macOS Apple Silicon):

# Look up the latest release tag (or set VERSION=v1.9.0 to pin one).
VERSION=$(curl -fsSL https://api.github.com/repos/hkmt-sw/markout/releases/latest \
  | sed -n 's/.*"tag_name": *"\([^"]*\)".*/\1/p')
curl -fsSL -o markout \
  "https://github.com/hkmt-sw/markout/releases/download/$VERSION/markout_${VERSION}_darwin_arm64"
chmod +x markout
mv markout /usr/local/bin/        # or any directory on your $PATH, e.g. ~/.local/bin

Pick the asset that matches your system:

OS / CPU Asset
macOS Apple Silicon markout_<VERSION>_darwin_arm64
macOS Intel markout_<VERSION>_darwin_amd64
Linux x86-64 markout_<VERSION>_linux_amd64
Linux arm64 markout_<VERSION>_linux_arm64
Windows x86-64 markout_<VERSION>_windows_amd64.exe
Windows arm64 markout_<VERSION>_windows_arm64.exe

On macOS the binary is unsigned, so Gatekeeper may block the first run. Clear the quarantine flag once:

xattr -d com.apple.quarantine /usr/local/bin/markout

Optionally verify the download against the release's SHA256SUMS.txt, or check with the GitHub CLI that it was built by this repository's release workflow (releases from v1.1.2 on):

shasum -a 256 -c SHA256SUMS.txt    # (Linux: sha256sum -c)
gh attestation verify markout --repo hkmt-sw/markout
With Go
go install github.com/hkmt-sw/markout@latest
From source
git clone https://github.com/hkmt-sw/markout.git
cd markout
go build -o markout .   # produces ./markout
make install            # or install to $GOBIN (~/go/bin): go install .

If the install dir isn't on your PATH, add it to your shell profile:

export PATH="$(go env GOPATH)/bin:$PATH"

Other Makefile targets: make run (launch without installing), make test, make release (cross-compile all platforms into dist/), make uninstall, make clean.

Usage

Interactive TUI

Run with no arguments to launch the full-screen UI:

./markout

It's a single Midnight-Commander-style window: a file list on top, a docked Convert box below (flavor / theme / language / format / output / status), and a function-key bar at the bottom. Highlight a Markdown file and press Enter to convert it.

Key Action
↑ ↓ Move through the file list
→ / Enter Enter a directory
← Go up a directory
Enter Convert the highlighted .md file
F2 Settings: flavor, theme, language, update check, PDF/A
F3 Toggle output format (DOCX ⇄ PDF)
F4 Edit the output path by hand
o Open the last converted file
F10 / q Quit

The output path is derived automatically from the highlighted file and the selected format (PDF by default); F4 lets you override it. If the output file already exists, a popup asks you to confirm before overwriting it.

Direct (non-interactive)

The output format is inferred from the file extension:

./markout input.md output.docx
./markout input.md output.pdf
./markout --flavor gitlab input.md output.pdf   # interpret as a specific flavor
./markout --list-flavors                        # show the supported flavors
./markout --lang hu input.md output.docx        # the language, if the document names none
./markout --pdfa input.md output.pdf            # a PDF/A-2u file, for archiving
./markout --remote-images allow input.md out.pdf # download images referenced by URL

Themes

A theme decides how the output looks: page size and margins, fonts, sizes, spacing, colors, and headers and footers. Five come built in:

--theme Look
default The standard markout look
classic Serif type, wide margins, restrained color: like a book or a paper
modern Sans-serif and airy, with one vivid accent color
compact Small type and narrow margins: long technical documents on fewer pages
report A business report: title and date in the header, page numbers in the footer
classic modern report
classic modern report

Pick one on the Theme tab of the settings (F2, then Tab), or per conversion:

./markout --theme report input.md output.pdf
./markout --list-themes

The Theme tab of the settings dialog, listing the built-in themes with report selected

Your own theme is a small text file that lists what it changes:

extends = "default"

[fonts]
body = "serif"

[heading]
color = "#7A1E1E"

[footer]
center = "Page {page} of {pages}"
./markout --export-theme > mytheme.toml     # every setting, to start from
./markout --theme mytheme.toml input.md output.pdf

The theme guide walks through making one, with every setting, your own fonts, and headers and footers. To check a theme, convert fixtures/showcase.md: it contains every element with a note on what it should look like.

Language

A document says what language it is written in with lang in its front matter:

---
title: Mentési eljárásrend
lang: hu
---

The language is a tag such as hu, en-US or en-GB; en alone is taken as American English. It is written into the output: in a DOCX it is the language Word checks the spelling in and hyphenates by, in a PDF it is what a screen reader reads the text as. It is not shown among the fields of the front matter.

For documents that do not name their language, give one with --lang, or choose one on the Language tab of the settings (F2), which is saved. What a document says goes before both. With no language anywhere the output names none, as before.

The language is also what words are hyphenated by, in a theme that sets text justified or hyphenated. In PDF markout hyphenates Hungarian, American English and British English.

Document control

New to this? Writing a policy document walks through the front matter, the cover page, the classification band, the version history and the headers in order, with what to do when something does not appear.

A policy or procedure says in its front matter which version it is, who wrote and approved it, and how it is classified:

---
title: Mentési eljárásrend
version: 1.2
status: Jóváhagyva
author: Informatikai üzemeltetés
owner: Informatikai vezető
approved-by: Ügyvezető
date: 2026-09-20
review-date: 2027-09-20
classification: Belső
lang: hu
---

markout sets this as a table at the top of the document, in PDF and DOCX. These keys come first, in this order, labelled in the language of the document (here Hungarian; English for any other language): title, version, status, author, owner, approved-by, date, review-date, classification. Any other key with a text value follows under its own name. A theme can rename the labels under [control.labels].

Cover page

cover: true in the front matter gives a document a cover page: the title in large type, the subtitle under it, and under them the rest of the control block. The text starts on the next page. The cover page has no header or footer, though it counts as page 1; in DOCX the title and subtitle are in Word's Title and Subtitle styles. true, false, yes and no count, in any case; any other value is ignored, with a warning.

---
title: Mentési eljárásrend
subtitle: Informatikai üzemeltetés
version: 1.2
classification: Belső
cover: true
---

A theme can give every document with front matter a cover with [cover] (enabled = true), which also sets its sizes and colors; cover: false takes it off one document. See Cover page for the settings.

examples/policy/jogosultsagkezelesi-szabalyzat.md is a short policy with a cover page, every control key, a classification and a watermark: copy its front matter to start your own.

Classification and watermark

classification in the front matter puts a band across the top of every page, in PDF and DOCX, with the classification centered in it in bold type. The cover page has it too. The band says the classification as you wrote it, and the level it belongs to decides the colors. Four levels are built in, each known by an English and a Hungarian name, in any case:

Level Names Band
public public, nyilvános white on green
internal internal, belső, belső használatra white on blue
confidential confidential, bizalmas white on amber
secret secret, titkos, szigorúan titkos white on red

A classification no level knows still gets a band, in neutral colors (white on gray), and markout warns. watermark draws its text in light gray across every page, from the bottom left to the top right, behind the text; only text counts, anything else is ignored with a warning. In DOCX the watermark is Word's own text watermark.

---
title: Mentési eljárásrend
classification: Szigorúan titkos
watermark: TERVEZET
---

A theme can add names to a level, change colors, add levels of its own and size the band and the watermark with [classification], [classification.NAME] and [watermark]; band = false under [classification] draws no band for the theme's documents. See Classification and watermark.

A header or footer can show the version and the classification of the document with {version} and {classification}, next to {title}, {author}, {date}, {page} and {pages}.

Version history

history: git in the front matter adds a version history table to the document, made from the git history of the Markdown file itself. Only the commits that touched the file and carry a Changelog: trailer are listed; the row says what the trailer says. Write the trailer in the commit message, as its last paragraph:

git commit -m "Tighten the retention rule" -m "Changelog: A mentések megőrzési ideje 90 nap."
---
title: Mentési eljárásrend
lang: hu
history: git
---

The table has the columns Date (the day of the commit, where it was made), Author and Change, newest commit first, and follows the file through renames. A Version: trailer on a commit (git commit --trailer "Changelog: ..." --trailer "Version: 1.2") adds a Version column on the left, empty for the commits that have none. Several Changelog: trailers on one commit are joined with ; . Commits without a Changelog: trailer are left out. The title and the labels are in the language of the document: Hungarian for hu, English otherwise.

The table goes right after the control block, or at the top of the page after the cover page if the document has one. Its title is not a heading, so it is not in the outline or the table of contents.

markout only runs git on the repository the file is in. Nothing leaves your machine, and no hooks of the repository run. Without git, with a file outside a git work tree, one that is not committed yet, or one with no commit that has a Changelog: trailer, markout warns and converts the document without the table. A history value other than git is ignored, with a warning.

PDF/A

PDF/A is the form of PDF for documents that have to stay readable and look the same for years, in an archive or a records system. markout makes PDF/A-2u: the fonts are in the file, the colors are tied to a color profile (sRGB), and the file describes itself (title, author, language) in a standard way. Text still copies and searches as Unicode. Ask for it in one of three ways:

  • markout --pdfa input.md output.pdf on the command line;
  • pdfa: true in the front matter, which pdfa: false turns off again (yes and no count too, in any case; any other value is ignored, with a warning);
  • PDF/A in the TUI: F2 for Settings, then a. It is saved with the other settings and also applies to markout on the command line.
---
title: Mentési eljárásrend
lang: hu
pdfa: true
---

Without one of them a PDF is made as before. The setting changes the PDF only: a DOCX is made as usual, and --pdfa with a .docx output says so.

The file's title, author and language are those of the document. Its creation and modification date is the date of the front matter, when that is a day or a time markout can read, and none otherwise. The same document gives the same file every time, byte for byte. The cover page, the classification band, the watermark and the headers and footers are PDF/A like the rest. The color profile is sRGB-v2-magic.icc, a public domain file.

What is not covered: PDF/A-2a, which asks for a tagged PDF, a structure of headings, paragraphs and tables for screen readers, and PDF/UA, the standard for accessible PDF. markout does not tag its PDFs. CI converts the sample documents with --pdfa and validates them with veraPDF, and a PDF/A file is checked against the same rules by scripts/check-pdfa.sh if you have veraPDF.

Update notifications

When a newer release exists, the TUI says so in its top border:

┌─ markout ──────────── v1.3.0 is available · github.com/hkmt-sw/markout/releases ──┐

It only tells you; installing the new version is up to you (download the binary again, or go install github.com/hkmt-sw/markout@latest).

To know, the TUI asks GitHub for the latest release when it starts, at most once a day. That request carries nothing but the markout version, though GitHub sees your IP address like any web server does. It is the one request markout makes without asking first, so you can turn it off: press F2, then u. Direct conversions from the command line never check. To look on demand:

./markout --check-update

Images from the internet

A document can reference images by URL. Downloading them tells the servers it names your IP address and that you opened the document, and a document from someone else can point at addresses on your own machine or local network. So markout never downloads an image without asking.

The dialog listing the servers a document would download images from, with the buttons Load images, Skip images and Cancel

  • In the TUI, converting such a document first shows every server it would contact and how many images are on each; addresses on your machine or local network are marked. Choose Load images, Skip images (the default: the conversion runs and those images become placeholders) or Cancel.
  • On the command line, the same list is printed and you are asked [y/N]. When there is nobody to ask (a script, a pipe, CI) the images are skipped and listed on standard error. --remote-images allow downloads without asking, --remote-images deny always skips.

The answer is not remembered: the question comes up for each conversion that needs it. Images stored on disk next to the document are not affected.

Markdown flavors

"Markdown" is a family of dialects: the same file renders differently on GitHub, GitLab, YouTrack or in Obsidian. markout has an interpreter for each of the flavors below, so a document converts the way it looks on the platform it was written for.

The settings dialog listing the Markdown flavors, with GitLab selected

Pick the flavor with F2 in the TUI. The choice is saved (in markout/config.json under your OS config directory, or the file named by $MARKOUT_CONFIG) and is also the default for direct conversions; --flavor overrides it for one run.

--flavor Flavor What it adds to CommonMark
markout Markout (universal, default) Every extension below that doesn't conflict with another; a good fit when you don't know where a file came from
commonmark CommonMark Nothing: the strict standard
github GitHub (GFM) Tables, task lists, strikethrough, autolinks, footnotes, > [!NOTE] alerts, $math$, Mermaid, :emoji:, color chips
gitlab GitLab (GLFM) GFM plus [[_TOC_]], alert titles, {+ inline +} {- diff -}, >>> quotes, [~] tasks, JSON/TOML front matter, json:table, ::include{file=…}, {width=…} images, definition lists
youtrack YouTrack Tables, checklists, strikethrough, autolinks, ```latex formulas, Mermaid, {width=…} images
azure Azure DevOps GFM plus [[_TOC_]], ::: mermaid, $math$, ![](img =500x250) sizes, :emoji:
bitbucket Bitbucket Tables, strikethrough, footnotes, definition lists, [TOC], [[wiki links]]; raw HTML is shown as text
obsidian Obsidian [[wiki links]], ![[image.png|300]] embeds, ==highlight==, callouts of any type (foldable, titled), %%comments%%, ^[inline notes], $math$
pandoc Pandoc (also Quarto, R Markdown) % title block, ::: fenced divs, ^super^ and ~sub~script, inline notes, [span]{.underline}, heading attributes, smart punctuation
multimarkdown MultiMarkdown Metadata header, {{TOC}}, CriticMarkup, \\(math\\), ^super^ and ~sub~script
extra Markdown Extra Tables, footnotes, definition lists, abbreviations, heading IDs
kramdown kramdown (Jekyll) Markdown Extra plus {:toc}, {: .class} attribute lists, $$math$$, smart punctuation
myst MyST (Sphinx, Jupyter Book) ```{directive} and :::{directive} blocks, {role}`text` , $math$
mkdocs MkDocs Material !!! note admonitions, === "Tab" tabs, [TOC], ==mark==, ^^insert^^, ^super^, ~sub~, CriticMarkup
docusaurus Docusaurus (MDX) :::note admonitions, {#heading-ids}; MDX imports, exports and comments are dropped

Text written by AI assistants is GitHub-style Markdown, often with math in LaTeX delimiters; the default flavor reads both $…$ and \(…\) / \[…\].

Syntax that a flavor doesn't define is left as plain text, exactly as that platform would show it (~~text~~ stays literal under CommonMark, for example). Front matter is the exception: it is markout's own, so every flavor reads the YAML or TOML block that starts the file.

Things that only make sense on the original platform have no document equivalent and stay as text: @mentions, issue references such as #123 or ABC-123, and links to other wiki pages.

Code blocks

A code block that names its language is syntax-highlighted, in PDF and in DOCX:

```python
def greet(name):
    return f"Hello, {name}"
```

Almost 300 languages are known, by the names GitHub uses for them. A block with no language, or one that is not known, is shown in one color. The colors are the theme's, under [code.syntax], where highlighting can also be turned off.

Math

Formulas written in LaTeX are typeset, in both formats:

The roots are $x = \frac{-b \pm \sqrt{b^2 - 4ac}}{2a}$.

$$
\sum_{i=1}^{n} x_i^2 \le \int_0^\infty e^{-x}\,dx
$$
  • In PDF markout sets them itself: fractions, roots, sub- and superscripts, sums and integrals with their limits, brackets that grow, matrices, cases and aligned, accents, Greek and the usual symbols.
  • In DOCX they are Word equations, which Word typesets with its own math font and which can be edited there.

Which delimiters mark a formula ($…$, \(…\), ```math) depends on the flavor. What cannot be typeset is listed under Limits.

Examples

The examples directory has documents of the kinds people convert (a security policy, an incident procedure, an architecture description, a runbook, a risk assessment, meeting minutes), each using what such a document needs: tables, flowcharts, formulas, code, callouts, footnotes. Convert one to see what markout makes of it:

markout examples/technical/architecture.md architecture.pdf

Diagrams

A Mermaid flowchart is drawn, in both formats:

```mermaid
graph TD
    A[Read the file] --> B{Is it Markdown?}
    B -->|yes| C[Convert it]
    B -->|no| D([Stop])
```

Boxes of every shape, arrows and lines of every kind with their text, subgraphs, and the four directions (TD, BT, LR, RL) are understood. In PDF the chart is drawn as lines and text, sharp at any zoom; in DOCX it is a picture. A chart wider than the text is made smaller to fit.

Finding your way in the output

Both formats know where the headings are:

  • A PDF has bookmarks: its headings, nested by level, in the pane a viewer shows beside the pages.
  • Links to a heading ([text](#heading)) jump to it, in PDF and in DOCX. The heading can be named the way GitHub or GitLab names it, with accented letters kept. A link to a heading the document does not have is plain colored text.
  • The entries of a table of contents are such links too. In PDF each has the page its heading is on, at the end of a row of dots.

What a DOCX is made of

A DOCX from markout is a Word document to go on working in, not a picture of one:

  • Headings are in Word's Heading 1 to Heading 6 styles, so they show in the navigation pane, and changing a style changes every heading. Body text is in Normal. The styles look the way the theme says.
  • Lists are Word lists: add an item to a numbered list and the rest is renumbered.
  • A table of contents is a field Word can update, and its entries are links to the headings.

Limits

markout lays the document out itself, with no browser or TeX behind it. Some things are beyond it for now:

  • Math is typeset by markout itself in PDF, with far fewer rules than TeX has: the result is correct and readable, not TeX's typography. A formula on a line of its own that is wider than the text is made smaller to fit, and one in a paragraph can stand taller than its line. A formula that uses a command markout does not know, or that is not well-formed, is shown as its source in color, and the conversion says so.
  • Mermaid: only flowcharts (graph and flowchart) are drawn, laid out by markout itself, more plainly than Mermaid does: the same boxes and connections, not the same positions. Colors and styles set in the chart are not used; the theme's are. In DOCX a chart is a picture, which Word cannot edit. Sequence, class, state, Gantt and the other kinds of diagram, and a flowchart markout cannot read, are shown as their source in a labeled box, and the conversion says so.
  • Characters the PDF fonts lack. The built-in fonts cover Latin, Greek and Cyrillic. Other characters (Chinese, Japanese, Korean, Arabic, emoji) are left out of a PDF, and the conversion says which ones: as a warning on standard error for a direct conversion, in the status line in the TUI. A theme can use a font of your own that has them. DOCX keeps every character and leaves the choice of font to the word processor.
  • Right-to-left text (Hebrew, Arabic) is not laid out in PDF: its letters come out in reverse order. The conversion warns about this too.
  • Documentation-site Markdown (MyST, MkDocs Material, Docusaurus) is read for what a document can show: admonitions, tabs with their labels, code block titles, MyST tables, version notes, and the pages a toctree names, as a list. Some of what those sites build has no place in a single document:
    • MkDocs Material: a snippet (--8<-- "file.md") is put in if the file is in the document's directory or below it, and named in a warning if it cannot be read; keys (++ctrl+c++) are shown as Ctrl+C; icons (:material-…:) are pictures from a font and are left out, with a warning.
    • A MyST eval-rst block is shown as its source, since reStructuredText is not read.
    • raw blocks for a format other than HTML, bibliography and index, the video and query-table blocks of Azure DevOps, and JSX components that stand by themselves in MDX (<Chart />), are left out, and the conversion names them.
    • A directive markout does not know is shown as plain text, and the conversion names it.
    • MDX import and export lines and comments are dropped: they show nothing on the site either.
  • A table of contents in DOCX lists the headings without page numbers until Word is asked to update it (right-click it, Update Field); where the pages break is Word's decision, so markout cannot know the numbers.

Project layout

Path Purpose
main.go Entry point (TUI + direct CLI mode)
internal/tui Bubble Tea terminal UI
internal/flavor The supported Markdown flavors and their features
internal/parse Markdown → AST (goldmark), per-flavor syntax
internal/render AST → DOCX / PDF renderers
internal/convert Conversion orchestration
internal/theme Themes: the built-in ones and theme files
internal/lang Language tags
internal/hyphen Hyphenation, with the patterns of each language
internal/settings Saved preferences (flavor, theme, language, PDF/A)
fixtures Sample documents: showcase.md shows every element, flavors/ has one per flavor, corpus/ has real documents from public projects
examples Example documents: a policy, a runbook, a report, minutes and more, to convert and to copy from
docs The theme guide and pictures
cmd/debug Dumps the parsed AST as JSON for debugging

Contributing

Bug reports and pull requests are welcome; see CONTRIBUTING.md. Changes between versions are listed in CHANGELOG.md, and security problems are reported as described in SECURITY.md.

License

Copyright © 2026 Márton Tamás

markout is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License, version 3. It is distributed in the hope that it will be useful, but without any warranty.

The documents under examples are an exception: they are dedicated to the public domain (CC0 1.0), so that they can be copied as the start of your own.

It builds on these projects:

Documentation

Overview

Command markout is a terminal UI for converting Markdown files to DOCX or PDF.

Run it with no arguments to launch the interactive TUI:

markout

Or convert directly from the command line:

markout input.md output.docx
markout input.md output.pdf
markout --flavor gitlab input.md output.pdf

Directories

Path Synopsis
cmd
debug command
internal
ast
atomicfile
Package atomicfile writes files so that a failure never leaves a half written file behind: the old content stays until the new content is complete.
Package atomicfile writes files so that a failure never leaves a half written file behind: the old content stays until the new content is complete.
flavor
Package flavor describes the Markdown dialects markout can interpret.
Package flavor describes the Markdown dialects markout can interpret.
history
Package history reads the version history of a document from the git repository it is in.
Package history reads the version history of a document from the git repository it is in.
hyphen
Package hyphen says where words may be broken at the end of a line, in the languages it has patterns for.
Package hyphen says where words may be broken at the end of a line, in the languages it has patterns for.
lang
Package lang knows the languages a document can say it is written in.
Package lang knows the languages a document can say it is written in.
mathml
Package mathml reads LaTeX math into a tree of MathML elements, which is what the renderers typeset: PDF by laying the tree out itself, DOCX by turning it into a Word equation.
Package mathml reads LaTeX math into a tree of MathML elements, which is what the renderers typeset: PDF by laying the tree out itself, DOCX by turning it into a Word equation.
mermaid
Package mermaid reads Mermaid flowcharts and lays them out as a drawing: shapes, connecting lines and text with positions, which the renderers put on a page.
Package mermaid reads Mermaid flowcharts and lays them out as a drawing: shapes, connecting lines and text with positions, which the renderers put on a page.
settings
Package settings persists the user's markout preferences between runs.
Package settings persists the user's markout preferences between runs.
theme
Package theme describes how a converted document looks: page, fonts, sizes, spacing and colors.
Package theme describes how a converted document looks: page, fonts, sizes, spacing and colors.
tui
Package tui implements the interactive terminal UI for markout.
Package tui implements the interactive terminal UI for markout.
update
Package update finds out whether a newer markout release exists.
Package update finds out whether a newer markout release exists.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL