{"id":50090728,"url":"https://github.com/tisgoud/omd2typst","last_synced_at":"2026-05-25T22:03:59.259Z","repository":{"id":359674979,"uuid":"1246730994","full_name":"tIsGoud/Omd2Typst","owner":"tIsGoud","description":"Obsidian MarkDown conversion 2 Typst and PDF.","archived":false,"fork":false,"pushed_at":"2026-05-22T21:31:14.000Z","size":2337,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-22T23:37:29.769Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/tIsGoud.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-22T13:48:41.000Z","updated_at":"2026-05-22T21:31:15.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/tIsGoud/Omd2Typst","commit_stats":null,"previous_names":["tisgoud/omd2typst"],"tags_count":10,"template":false,"template_full_name":null,"purl":"pkg:github/tIsGoud/Omd2Typst","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tIsGoud%2FOmd2Typst","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tIsGoud%2FOmd2Typst/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tIsGoud%2FOmd2Typst/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tIsGoud%2FOmd2Typst/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tIsGoud","download_url":"https://codeload.github.com/tIsGoud/Omd2Typst/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tIsGoud%2FOmd2Typst/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33494788,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-25T14:31:05.219Z","status":"ssl_error","status_checked_at":"2026-05-25T14:31:02.878Z","response_time":57,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-05-22T23:07:12.234Z","updated_at":"2026-05-25T22:03:59.252Z","avatar_url":"https://github.com/tIsGoud.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Omd2Typst\n\nConvert Obsidian Markdown notes to publication-quality PDFs via Typst.\n\n---\n\n## Why this tool exists\n\n[Obsidian](https://obsidian.md) is an excellent writing and knowledge-management tool, but its built-in PDF export is limited: no custom cover pages, no structured headers/footers, no table of contents with numbering, and limited control over typography and layout.\n\n[Typst](https://typst.app) is a modern typesetting system — fast, scriptable, and capable of producing professional documents. It uses its own markup language rather than LaTeX, making it far easier to customise.\n\n**omd2typst** bridges the two: it reads an Obsidian Markdown file and produces either a `.typ` source file or a compiled `.pdf`, using a Typst template to control all visual aspects of the document.\n\n---\n\n## Workflow\n\n```\nObsidian note (.md)\n        │\n        ▼\n  ┌─────────────┐\n  │   omd2typst │  parse → intermediate AST → render → compile (in-process)\n  └─────────────┘\n        │\n        ├──► Typst source (.typ)   ← inspect or hand-edit if needed\n        │\n        └──► PDF document          ← compiled in-process, no external tools\n```\n\n### Step by step\n\n1. **Write** your document in Obsidian using standard Markdown with YAML frontmatter.\n2. **Run omd2typst** to convert it to a Typst source file (`.typ`) or directly to a PDF.\n3. **Customise the template** (optional) to match your house style — cover page, fonts, colours, headers/footers, callout styles, etc.\n\nThe intermediate `.typ` output is optional — requesting a `.typ` output file produces a human-readable source that can be inspected or hand-edited before compiling, useful for one-off adjustments. When exporting directly to PDF, compilation happens in-process without writing an intermediate file to disk.\n\n---\n\n## Why this architecture\n\n| Decision | Reason |\n|---|---|\n| **Intermediate AST** | A clean separation between parsing (comrak) and rendering (Typst) makes it straightforward to add new Markdown features or change the output format independently. |\n| **Template-based styling** | All visual decisions live in a single `.typ` file. Users can customise fonts, colours, cover pages and layout without modifying Rust code. |\n| **comrak for parsing** | comrak is a fully CommonMark-compliant Rust parser with Obsidian-relevant extensions (tables, strikethrough, math, footnotes). |\n| **Typst for output** | Typst produces high-quality PDFs, has native math support, handles fonts and colours well, and its scripting model makes dynamic content (list of figures, conditional pages) simple. |\n| **Two-step output** | Emitting `.typ` first allows the user to inspect, tweak, and reuse the intermediate file. Direct PDF output is also supported when inspection is not needed. |\n\n---\n\n## Installation\n\n### Pre-built binary (Linux x86_64)\n\nDownload the latest `omd2typst-linux-x86_64` binary from the [Releases page](https://codeberg.org/tisgoud/omd2typst/releases), make it executable, and place it on your PATH:\n\n```bash\nchmod +x omd2typst-linux-x86_64\nmv omd2typst-linux-x86_64 ~/.local/bin/omd2typst\n```\n\n### Build from source (macOS, Windows, Linux)\n\nRequires [Rust](https://rustup.rs) (stable):\n\n```bash\ncargo install omd2typst\n```\n\nOr clone and build:\n\n```bash\ngit clone https://codeberg.org/tisgoud/omd2typst\ncd omd2typst\ncargo build --release\n# Binary: target/release/omd2typst\n```\n\n---\n\n## Usage\n\n```bash\n# Convert to Typst source\nomd2typst input.md output.typ\n\n# Convert directly to PDF\nomd2typst input.md output.pdf\n\n# Use a custom template\nomd2typst input.md output.pdf --template my-template.typ\n\n# Export the built-in template as a starting point for customisation\nomd2typst --export-template my-template.typ\n```\n\n### YAML frontmatter\n\nThe following frontmatter keys have special meaning in the built-in template:\n\n| Key | Description |\n|---|---|\n| `title` | Document title — shown on the cover page; also offsets heading levels (`##` → `=`). Optional: if omitted, the text of the first `# Heading` is used automatically. |\n| `subtitle` | Subtitle shown below the title on the cover page |\n| `author` | Author name |\n| `date` | Document date |\n| `version` | Version string |\n| `status` | Status label (e.g. *Concept*, *Final*) |\n| `summary` | Abstract shown between the title and metadata on the cover page |\n| `figure-list` | Set to `true` to generate a list of figures after the table of contents |\n| `revision-table` | Name of the level-2 section that contains the revision history table |\n| `approval-table` | Name of the level-2 section that contains the approval table |\n| `language` | UI language for template strings. Supported out of the box: `nl` (default), `en`, `de`, `es`, `fr`. See *Language support* below. |\n\nAll frontmatter keys are also available in the template as a Typst dictionary `fm`, so you can add your own keys and reference them freely with `fm.at(\"key\", default: \"\")`.\n\n#### Title fallback\n\nThe `title` key is optional. When it is absent, omd2typst automatically uses the text of the first level-1 heading (`# …`) as the document title. The heading is then omitted from the body so it does not appear twice.\n\n\u003e **A title is required for correct rendering.** Either set `title` in the frontmatter or open the document with a level-1 heading (`# …`). If both are absent, the heading level offset is not applied and the table of contents will not render correctly.\n\n#### Revision and approval tables\n\nTwo optional front-matter keys — `revision-table` and `approval-table` — enable a dedicated page that appears between the cover page and the table of contents. Each key holds the exact name of a level-2 section (`## …`) in the document body. omd2typst extracts the content of that section, places it on the pre-TOC page, and removes the section from the document body entirely. The headings are not numbered and do not appear in the table of contents.\n\nBoth keys are independent: you can use either one or both together. When both are present their tables are placed on the same page separated by whitespace.\n\n**Frontmatter:**\n\n```yaml\nrevision-table: Revision History\napproval-table: Approval\n```\n\n**Document body** — standard level-2 headings with Markdown tables underneath:\n\n```markdown\n## Revision History\n\n| Version | Date       | Author | Description             |\n|---------|------------|--------|-------------------------|\n| 0.9     | 2026-05-01 | Alice  | Initial draft           |\n| 1.0     | 2026-05-16 | Alice  | Review comments applied |\n\n## Approval\n\n| Role     | Name  | Date       | Signature |\n|----------|-------|------------|-----------|\n| Author   | Alice | 2026-05-16 |           |\n| Reviewer | Bob   | 2026-05-16 |           |\n```\n\nThe section name in the frontmatter must match the heading text exactly (case-sensitive). Any content that is valid under a normal heading — tables, paragraphs, lists — can be used inside these sections.\n\n---\n\n## Example documents\n\nThe `input/` directory contains a set of example documents that exercise the full feature set — cover page, table of contents, revision and approval tables, images, callouts, tables, lists, and a figure list.\n\n| File | Language |\n|---|---|\n| `input/input-nl.md` | Dutch (`nl`) |\n| `input/input-en.md` | English (`en`) |\n| `input/input-de.md` | German (`de`) |\n| `input/input-es.md` | Spanish (`es`) |\n| `input/input-fr.md` | French (`fr`) |\n\nAll five files share the same structure and content; only the language and translated text differ. They rely on the image `input/_assets/laptop.png`.\n\nTo compile one of the examples using the default embedded template:\n\n```bash\nomd2typst input/input-en.md output/input-en.pdf\n```\n\nTo use a bundled template instead:\n\n```bash\nomd2typst input/input-en.md output/input-en.pdf --template templates/template-duo.typ\n```\n\n---\n\n## Templates\n\nA template is a `.typ` file that must export two symbols:\n\n- **`template`** — a show-rule function `(doc) =\u003e { … }` that wraps the entire document. It controls page setup, fonts, heading styles, and all other global styling.\n- **`callout`** — a function `(kind, title, body)` that renders Obsidian callout blocks.\n\nExport the built-in template as a starting point:\n\n```bash\nomd2typst --export-template my-template.typ\n```\n\nThen edit it and pass it back with `--template my-template.typ`.\n\n### Language support\n\nTemplates contain a `_lang_strings` dictionary that maps language codes to UI strings (table-of-contents label, page counter, figure list header, metadata labels, etc.). The active language is selected with the `language` frontmatter key; when the key is absent the template falls back to its own default language.\n\n**Supported languages** in the built-in templates:\n\n| Code | Language |\n|---|---|\n| `nl` | Dutch (default) |\n| `en` | English |\n| `de` | German |\n| `es` | Spanish |\n| `fr` | French |\n\n**Selecting a language:**\n\n```yaml\nlanguage: en\n```\n\n**Adding a new language** — open the template file and add an entry to `_lang_strings`:\n\n```typst\n#let _lang_strings = (\n  \"nl\": ( toc: \"Inhoudsopgave\", figures: \"Lijst van figuren\", ... ),\n  \"en\": ( toc: \"Table of Contents\", figures: \"List of Figures\", ... ),\n  \"de\": ( toc: \"Inhaltsverzeichnis\", figures: \"Abbildungsverzeichnis\", ... ),\n)\n```\n\nThe keys required for each language entry are: `toc`, `figures`, `summary`, `version`, `status`, `date`, `author`, `page`, `of`, `fig_nr`, `fig_desc`. If an unknown code is passed the template silently falls back to Dutch.\n\n**Creating a language-default template** — if you always write in English, change the fallback in the template function:\n\n```typst\nlet _lang = fm.at(\"language\", default: \"en\")\n```\n\n---\n\n## Font handling\n\nTypst always embeds all fonts used by a document directly into the PDF. The output file is therefore self-contained: recipients do not need to install any font to view or print it correctly.\n\n### Embedded fallback fonts\n\nomd2typst bundles **Liberation Sans** and **Liberation Serif** inside the binary itself. They are always available regardless of what the host system has installed.\n\nThese fonts are the **global last-resort fallback** for every document compiled by omd2typst:\n\n- When a preferred font such as Verdana or Arial is present on the system, it is used.\n- When none of the fonts in a template's font stack can be found — including external templates whose fonts are not installed — Liberation Sans is used instead of Typst's built-in default of Libertinus Serif.\n- A document always uses a sans-serif body font; it never silently falls back to a serif or monospace face.\n\n| Font | Metric-compatible with | Role |\n|---|---|---|\n| Liberation Sans | Arial / Helvetica | Sans-serif fallback — last resort for any unavailable font |\n| Liberation Serif | Times New Roman | Serif fallback for templates that explicitly use it |\n\n### User fonts directory\n\nPlace any `.ttf` or `.otf` font files in a `fonts/` directory next to the `omd2typst` binary. These fonts are discovered automatically at startup and are available to any template.\n\n```\nmy-project/\n  omd2typst          ← binary\n  fonts/\n    CorpSans-Regular.ttf\n    CorpSans-Bold.ttf\n  document.md\n  my-template.typ\n```\n\nThis layout is convenient for CI/CD pipelines where the binary, input files, and custom fonts travel together as a unit.\n\n### Using Liberation fonts in custom templates\n\nBecause Liberation Sans and Liberation Serif are always available, you can reference them directly in any custom template as a reliable fallback at the end of your font stack:\n\n```typst\n// Sans-serif template — Liberation Sans guarantees a consistent fallback\nset text(font: (\"Your Custom Font\", \"Verdana\", \"Arial\", \"Liberation Sans\"))\n\n// Serif template — Liberation Serif guarantees a consistent fallback\nset text(font: (\"Your Serif Font\", \"Georgia\", \"Times New Roman\", \"Liberation Serif\"))\n```\n\n---\n\n## Supported Markdown / Obsidian features\n\n| Feature | Syntax | Notes |\n|---|---|---|\n| YAML frontmatter | `---` block | Strings, numbers, booleans, inline arrays |\n| Headings | `# H1` … `###### H6` | Level automatically offset when document has a title |\n| Bold | `**text**` or `__text__` | |\n| Italic | `*text*` or `_text_` | |\n| Strikethrough | `~~text~~` | Obsidian native |\n| Highlight | `==text==` | Obsidian native |\n| Inline code | `` `code` `` | |\n| Links | `[text](url)` | |\n| Images — standard | `![alt](path)` | |\n| Images — with width | `![alt\\|200](path)` | Width in points |\n| Images — wikilink | `![[path\\|200]]` | Obsidian wikilink format |\n| Code block | ` ```lang ``` ` | Language tag preserved for syntax highlighting |\n| Table | `| col |` | Left / center / right column alignment |\n| Callout | `\u003e [!type] Title` | 13 built-in types with Lucide SVG icons in each type's accent colour |\n| Block quote | `\u003e text` | Left accent bar with indented text; same colour as the summary box border |\n| Bullet list | `- item` | Nested to any depth |\n| Ordered list | `1. item` | Nested to any depth |\n| Checkbox list | `- [ ]`, `- [x]`, `- [i]` | 10 emoji variants — see table below |\n| Superscript | `\u003csup\u003etext\u003c/sup\u003e` | HTML inline; also renders in Obsidian preview |\n| Subscript | `\u003csub\u003etext\u003c/sub\u003e` | HTML inline; also renders in Obsidian preview |\n| Inline math | `$formula$` | Typst math syntax |\n| Display math | `$$formula$$` | Centred display block |\n| Footnotes | `[^1]` / `[^1]: text` | Rendered as Typst footnotes (bottom of the page) |\n| Thematic break | `---` | Full-width horizontal rule |\n\n### Callout types and icons\n\nEach callout type has a Lucide SVG icon rendered inline before the title, stroked in the type's accent colour. Unknown types show the title without an icon.\n\n| Type | Lucide icon | Accent colour |\n|---|---|---|\n| `note` / `info` | info (circle + i) | blue `#1d4ed8` |\n| `tip` / `hint` | lightbulb | green `#15803d` |\n| `important` | circle-alert (circle + !) | green `#15803d` |\n| `warning` / `caution` / `attention` | triangle-alert | amber `#a16207` |\n| `danger` | flame | red `#b91c1c` |\n| `error` | circle-x | red `#b91c1c` |\n| `bug` | bug | red `#b91c1c` |\n| `quote` / `cite` | quote | slate `#475569` |\n| *(unknown type)* | *(none — title only)* | grey `#374151` |\n\nCallouts are non-breakable by default — they will not be split across a page break.\n\n### Checkbox variants\n\n| Syntax | Emoji | Meaning |\n|---|---|---|\n| `- [ ]` | 🔲 | Empty / to do |\n| `- [x]` or `- [X]` | ✅ | Done |\n| `- [/]` | 🔄 | In progress |\n| `- [-]` | 🚫 | Cancelled |\n| `- [\u003e]` | ➡️ | Forwarded |\n| `- [!]` | ⚠️ | Important |\n| `- [?]` | ❓ | Question |\n| `- [i]` | 💡 | Idea |\n| `- [I]` | 📖 | Info |\n| `- [*]` | ⭐ | Star |\n\n### Block quotes\n\nA standard Markdown block quote is written with a `\u003e` prefix:\n\n```markdown\n\u003e \"The best way to predict the future is to invent it.\" — Alan Kay\n```\n\nIn the PDF the quote is rendered with a thin vertical bar on the left margin and the text indented by 1 em. The bar uses the same colour (`#c5d8f0`) and stroke weight (1 pt) as the border of the summary box on the cover page, giving a consistent visual language across the document.\n\nBlock quotes and callouts share the same `\u003e` prefix syntax. omd2typst distinguishes them by the presence of a `[!type]` marker on the first line: a line that starts with `\u003e [!note]` (or any recognised callout type) is treated as a callout; everything else is a plain block quote.\n\n```markdown\n\u003e This is a plain block quote.\n\n\u003e [!note] This is a callout\n\u003e The [!type] marker makes the difference.\n```\n\n---\n\n## Not supported\n\nThe following Obsidian and Markdown features are **not** handled. They will either be silently dropped or passed through as plain text.\n\n| Feature | Reason |\n|---|---|\n| Wikilinks `[[Page Name]]` | Internal note links have no meaning in a single-file PDF export |\n| Note embeds `![[Note Name]]` | Embedding other notes would require resolving the full vault at runtime |\n| Obsidian comments `%%…%%` | Stripped from output (inline and block) |\n| Tags `#tag` | No equivalent concept in a PDF document |\n| Mermaid diagrams | Typst has no native Mermaid renderer |\n| Obsidian Tasks plugin fields | Due dates, priorities, recurrence — tool-specific syntax not in standard Markdown |\n| Dataview queries | Require vault-wide evaluation at Obsidian runtime |\n| Canvas files (`.canvas`) | JSON-based spatial format, not Markdown |\n| Multi-file documents | Each run converts a single `.md` file |\n| Definition lists | Not part of CommonMark; no comrak extension available |\n| Subscript `~text~` / Superscript `^text^` | Not native to Obsidian; use `\u003csub\u003e`/`\u003csup\u003e` HTML instead |\n| LaTeX math | Typst uses its own math syntax; formulas must be written in Typst notation |\n\n---\n\n## Math notation\n\nMath content is passed directly to Typst. Typst uses **its own math notation**, which is similar to but not identical to LaTeX.\n\n```markdown\nInline:  $a^2 + b^2 = c^2$\n\nDisplay:\n$$sum_(k=1)^n k = (n(n+1)) / 2$$\n```\n\nCommon differences from LaTeX:\n\n| LaTeX | Typst |\n|---|---|\n| `\\frac{a}{b}` | `a/b` or `frac(a, b)` |\n| `\\sqrt{x}` | `sqrt(x)` |\n| `\\sum_{i=1}^{n}` | `sum_(i=1)^n` |\n| `\\alpha`, `\\beta` | `alpha`, `beta` |\n| `\\times` | `times` |\n\nFor LaTeX compatibility the [`mitex`](https://typst.app/universe/package/mitex) Typst package can be added to the template, but this is not built in.\n\n---\n\n## Project structure\n\nThis repository is a Cargo workspace with four crates:\n\n```\ncrates/\n  core/         — omd2typst-core (library)\n    src/\n      lib.rs        — public API: parse_markdown, render_typst, RenderOptions, BUILTIN_TEMPLATE\n      ast.rs        — Intermediate AST: Block, Inline, Document\n      parser.rs     — Markdown → AST via comrak + custom preprocessing\n      renderer.rs   — AST → Typst source; built-in preamble and template\n  cli/          — omd2typst (binary) — thin shell over omd2typst-core\n    src/main.rs     — CLI (clap), file I/O, typst invocation\n  wasm/         — omd2typst-wasm (cdylib) — WASM bindings for the Obsidian plugin\n    src/lib.rs      — render_to_typst, get_builtin_template via wasm-bindgen\n  web/          — omd2typst-web (stub, specced separately)\n    src/main.rs     — todo!()\n```\n\nThe AST is intentionally minimal — only the constructs that have a direct Typst equivalent are represented. Everything else is either preprocessed before parsing (Obsidian wikilink images) or silently ignored.\n\n---\n\n## Platform\n\nomd2typst-core is the shared foundation for four consumers:\n\n| Consumer | How it uses the core |\n|---|---|\n| **CLI** (`crates/cli`) | Native binary; Typst embedded — no external tools required |\n| **Obsidian plugin** ([obsidian-omd2typst](https://codeberg.org/tisgoud/obsidian-omd2typst)) | `crates/wasm` compiled via wasm-pack; PDF via system `typst` CLI |\n| **Web service** (`crates/web`) | Stub — specced separately |\n| **CI/CD pipelines** | CLI binary |\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftisgoud%2Fomd2typst","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftisgoud%2Fomd2typst","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftisgoud%2Fomd2typst/lists"}