A dependency-free Swift library that renders org-mode to sanitized HTML.

html library org-mode swift

Commit 1620a0f1e7

1620a0f1e742ba53f162de2e613ce25dc6fe8145

parent: e0ca3558d1

Verified · cmc

cmc <hello@cleberg.net> · 2026-08-28T18:51:52Z

OrgSwift: image-as-link descriptions render as clickable images

An org link whose description is itself an image — [[url][file:badge.svg]] (the
common build-badge form) or [[url][[badge.svg]]] — makes the image the anchor's
content instead of showing the raw description text. A plain-text description
that merely looks path-like stays text.
Sources/OrgSwift/Links.swift +22 −8
@@ -74,6 +74,19 @@ func normalizeOrgLinkTarget(_ target: String) -> String {
7474 target.hasPrefix("file:") ? String(target.dropFirst(5)) : target
7575 }
7676
77/// If a link *description* is itself an image reference `[[img]]`, a `file:` image, or an
78/// image URL return its source, so the image becomes the link's content. A bare relative
79/// string (`img.png`) is a plain text description, not an image.
80private func descriptionImageSource(_ label: String) -> String? {
81 if label.hasPrefix("[["), label.hasSuffix("]]") {
82 return String(label.dropFirst(2).dropLast(2))
83 }
84 if label.hasPrefix("file:") || label.hasPrefix("http://") || label.hasPrefix("https://") {
85 return label
86 }
87 return nil
88}
89
7790 private func renderOrgLink(
7891 destination rawDestination: String,
7992 label: String?,
@@ -90,15 +103,16 @@ private func renderOrgLink(
90103 return ##"<a href="#\##(escapeHTMLAttribute(id))">\##(text)</a>"##
91104 }
92105 let destination = normalizeOrgLinkTarget(rawDestination)
93 if let label, label.hasPrefix("[["), label.hasSuffix("]]") {
94 let source = String(label.dropFirst(2).dropLast(2))
95 if let imageHTML = makeOrgImageHTML(source: source, alt: nil, imageURLResolver: imageURLResolver) {
96 let resolvedDestination = linkURLResolver?(destination) ?? destination
97 guard let sanitizedURL = sanitizedReadmeLinkURLString(resolvedDestination) else {
98 return imageHTML
99 }
100 return #"<a href="\#(sanitizedURL)">\#(imageHTML)</a>"#
106 // A description that is itself an image link (`[[url][file:badge.svg]]`, the common
107 // build-badge form, or the double-bracketed `[[url][[badge.svg]]]`) makes the image the
108 // clickable content of the link. A plain-text description like `img.png` stays text.
109 if let label, let source = descriptionImageSource(label),
110 let imageHTML = makeOrgImageHTML(source: source, alt: nil, imageURLResolver: imageURLResolver) {
111 let resolvedDestination = linkURLResolver?(destination) ?? destination
112 guard let sanitizedURL = sanitizedReadmeLinkURLString(resolvedDestination) else {
113 return imageHTML
101114 }
115 return #"<a href="\#(sanitizedURL)">\#(imageHTML)</a>"#
102116 }
103117
104118 // A bare `[[image]]` with no description is an inline image. `[[image][text]]` is a
Tests/OrgSwiftTests/OrgRendererTests.swift +16
@@ -325,6 +325,22 @@ struct OrgRendererTests {
325325 #expect(!html.contains("<img"))
326326 }
327327
328 @Test
329 func imageDescriptionBecomesClickableImage() {
330 // A build-badge: the description is itself an image, so it is the link's content.
331 let badge = render("[[https://example.com/builds][file:https://example.com/badge.svg]]")
332 #expect(badge.contains(#"<a href="https://example.com/builds"><img src="https://example.com/badge.svg" alt=""></a>"#))
333
334 // The double-bracketed form works the same.
335 let bracketed = render("[[https://example.com/builds][[https://example.com/badge.svg]]]")
336 #expect(bracketed.contains(#"<a href="https://example.com/builds"><img"#))
337
338 // A plain-text description that merely looks path-like stays text.
339 let plain = render("[[https://example.com][see diagram.png]]")
340 #expect(plain.contains(#"<a href="https://example.com">see diagram.png</a>"#))
341 #expect(!plain.contains("<img"))
342 }
343
328344 @Test
329345 func captionPromotesImageToFigure() {
330346 let html = render("""