← Back

ofxImGuiMarkdown

github.com/ofxyz/ofxImGuiMarkdown · @ofxyz
OF 0.12 GUI

Markdown Preview for ImGui in OpenFrameworks

1 0 Last commit 4mo ago Created 5mo ago

README

ofxImGuiMarkdown



Markdown preview widget for Dear ImGui inside openFrameworks, built on top of imgui_md and MD4C.

preview

Supported Markdown Features



Everything below is implemented and exercised by example-markdownEditor:

CategoryFeatures
StructureParagraphs, soft-wrapped text, headers (H1–H6), horizontal rules
EmphasisBold, italic, bold+italic (nested or combined), ~~strikethrough~~, <u>underline</u>
ListsOrdered and unordered lists, including nested sub-lists
Linkslabel — opens via ofLaunchBrowser; [[Page\label]] wiki links (styled, clickable)
Imagesalt — all raster formats ofImage loads (FreeImage), plus SVG preview (ofxSvg); cached; optional http(s):// URLs
QuotesBlock quotes with left bar and tinted text
CodeInline ` code spans and fenced blocks with syntax highlighting (ofxImGuiTextEdit)
TablesResizable columns, borders, alternating row background, bold headers (stretch to panel width)
HTML
, <hr>, <u>, </u>, <div class="...">, </div> (style via html_div() override)
OtherBackslash escapes, &nbsp; entity
Word wrap uses font measurement by default. When ofxUnicode is linked, line breaks also follow Unicode rules for the configured wrapLanguage (default "en").

Not supported (yet)



These MD4C constructs are parsed by the underlying library but have empty handlers in
imgui_md — they render as plain text or are skipped:

Raw HTML blocks beyond the tags listed above are ignored. <div class="..."> emits enter/leave callbacks but has no default styling — subclass and override html_div() to style named classes.

Font coverage



With bundled ofxImGuiStyle fonts, all emphasis styles have a dedicated JetBrains Mono face:

Renderer fieldJetBrains MonoUsed for
regularFontRegularBody text
boldFontBoldBold, table headers
italicFontItalicItalic
boldItalicFontBoldItalicBold + italic (nested ……… or ………)
monoFontRegularInline and fenced code
headingFontBold @ larger sizeH1 (example uses 22 px)
h2FontoptionalH2 only; when unset, H2 uses headingScale[2]
(H3–H6)boldFont / regularFontSized by headingScale[3] … [6]
That is full variant coverage for the bundled monospace family. It is not 100% MD4C/imgui_md feature coverage — see Not supported above.

Dependencies



AddonRole
ofxImGuiImGui integration
ofxImGuiTextEditFenced code blocks with language highlighting
ofxUnicodeOptional — Unicode-aware line breaking in preview wrap
ofxImGuiStyleOptional — bundled Input Sans + JetBrains Mono (no disk font files)

Usage



#include "ofxImGuiMarkdown.h"

include "ImFonts.h"

// --- In your ofApp class --- ofxImGui::Gui gui; ofxMarkdownRenderer renderer; // --- setup() --- ofDisableArbTex(); // before textures / gui.setup() — see Images below gui.setup(); // Bundled fonts (ofxImGuiStyle): if (ImFont* ui = ImFonts::LoadDefaultFonts(ImGui::GetIO().Fonts, 15.f)) gui.setDefaultFont(ui); ImFonts::JetBrainsMonoFonts jbm = ImFonts::LoadJetBrainsMono(ImGui::GetIO().Fonts, 15.f); renderer.regularFont = jbm.regular; renderer.boldFont = jbm.bold; renderer.italicFont = jbm.italic; renderer.boldItalicFont = jbm.boldItalic; renderer.monoFont = jbm.regular; renderer.headingFont = ImFonts::LoadJetBrainsMonoFont( ImGui::GetIO().Fonts, 22.f, ImFonts::JetBrainsMonoVariant::Bold); gui.rebuildFontsTexture(); // Or load from disk via gui.addFont(): // renderer.regularFont = gui.addFont(ofToDataPath("fonts/MyRegular.ttf"), 15.0f); // renderer.boldFont = gui.addFont(ofToDataPath("fonts/MyBold.ttf"), 15.0f); // renderer.italicFont = gui.addFont(ofToDataPath("fonts/MyItalic.ttf"), 15.0f); // renderer.boldItalicFont = gui.addFont(ofToDataPath("fonts/MyBoldItalic.ttf"), 15.0f); // --- draw() --- gui.begin(); if (ImGui::Begin("Markdown Preview")) { renderer.render(myMarkdownString); } ImGui::End(); gui.end();


Convenience Free Function



For simple cases without custom fonts, use the free function which keeps a shared static renderer:

gui.begin();
if (ImGui::Begin("Preview")) {
    ofxRenderMarkdown(markdownText);
}
ImGui::End();
gui.end();


Images



Inline images use the same loaders as openFrameworks itself — not a hand-picked subset.

Raster — anything
ofImage can read via FreeImage, including:

CommonAlso supported
PNG (.png)BMP, ICO, TIFF (.tif/.tiff), TGA
JPEG (.jpg, .jpeg)GIF (first frame), PSD, PPM/PGM/PBM
EXR, HDR, DDS, WBMP, PCX, SGI, JP2, …
Format detection is by file content and extension (FreeImage), so extensionless paths still work when the file is readable.

Vector — SVG (
.svg) is rasterised through ofxSvg into an off-screen FBO at svgScale (default 2.0) for crisp preview in ImGui. This is separate from ofImage because openFrameworks does not load SVG as a bitmap natively.

Paths — resolved in order: path as written (relative to the app’s working directory, usually
bin/), then ofToDataPath(). Example: data/photo.tiff or photo.tiff in bin/data/.

Remote —
http:// and https:// URLs work for raster images (ofImage network load). SVG URLs are not fetched automatically.

Images are loaded on first encounter and cached for the lifetime of the renderer. Call
renderer.clearImageCache() after a GL context rebuild or to force a reload.

Failed loads are skipped silently (no broken-image placeholder). Check the verbose log for
"Image not loaded:" messages.

Important: ARB textures must be disabled before images are loaded so that texture coordinates are normalised to 0–1 (as ImGui requires). Call
ofDisableArbTex() once in setup() before gui.setup():

void ofApp::setup() {
    ofDisableArbTex();
    gui.setup();
    // ...
}


To cap display width (aspect ratio preserved):

renderer.maxImageWidth = 400.0f; // pixels; 0 = auto-fit to window width


For sharper SVG on HiDPI displays:

renderer.svgScale = 2.0f; // 1.0 = native SVG units, 2.0 = 2× supersampling


To customise loading (URLs, tint, placeholders), subclass
ofxMarkdownRenderer and override get_image():

struct MyRenderer : public ofxMarkdownRenderer {
    bool get_image(image_info& nfo) const override {
        // m_href contains the image path/URL from the markdown source
        nfo.texture_id = (ImTextureID)(uintptr_t)myTexture.getTextureData().textureID;
        nfo.size       = { 200, 150 };
        nfo.uv0        = { 0.0f, 0.0f };
        nfo.uv1        = { 1.0f, 1.0f };
        nfo.col_tint   = { 1.0f, 1.0f, 1.0f, 1.0f };
        nfo.col_border = { 0.0f, 0.0f, 0.0f, 0.0f };
        return true;
    }
};


Wiki links



MD4C wiki-link syntax is enabled:
[[Page]], [[Page|label]], [[path/to/Page]], [[https://example.com|Example]], and [[#anchor]].

Wiki links use the same link styling as
text (colour, underline, tooltip showing the target). On click:

renderer.wikiLinkBasePath = ofToDataPath("", true); // bare "Notes" -> data/Notes.md

renderer.onWikiLinkClicked = [&renderer](const std::string& target) {
    const std::string path = renderer.resolveWikiPagePath(target);
    if (!path.empty() && ofFile::doesFileExist(path))
        loadMarkdownFile(path);
    else if (target.find("://") != std::string::npos)
        ofLaunchBrowser(target);
};


example-markdownEditor loads data/README.md when you click [[README|README]].

Example



example-markdownEditor — a docked live editor/preview split using ofxImGuiTextEdit (Markdown language, soft wrap) on the left and ofxMarkdownRenderer on the right. Edit markdown source and see the rendered output update in real time.

Requires
ofxImGui, ofxImGuiTextEdit, ofxImGuiStyle, ofxSvg, and optionally ofxUnicode in addons.make.

Vendored Libraries



LibrarySourceLicense
imgui_mdlibs/imgui_md/src/MIT
MD4Clibs/md4c/src/`MIT

Get this addon