Skip to content
Brett Terpstra edited this page Jun 23, 2026 · 6 revisions

C API

Apex provides a simple C API for programmatic Markdown to HTML conversion.

Swift Package Note

If you use Apex via Swift Package Manager in an Xcode Swift target, add the ApexC product to your target dependencies, then use:

import ApexC

for direct C API calls like apex_options_default() and apex_markdown_to_html(...).

import Apex exposes the high-level Swift wrapper API (Apex, ApexOptions, ApexMode), which is recommended for most Swift use cases.

Direct C API from Swift (SPM)

import ApexC

let markdown = "# Hello from C API\n"

var opts = apex_options_default()
opts.mode = APEX_MODE_UNIFIED

let html: String = markdown.withCString { cstr in
    guard let out = apex_markdown_to_html(cstr, strlen(cstr), &opts) else {
        return ""
    }
    defer { apex_free_string(out) }
    return String(cString: out)
}

Overview

The Apex C API is thread-safe and designed for integration into C and C++ applications. All functions are declared in apex/apex.h.

Core Types

apex_mode_t

Processor compatibility modes:

typedef enum {
    APEX_MODE_COMMONMARK = 0,      /* Pure CommonMark spec */
    APEX_MODE_GFM = 1,              /* GitHub Flavored Markdown */
    APEX_MODE_MULTIMARKDOWN = 2,    /* MultiMarkdown compatibility */
    APEX_MODE_KRAMDOWN = 3,         /* Kramdown compatibility */
    APEX_MODE_UNIFIED = 4           /* All features enabled */
} apex_mode_t;

apex_options

Configuration structure for processing:

typedef struct {
    apex_mode_t mode;

    /* Feature flags */
    bool enable_tables;
    bool enable_footnotes;
    bool enable_definition_lists;
    bool enable_smart_typography;
    bool enable_math;
    bool enable_critic_markup;
    bool enable_wiki_links;
    bool enable_task_lists;
    bool enable_attributes;
    bool enable_callouts;
    bool enable_marked_extensions;

    /* Critic markup mode */
    int critic_mode;  /* 0=accept, 1=reject, 2=markup (default) */

    /* Metadata handling */
    bool strip_metadata;
    bool enable_metadata_variables;

    /* File inclusion */
    bool enable_file_includes;
    int max_include_depth;
    const char *base_directory;

    /* Output options */
    bool unsafe;  /* Allow raw HTML */
    bool validate_utf8;
    bool github_pre_lang;
    bool standalone;  /* Generate complete HTML document */
    bool pretty;      /* Pretty-print HTML */
    const char *stylesheet_path;
    const char *document_title;

    /* Line break handling */
    bool hardbreaks;  /* Treat newlines as hard breaks */
    bool nobreaks;    /* Render soft breaks as spaces */

    /* Header ID generation */
    bool generate_header_ids;
    bool header_anchors;  /* Generate <a> tags instead of id attributes */
    int id_format;  /* 0=GFM, 1=MMD, 2=Kramdown */

    /* Table options */
    bool relaxed_tables;

    /* List options */
    bool allow_mixed_list_markers;  /* Allow mixed list markers at same level (inherit type from first item) */
    bool allow_alpha_lists;  /* Support alpha list markers (a., b., c. and A., B., C.) */
} apex_options;

Core Functions

apex_options_default

Get default options with all features enabled (unified mode).

apex_options apex_options_default(void);

Returns: Default options structure

Example:

apex_options opts = apex_options_default();
opts.enable_math = true;
opts.pretty = true;

apex_options_for_mode

Get options configured for a specific processor mode.

apex_options apex_options_for_mode(apex_mode_t mode);

Parameters:

  • mode - Desired processor mode

Returns: Options configured for the specified mode

Example:

apex_options gfm_opts = apex_options_for_mode(APEX_MODE_GFM);
apex_options mmd_opts = apex_options_for_mode(APEX_MODE_MULTIMARKDOWN);

