Clone: git clone https://gitbay.org/krz/org-swift.git
OrgSwift
A dependency-free Swift library that renders a practical subset of org-mode to sanitized HTML. Pure Foundation — no SwiftUI, WebKit, UIKit, or third-party packages.
Originally extracted from the hand-rolled renderer in the Hutch iOS client so it could be shared across apps, and since rebuilt around an element tree: org is parsed once into a document model, and each output format walks it. See ARCHITECTURE.md.
Supported syntax
Headings (with trailing :tags:), paragraphs, ordered/unordered lists (with
arbitrary-depth nesting, wrapped lines, and [ ]/[x]/[-] task checkboxes),
description lists (term :: definition → <dl>), multi-paragraph list items,
tables (with :---: alignment), #+begin_src / example / quote / center /
verse / export blocks and #+begin_<name> special blocks,
#+TITLE/#+AUTHOR/#+DATE metadata, #+CAPTION/#+ATTR_HTML/#+NAME
figures, org links and images ([[file:x.png]] → <img>, [[dest][label]] →
link, [[id:target]] → #target), horizontal rules, comments, timestamps
(<2024-01-15 Mon>, inactive, times, and ranges → <time>), footnotes
(references, inline, and definitions → a <section class="footnotes">), and
inline markup (*bold*, /italic/, ~code~, =verbatim=, +strike+,
_underline_, x^2 superscript, bare-URL and email autolinks). Other
#+KEYWORD: lines are consumed as document metadata.
Output is sanitized: only http/https/mailto link schemes and
http/https image schemes are allowed; everything else is dropped.
Usage
import OrgSwift
let html = OrgRenderer.renderToHTML(orgSource)
Relative links
Pass an OrgRenderOptions to rewrite repository-relative links and images to
absolute blob URLs. When owner/repositoryName are nil (the default),
relative links are left as-is, which the scheme allowlist then drops.
let html = OrgRenderer.renderToHTML(
orgSource,
options: OrgRenderOptions(
host: "git.sr.ht", // default
owner: "~ccleberg",
repositoryName: "Hutch",
ref: "HEAD", // default
readmePath: "README.org" // resolves paths relative to this file
)
)
./images/badge.svg then resolves to
https://git.sr.ht/~ccleberg/Hutch/blob/HEAD/images/badge.svg.
Title and heading level
Two presentation knobs, both defaulting to how Hutch rendered:
metadataHeader(defaulttrue) — emit a leading<div class="org-metadata">with the#+TITLE/#+AUTHOR/#+DATE. Setfalseto drop it (e.g. when the surrounding UI shows the title itself).headingLevelOffset(default0) — added to each heading's star count, clamped to1...6. Default renders*as<h1>; use1to render*as<h2>, leaving<h1>for a document title.
Syntax highlighting
Code blocks are highlighted through a protocol so the library carries no highlighter dependency:
public protocol CodeHighlighter {
func highlightedHTML(code: String, language: String?) -> String?
}
The default PlainCodeHighlighter returns nil, so blocks fall back to escaped
<pre><code>. Provide your own conformer to plug in a real highlighter:
let html = OrgRenderer.renderToHTML(orgSource, highlighter: MyHighlighter())
A conformer returning nil for a given block gets the same escaped fallback.
Conformance
OrgSwift is tested against the shared org-conformance
corpus, whose golden outputs come from orgo (itself validated against Emacs
ox-html). swift test locates a sibling ../org-conformance checkout, or set
ORG_CONFORMANCE_DIR. The suite reduces both this renderer's HTML and orgo's to
a semantic skeleton and compares them; the current pass/divergence state is
recorded per case, so a closed gap or a regression both surface as a failing
test. See GAPS.md for the outstanding backlog.