apex_markdown_to_html

Main conversion function - converts Markdown to HTML.

char *apex_markdown_to_html(const char *markdown, size_t len,
                            const apex_options *options);

Parameters:

  • markdown - Input Markdown text (UTF-8)
  • len - Length of input text (use strlen() if null-terminated)
  • options - Processing options (NULL for defaults)

Returns: Newly allocated HTML string (must be freed with apex_free_string), or NULL on error

Example:

const char *markdown = "# Hello\n\nThis is **bold**.";
apex_options opts = apex_options_default();
char *html = apex_markdown_to_html(markdown, strlen(markdown), &opts);

if (html) {
    printf("%s\n", html);
    apex_free_string(html);
}

apex_free_string

Free a string allocated by Apex.

void apex_free_string(char *str);

Parameters:

  • str - String to free (can be NULL, safe to call on NULL)

Example:

char *html = apex_markdown_to_html(markdown, len, NULL);
// Use html...
apex_free_string(html);

apex_wrap_html_document

Wrap HTML content in a complete HTML5 document structure.

char *apex_wrap_html_document(const char *content, const char *title,
                              const char *stylesheet_path);

Parameters:

  • content - HTML content to wrap
  • title - Document title (NULL for default "Document")
  • stylesheet_path - Path to CSS file to link (NULL for none)

Returns: Newly allocated HTML document string (must be freed with apex_free_string)

Example:

char *fragment = apex_markdown_to_html(markdown, len, &opts);
char *document = apex_wrap_html_document(fragment, "My Document", "style.css");
apex_free_string(fragment);
// Use document...
apex_free_string(document);

apex_pretty_print_html

Pretty-print HTML with proper indentation.

char *apex_pretty_print_html(const char *html);

Parameters:

  • html - HTML to format

Returns: Newly allocated formatted HTML string (must be freed with apex_free_string)

Example:

char *html = apex_markdown_to_html(markdown, len, &opts);
char *pretty = apex_pretty_print_html(html);
apex_free_string(html);
// Use pretty...
apex_free_string(pretty);

Version Functions

apex_version_string

Get version string.

const char *apex_version_string(void);

Returns: Version string (e.g., "0.1.0")

apex_version_major / apex_version_minor / apex_version_patch

Get individual version components.

int apex_version_major(void);
int apex_version_minor(void);
int apex_version_patch(void);

Returns: Version component as integer

List Options

Mixed List Markers

When allow_mixed_list_markers is enabled, lists with different marker types at the same indentation level will inherit the type from the first item. This is enabled by default in MultiMarkdown and Unified modes.

apex_options opts = apex_options_for_mode(APEX_MODE_UNIFIED);
// opts.allow_mixed_list_markers is true by default

// To enable in other modes:
opts.allow_mixed_list_markers = true;

// To disable in unified/multimarkdown modes:
opts.allow_mixed_list_markers = false;

Example markdown:

1. First numbered item
* Second item (becomes numbered)
* Third item (becomes numbered)

Alpha Lists

When allow_alpha_lists is enabled, alphabetic markers (a., b., c. for lower-alpha and A., B., C. for upper-alpha) are converted to HTML lists with appropriate list-style-type CSS. This is enabled by default in Unified mode only.

apex_options opts = apex_options_for_mode(APEX_MODE_UNIFIED);
// opts.allow_alpha_lists is true by default

// To enable in other modes:
opts.allow_alpha_lists = true;

// To disable in unified mode:
opts.allow_alpha_lists = false;

Example markdown:

a. First item
b. Second item
c. Third item

This produces HTML with style="list-style-type: lower-alpha" on the <ol> tag.

Feature Flags

All feature flags control whether specific Markdown extensions are enabled:

  • enable_tables - GFM-style tables
  • enable_footnotes - Reference and inline footnotes
  • enable_definition_lists - Definition lists (term: definition)
  • enable_smart_typography - Smart quotes, dashes, ellipsis
  • enable_math - LaTeX math (inline $...$ and display $$...$$)
  • enable_critic_markup - Critic Markup for change tracking
  • enable_wiki_links - [[WikiLink]] syntax
  • enable_task_lists - GFM task lists (- [ ] and - [x])
  • enable_attributes - Inline Attribute Lists (IAL)
  • enable_callouts - Bear/Obsidian-style callouts
  • enable_marked_extensions - Marked app extensions (TOC, page breaks, etc.)

These are typically enabled/disabled based on the selected mode, but can be overridden.

Metadata Options

  • strip_metadata - Remove metadata from output (default: true)
  • enable_metadata_variables - Enable [%key] variable replacement (default: true in MMD/Kramdown/Unified modes)

File Inclusion

  • enable_file_includes - Enable file inclusion syntax (<<[file.md], {{file.md}}, /file.md)
  • max_include_depth - Maximum nesting depth for includes (default: 10)
  • base_directory - Base directory for resolving relative include paths (NULL = current directory)

Output Options

  • unsafe - Allow raw HTML in output (default: true)
  • validate_utf8 - Validate UTF-8 encoding (default: true)
  • github_pre_lang - Use GitHub-style code block language format (default: true)
  • standalone - Generate complete HTML document with <html>, <head>, <body> (default: false)
  • pretty - Pretty-print HTML with indentation (default: false)
  • stylesheet_path - Path to CSS file to link in <head> (requires standalone)
  • document_title - Title for HTML document (requires standalone)

Line Break Options

  • hardbreaks - Treat newlines as hard breaks (<br>) like GFM (default: false, true in GFM mode)
  • nobreaks - Render soft breaks as spaces instead of newlines (default: false)

Header ID Options

  • generate_header_ids - Automatically generate IDs for headers (default: true)
  • header_anchors - Generate <a> anchor tags instead of id attributes (default: false)
  • id_format - ID format: 0 = GFM (with dashes), 1 = MMD (no dashes), 2 = Kramdown

Table Options

  • relaxed_tables - Support tables without separator rows (Kramdown-style, default: true in Unified/Kramdown modes)
  • per_cell_alignment - Enable per-cell alignment markers using colons at start/end of cells (default: true in Unified mode, false in other modes)

Complete Example

#include <apex/apex.h>
#include <stdio.h>
#include <string.h>

int main() {
    const char *markdown =
        "---\n"
        "title: Test Document\n"
        "author: John Doe\n"
        "---\n"
        "\n"
        "# [%title]\n"
        "\n"
        "By [%author]\n"
        "\n"
        "This has **bold** and a [[WikiLink]].\n"
        "\n"
        "Math: $E = mc^2$\n"
        "\n"
        "1. Mixed markers\n"
        "* Second item\n"
        "\n"
        "a. Alpha list\n"
        "b. Second alpha\n";

    // Use unified mode with all features
    apex_options opts = apex_options_for_mode(APEX_MODE_UNIFIED);
    // Mixed markers and alpha lists are enabled by default in unified mode
    opts.pretty = true;
    opts.standalone = true;
    opts.document_title = "Test Document";

    // Convert to HTML
    char *html = apex_markdown_to_html(markdown, strlen(markdown), &opts);

    if (html) {
        printf("%s\n", html);
        apex_free_string(html);
        return 0;
    } else {
        fprintf(stderr, "Conversion failed\n");
        return 1;
    }
}

Plugin Catalog API

Plugin listing and installation for app UIs is declared in apex/plugins.h (also included by apexc.h).

Types

typedef struct apex_plugin_info {
    char *id;
    char *title;
    char *description;
    char *author;
    char *homepage;
    char *repo;
} apex_plugin_info;

typedef struct apex_plugin_catalog {
    apex_plugin_info *items;
    size_t count;
} apex_plugin_catalog;

Fetch available plugins

#include <apex/plugins.h>

apex_plugin_catalog *catalog = apex_plugin_catalog_fetch_default();
if (catalog) {
    for (size_t i = 0; i < catalog->count; i++) {
        apex_plugin_info *p = &catalog->items[i];
        printf("%s - %s (%s)\n", p->id, p->title ? p->title : "", p->repo);
    }
    apex_plugin_catalog_free(catalog);
}

The default catalog URL is APEX_PLUGIN_DIRECTORY_URL (ApexMarkdown/apex-plugins on GitHub).

List installed plugins

Uses the same discovery order as runtime plugin loading (project .apex/plugins, then user-global ~/.config/apex/plugins):

apex_plugin_discovery_options opts = {
    .base_directory = "/path/to/document",
    .include_project = true,
    .include_user_global = true,
};
apex_plugin_catalog *installed = apex_plugins_list_installed(&opts);

Install and uninstall

Installs into the user-global plugins directory. Requires git and curl on PATH (same as the CLI).

apex_plugin_install_options install_opts = {
    .allow_untrusted_repo = false,
    .run_post_install = true,
};
char err[512];
if (apex_plugin_install("kbd", &install_opts, err, sizeof(err)) != 0) {
    fprintf(stderr, "Install failed: %s\n", err);
}

if (apex_plugin_uninstall("kbd", err, sizeof(err)) != 0) {
    fprintf(stderr, "Uninstall failed: %s\n", err);
}

Set allow_untrusted_repo to true when installing from a Git URL or user/repo shorthand instead of a directory id.

Enable plugins during conversion

apex_options opts = apex_options_for_mode(APEX_MODE_UNIFIED);
opts.enable_plugins = true;
char *html = apex_markdown_to_html(markdown, len, &opts);

Compiling and Linking

With CMake

# In your CMakeLists.txt
find_package(apex REQUIRED)
target_link_libraries(your_app apex)

Manual Compilation

# Compile
gcc -c -I/usr/local/include your_app.c

# Link
gcc your_app.o -L/usr/local/lib -lapex -o your_app

# Run (may need library path)
DYLD_LIBRARY_PATH=/usr/local/lib ./your_app  # macOS
LD_LIBRARY_PATH=/usr/local/lib ./your_app    # Linux

Static Linking

gcc your_app.c -I/usr/local/include -L/usr/local/lib -lapex -static -o your_app

Thread Safety

Apex is thread-safe as long as:

  1. Each thread uses its own apex_options structure
  2. Document nodes are not shared between threads
  3. The conversion function can be called from multiple threads simultaneously

Example:

// Thread-safe: each thread has its own options
void *thread_func(void *arg) {
    apex_options opts = apex_options_default();
    char *html = apex_markdown_to_html(markdown, len, &opts);
    // Use html...
    apex_free_string(html);
    return NULL;
}

Memory Management

  • All strings returned by Apex must be freed with apex_free_string()
  • apex_options structures are typically stack-allocated (no freeing needed)
  • Safe to call apex_free_string(NULL)
  • Always check return values before use

Error Handling

  • Functions return NULL or empty strings on error
  • Check return values before use
  • Errors are generally due to:
    • Memory allocation failures
    • Invalid input
    • Missing dependencies

Example:

char *html = apex_markdown_to_html(markdown, len, &opts);
if (!html) {
    fprintf(stderr, "Conversion failed\n");
    return 1;
}
// Use html...
apex_free_string(html);

Performance Tips

  1. Reuse options: Create apex_options once and reuse for multiple conversions
  2. Provide accurate length: Avoid unnecessary strlen() calls
  3. Disable unused features: Turn off extensions you don't need
  4. Batch processing: Process multiple documents in parallel (thread-safe)

Example:

// Good: reuse options
apex_options opts = apex_options_default();
for (int i = 0; i < count; i++) {
    char *html = apex_markdown_to_html(docs[i], lens[i], &opts);
    // Process...
    apex_free_string(html);
}

Related

Quick Links

Clone this wiki locally