Apex is a unified Markdown processor that combines the best features from CommonMark, GitHub Flavored Markdown (GFM), MultiMarkdown, Kramdown, and Marked. One processor to rule them all.
Apex Icon
There are so many variations of Markdown, extending its features in all kinds of ways. But picking one flavor means giving up the features of another flavor. So I’m building Apex with the goal of making all of the most popular features of various processors available in one tool.
Mode-specific features: Each mode enables appropriate extensions for maximum compatibility
Markdown Extensions
Tables: GitHub Flavored Markdown tables with advanced features (rowspan via ^^, colspan via empty cells/<<, captions before/after tables including Pandoc-style Table: Caption and : Caption syntax, and individual cell alignment using colons :Left, Right:, :Center:)
Table caption positioning: Control caption placement with --captions above or --captions below (default: below)
Table caption IAL: IAL attributes in table captions (e.g., : Caption {#id .class}) are extracted and applied to the table element
Relaxed tables: Support for tables without separator rows (Kramdown-style)
Headerless tables: Support for tables that start with alignment rows (separator rows) without header rows; column alignment is automatically applied
Footnotes: Three syntaxes supported (reference-style, Kramdown inline, MultiMarkdown inline)
Definition lists: Kramdown-style definition lists with Markdown content support
Task lists: GitHub-style checkboxes (- [ ] and - [x])
Strikethrough: ~~text~~ syntax from GFM
Smart typography: Automatic conversion of quotes, dashes, ellipses, and more
Math support: LaTeX math expressions with $...$ (inline) and $$...$$ (display)
Wiki links: [[Page Name]], [[Page Name|Display Text]], and [[Page Name#Section]] syntax with configurable link targets via --wikilink-space and --wikilink-extension
Callouts: Bear/Obsidian-style callouts with collapsible support (> [!NOTE], > [!WARNING], etc.)
GitHub emoji: 350+ emoji support (:rocket:, :heart:, etc.)
Document Features
Metadata blocks: YAML front matter, MultiMarkdown metadata, and Pandoc title blocks
Metadata variables: Insert metadata values with [%key] syntax
Metadata transforms: Transform metadata values with [%key:transform] syntax - supports case conversion, string manipulation, regex replacement, date formatting, and more. See Metadata Transforms for complete documentation
Metadata control of options: Control command-line options via metadata - set boolean flags (indices: false, wikilinks: true) and string options (bibliography: refs.bib, title: My Document, wikilink-space: dash, wikilink-extension: html) directly in document metadata for per-document configuration
Table of Contents: Automatic TOC generation with depth control using HTML (<!--TOC-->), MMD ({{TOC}} / {{TOC:2-4}}), and Kramdown {:toc} markers. Headings marked with {:.no_toc} are excluded from the generated TOC.
File includes: Three syntaxes (Marked <<[file], MultiMarkdown {{file}}, iA Writer /file), with support for address ranges and wildcard/glob patterns such as {{file.*}}, {{*.md}}, and {{c?de.py}}.
Markdown combiner (--combine): Concatenate one or more Markdown files into a single Markdown stream, expanding all include syntaxes. When a SUMMARY.md file is provided, Apex treats it as a GitBook-style index and combines the linked files in order—perfect for building books, multi-file indices, and shared tables of contents that can then be piped back into Apex for final rendering.
MultiMarkdown merge (--mmd-merge): Read one or more mmd_merge-style index files and stitch their referenced documents into a single Markdown stream. Each non-empty, non-comment line specifies a file to include; indentation with tabs or four-space groups shifts all headings in that file down by one level per indent, mirroring the original mmd_merge.pl behavior. Output is raw Markdown that can be piped into Apex (e.g., apex --mmd-merge index.txt | apex --mode mmd).
CSV/TSV support: Automatic table conversion from CSV and TSV files
Inline Attribute Lists (IAL): Kramdown-style attributes {: #id .class} and Pandoc-style attributes {#id .class} - both formats work in all contexts (block-level, inline, paragraphs, headings, table captions)
Bracketed spans: Convert [text]{IAL} syntax to HTML span elements with attributes, enabled by default in unified mode
Fenced divs: Pandoc-style fenced divs ::::: {#id .class} ... ::::: for creating custom block containers, enabled by default in unified mode
Image IAL support: Inline and reference-style images support IAL syntax with automatic width/height conversion (percentages and non-integer/non-px values convert to style attributes, Xpx values convert to integer width/height attributes, bare integers remain as width/height attributes)
Special markers: Page breaks (<!--BREAK-->), autoscroll pauses (<!--PAUSE:N-->), end-of-block markers
Custom styling: Link external CSS files in standalone mode
Pretty-print: Formatted HTML with proper indentation for readability
Header ID generation: Automatic or manual header IDs with multiple format options (GFM, MMD, Kramdown)
Header anchors: Option to generate <a> anchor tags instead of header IDs
ARIA accessibility: Add ARIA labels and accessibility attributes (--aria) for better screen reader support, including aria-label on TOC navigation, role attributes on figures and tables, and aria-describedby linking tables to their captions
Advanced Features
Hard breaks: Option to treat newlines as hard line breaks
Feature toggles: Granular control to enable/disable specific features (tables, footnotes, math, smart typography, etc.)
Unsafe HTML: Option to allow or block raw HTML in documents
Autolinks: Automatic URL detection and linking
Superscript/Subscript: Support for ^superscript^ and ~subscript~ syntax
Extensibility and Plugins
Apex supports a flexible plugin system that lets you add new syntax and post-processing features in any language while keeping the core parser stable and fast. Plugins are disabled by default so there is no performance impact unless you opt in. Enable them per run with --plugins, or per document with a plugins: true (or enable-plugins: true) key in your metadata.
You can manage plugins from the CLI:
Install plugins with --install-plugin:
From the central directory using an ID: --install-plugin kbd
Directly from a Git URL or GitHub shorthand: --install-plugin https://github.com/user/repo.git or --install-plugin user/repo
Uninstall a local plugin with --uninstall-plugin ID.
See installed and available plugins with --list-plugins.
When installing from a direct Git URL or GitHub repo name, Apex will prompt with a security warning before cloning, since plugins execute unverified code.
For a complete guide to writing, installing, and publishing plugins, see the Plugins page in the Apex Wiki.
Installation
Homebrew (macOS/Linux)
brew tap ttscoff/thelab
brew install ttscoff/thelab/apex
Building from Source
git clone https://github.com/ApexMarkdown/apex.git
cd apex
git submodule update --init--recursive
make
The apex binary will be in the build/ directory.
To install the built binary and libraries system-wide:
make install
Note: The default make command runs both cmake -S . -B build (to configure the project) and cmake --build build (to compile). If you prefer to run cmake commands directly, you can use those instead.
Pre-built Binaries
Download pre-built binaries from the latest release. Binaries are available for:
macOS (Universal binary for arm64 and x86_64)
Linux (x86_64 and arm64)
Basic Usage
Command Line
# Process a markdown file
apex input.md
# Output to a file
apex input.md -o output.html
# Generate standalone HTML document
apex input.md --standalone--title"My Document"# Pretty-print HTML output
apex input.md --pretty
Processing Modes
Apex supports multiple compatibility modes:
--mode commonmark - Pure CommonMark specification
--mode gfm - GitHub Flavored Markdown
--mode mmd or --mode multimarkdown - MultiMarkdown compatibility
--mode kramdown - Kramdown compatibility
--mode unified - All features enabled (default)
# Use GFM mode
apex input.md --mode gfm
# Use Kramdown mode with relaxed tables
apex input.md --mode kramdown
Common Options
--pretty - Pretty-print HTML with indentation
--standalone - Generate complete HTML document with <html>, <head>, <body>
--style FILE - Link to CSS file in document head (requires --standalone)
--title TITLE - Document title (requires --standalone)
--relaxed-tables - Enable relaxed table parsing (default in unified/kramdown modes)
--captions POSITION - Table caption position: above or below (default: below)
--id-format FORMAT - Header ID format: gfm, mmd, or kramdown
--no-ids - Disable automatic header ID generation
--header-anchors - Generate <a> anchor tags instead of header IDs
--aria - Add ARIA labels and accessibility attributes to HTML output
--bibliography FILE - Bibliography file (BibTeX, CSL JSON, or CSL YAML) - can be used multiple times
--csl FILE - Citation style file (CSL format)
--link-citations - Link citations to bibliography entries
--indices - Enable index processing (mmark and TextIndex syntax)
--no-indices - Disable index processing
--no-index - Suppress index generation (markers still created)
--wikilinks - Enable wiki link syntax [[Page]], [[Page|Display]], and [[Page#Section]]
--wikilink-space MODE - Control how spaces in wiki link page names are converted (dash, none, underscore, space; default: dash)
--wikilink-extension EXT - File extension to append to wiki link URLs (e.g. html, md)
--divs / --no-divs - Enable/disable Pandoc fenced divs syntax (enabled by default in unified mode)
--spans / --no-spans - Enable/disable bracketed spans [text]{IAL} syntax (enabled by default in unified mode)
All Options
Apex Markdown Processor v0.1.42
One Markdown processor to rule them all
Project homepage: https://github.com/ApexMarkdown/apex
Usage: build/apex [options] [file]
build/apex --combine [files...]
build/apex --mmd-merge [index files...]
Options:
--accept Accept all Critic Markup changes (apply edits)
--[no-]alpha-lists Support alpha list markers (a., b., c. and A., B., C.)
--[no-]autolink Enable autolinking of URLs and email addresses
--base-dir DIR Base directory for resolving relative paths (for images, includes, wiki links)
--bibliography FILE Bibliography file (BibTeX, CSL JSON, or CSL YAML) - can be used multiple times
--captions POSITION Table caption position: above or below (default: below)
--combine Concatenate Markdown files (expanding includes) into a single Markdown stream
When a SUMMARY.md file is provided, treat it as a GitBook index and combine
the linked files in order. Output is raw Markdown suitable for piping back into Apex.
--csl FILE Citation style file (CSL format)
--css FILE, --style FILE Link to CSS file in document head (requires --standalone, overrides CSS metadata)
--embed-css Embed CSS file contents into a <style> tag in the document head (used with --css)
--embed-images Embed local images as base64 data URLs in HTML output
--hardbreaks Treat newlines as hard breaks
--header-anchors Generate <a> anchor tags instead of header IDs
-h, --help Show this help message
--id-format FORMAT Header ID format: gfm (default), mmd, or kramdown
(modes auto-set format; use this to override in unified mode)
--[no-]includes Enable file inclusion (enabled by default in unified mode)
--indices Enable index processing (mmark and TextIndex syntax)
--install-plugin ID Install plugin by id from directory, or by Git URL/GitHub shorthand (user/repo)
--link-citations Link citations to bibliography entries
--list-plugins List installed plugins and available plugins from the remote directory
--uninstall-plugin ID Uninstall plugin by id
--meta KEY=VALUE Set metadata key-value pair (can be used multiple times, supports quotes and comma-separated pairs)
--meta-file FILE Load metadata from external file (YAML, MMD, or Pandoc format)
--[no-]mixed-lists Allow mixed list markers at same level (inherit type from first item)
--mmd-merge Merge files from one or more mmd_merge-style index files into a single Markdown stream
Index files list document parts line-by-line; indentation controls header level shifting.
-m, --mode MODE Processor mode: commonmark, gfm, mmd, kramdown, unified (default)
--no-bibliography Suppress bibliography output
--no-footnotes Disable footnote support
--no-ids Disable automatic header ID generation
--no-indices Disable index processing
--no-index Suppress index generation (markers still created)
--no-math Disable math support
--aria Add ARIA labels and accessibility attributes to HTML output
--no-plugins Disable external/plugin processing
--no-relaxed-tables Disable relaxed table parsing
--no-smart Disable smart typography
--no-sup-sub Disable superscript/subscript syntax
--[no-]divs Enable or disable Pandoc fenced divs (Unified mode only)
--[no-]spans Enable or disable bracketed spans [text]{IAL} (Pandoc-style, enabled by default in unified mode)
--no-tables Disable table support
--no-transforms Disable metadata variable transforms
--no-unsafe Disable raw HTML in output
--no-wikilinks Disable wiki link syntax
--obfuscate-emails Obfuscate email links/text using HTML entities
-o, --output FILE Write output to FILE instead of stdout
--plugins Enable external/plugin processing
--pretty Pretty-print HTML with indentation and whitespace
--reject Reject all Critic Markup changes (revert edits)
--[no-]relaxed-tables Enable or disable relaxed table parsing (no separator rows required)
--script VALUE Inject <script> tags before </body> (standalone) or at end of HTML (snippet).
VALUE can be a path, URL, or shorthand (mermaid, mathjax, katex). Can be used multiple times or as a comma-separated list.
--show-tooltips Show tooltips on citations
-s, --standalone Generate complete HTML document (with <html>, <head>, <body>)
--[no-]sup-sub Enable or disable MultiMarkdown-style superscript (^text^) and subscript (~text~) syntax
--title TITLE Document title (requires --standalone, default: "Document")
--[no-]transforms Enable or disable metadata variable transforms [%key:transform]
--[no-]unsafe Allow or disallow raw HTML in output
-v, --version Show version information
--[no-]wikilinks Enable or disable wiki link syntax [[PageName]]
--wikilink-space MODE Space replacement for wiki links: dash, none, underscore, space (default: dash)
--wikilink-extension EXT File extension to append to wiki links (e.g., html, md)
If no file is specified, reads from stdin.
Per-Document Configuration via Metadata
Most command-line options can be controlled via document metadata, allowing different files to be processed with different settings when processing batches. Boolean options accept true/false, yes/no, or 1/0 (case-insensitive). String options use the value directly.
Example:
---indices:falsewikilinks:truebibliography:references.bibtitle:My Research Paperpretty:truestandalone:true---
This allows you to process multiple files with apex *.md and have each file use its own configuration. You can also use --meta-file to specify a shared configuration file that applies to all processed files.
Reference image attribute expansion now converts IAL attributes (ID, classes) to key=value format for compatibility with inline image parsing
Test suite refactored: split test_runner.c into multiple files (test_basic.c, test_extensions.c, test_helpers.c, test_ial.c, test_links.c, test_metadata.c, test_output.c, test_tables.c) for better organization
Test fixtures reorganized: moved all .md test files from tests/ to tests/fixtures/ with subdirectories (basic/, demos/, extensions/, ial/, images/, output/, tables/)
New
Support for Pandoc-style table captions using : Caption syntax (in addition to existing [Caption] and Table: Caption formats)
IAL attributes in table captions are now extracted and applied to the table element (e.g., : Caption {#id .class} applies id and class to the table)
Support for Pandoc-style IAL syntax without colon prefix ({#id .class} in addition to Kramdown {:#id .class} format)
Support Pandoc-style IAL syntax ({#id .class}) in addition to Kramdown-style ({: #id .class}) for all IAL contexts including block-level, inline, and paragraph IALs
Extract_ial_from_text function now recognizes both {: and {# or {. formats when extracting IALs from text
Extract_ial_from_paragraph function now accepts Pandoc-style IALs for pure IAL paragraphs
Process_span_ial_in_container function now processes Pandoc-style IALs for inline elements like links, images, and emphasis
Is_ial_line function now detects Pandoc-style IAL-only lines in addition to Kramdown format
Support Pandoc-style IAL syntax ({#id .class}) in addition to Kramdown-style ({: #id .class}) for all IAL contexts including block-level elements, paragraphs, inline elements, and headings
Add support for Pandoc fenced divs syntax (::::: {#id .class} … :::::) in unified mode, enabled by default
Add –divs and –no-divs command-line flags to control fenced divs processing
Add comprehensive test suite for Pandoc fenced divs covering basic divs, nested divs, attributes, and edge cases
Add bracketed spans feature that converts [text]{IAL} syntax to HTML span elements with attributes, enabled by default in unified mode
Add –spans and –no-spans command-line flags to enable/disable bracketed spans in other modes
Bracketed spans support all IAL attribute types (IDs, classes, key-value pairs) and process markdown inside spans
Reference link definitions take precedence over bracketed spans - if [text] matches a reference link, it remains a link
Add comprehensive test suite for bracketed spans including reference link precedence, nested brackets, and markdown processing
Add bracketed spans examples and documentation
Add automatic width/height attribute conversion: percentages and non-integer/non-px values convert to style attributes, Xpx values convert to integer width/height attributes (strips px suffix), bare integers remain as width/height attributes
Add support for Pandoc/Kramdown IAL syntax on images: inline images support IAL after closing paren like {#id .class width=50%}
Add support for IAL syntax after titles in reference image definitions: [ref]: url “title” {#id .class width=50%}
Add support for Pandoc-style IAL with space prefix: { width=50% } syntax works for images
Add comprehensive test suite for width/height conversion covering percentages, pixels, integers, mixed cases, and edge cases like decimals and viewport units
Reference image definitions now preserve and include title attributes from definitions like [ref]: url “title” {#id}
Improved
Table caption preprocessing now converts : Caption format to [Caption] format before definition list processing to avoid conflicts
HTML renderer now extracts and injects IAL attributes from table captions while excluding internal attributes like data-caption
IAL parsing automatically detects format and adjusts content offset accordingly (2 chars for {: format, 1 char for {# or {. format)
HTML markdown extension now uses CMARK_OPT_UNSAFE to allow raw HTML including nested divs in processed content
Width/height conversion properly merges with existing style attributes when both are present
IAL detection now properly handles whitespace before IAL syntax for both inline and reference images
Reference image expansion now correctly skips IAL even when closing paren is not found
Fixed
Table caption test assertions now correctly match table tags with attributes by using <table instead of <table>
Extract_ial_from_paragraph now allows newline character after closing brace in IAL syntax
List items with key:value format (e.g., “- Foo: Bar”) are no longer incorrectly parsed as MMD metadata in unified mode
Fenced divs now add markdown=”1” attribute so content inside divs is properly parsed as markdown
HTML markdown extension now preserves all attributes (id, class, custom attributes) when processing divs with markdown=”1”
HTML markdown extension now recursively processes nested divs with markdown=”1” attributes
HTML markdown extension now adds newline after closing div tags to ensure following markdown headers and content are parsed correctly
Bracketed spans now correctly handle nested brackets by matching outer brackets instead of first closing bracket
Remove test assertions checking for markdown attribute on bracketed spans which is correctly removed by html_markdown extension
IAL syntax with spaces (e.g., { width=50% }) now correctly detected and processed for images
IAL syntax is now properly stripped from output even when parsing fails, preventing raw IAL from appearing in HTML
Reference image definitions with IAL after URL (no title) now correctly detected and processed
Test string length issues: replaced hardcoded lengths with strlen() calls to ensure full IAL syntax is processed in width/height conversion tests
Removed unused style_attr_index variable to eliminate compiler warning
ALD references can now be combined with additional attributes in the same IAL (e.g., {:id .class3} where id is an ALD reference and .class3 is an additional class)
Improved
When merging ALD attributes with additional attributes, duplicate key-value pairs are now replaced instead of duplicated (e.g., if ALD defines rel=”x” and IAL includes rel=”y”, the result is rel=”y”)
Classes from additional attributes are appended to ALD classes, and IDs in IALs override ALD IDs when specified
Enhanced merge_attributes function to properly handle attribute key conflicts by replacing existing values rather than creating duplicates
Inline Attribute Lists (IALs) can now appear immediately after inline elements within paragraphs, not just at the end of paragraphs
IALs can be applied to strong (bold), emphasis (italic), and code elements in addition to links and images
IALs now work with nested inline elements, allowing attributes to be applied to italic text inside bold text and similar nested structures
Added comprehensive test suite for inline IAL functionality covering links, strong, emphasis, code, multiple IALs, and edge cases
Added IAL demo markdown file (tests/ial_demo.md) demonstrating all supported IAL features
Added script (tests/generate_ial_demo.sh) to automatically generate an HTML file with interactive attribute inspection tooltips
Fixed
IALs (Inline Attribute Lists) are now correctly applied to the intended link element when multiple links in a document share the same URL
IALs are now correctly applied to the intended element when multiple elements share the same URL or content, using separate element counters for each inline element type
Block-level HTML elements now correctly include a space between the tag name and attributes when IAL attributes are injected
Table captions now default to below the table instead of above
Tfoot row detection in AST now separates the logic for marking the === row itself versus rows that come after it, improving accuracy of tfoot section identification.
Disallow using –combine and –mmd-merge together to avoid ambiguous multi-file behavior
Update CSV include and inline table handling so both share the same CSV-to-table conversion and alignment behavior
New
Added –captions option to control caption position (above or below)
Added default CSS styling for figcaption elements in standalone output (centered, bold, 0.8em)
Added CSS styling for table figures to align captions with tables (fit-content width)
Support for tables that start with alignment rows (separator rows) without header rows. Column alignment specified in the separator row is automatically applied to all data columns.
Support for individual cell alignment in tables using colons, similar to Jekyll Spaceship. Cells can be aligned independently with :Text (left), Text: (right), or :Text: (center). Colons are removed from output and alignment is applied via CSS text-align styles.
Support per-cell alignment markers inside table cells
Support multiline table cells using trailing backslash markers
Support header and footer colspans based on empty cells
Add –combine CLI mode to concatenate Markdown files with include expansion and GitBook-style SUMMARY.md index support.
Add –mmd-merge CLI mode to merge MultiMarkdown index files into a single Markdown stream
Support indentation-based header level shifting when merging mmd_merge index entries
Support inline CSV/TSV tables using ```table fenced blocks with automatic CSV/TSV delimiter detection
Support markers that convert following CSV/TSV lines into Markdown tables until a blank line
Add –aria command-line option to enable ARIA labels and accessibility attributes in HTML output
Add aria-label=”Table of contents” to TOC navigation elements when –aria is enabled
Add role=”figure” to figure elements when –aria is enabled
Add role=”table” to table elements when –aria is enabled
Generate id attributes for figcaption elements in table figures when –aria is enabled
Add aria-describedby attributes linking tables to their captions when –aria is enabled
Improved
Removed unused variables to eliminate compiler warnings
Empty thead sections from headerless tables are now removed from HTML output instead of rendering empty header cells.
Table row mapping now better handles the relationship between HTML row indices and AST row indices, accounting for separator rows that are removed from HTML output.
Added safeguards to prevent rows that should be in tbody from being skipped, including protection for the first few rows (header and first two data rows) when a === separator is present.
Make table captions positionable above or below tables
Center and style figcaptions in standalone HTML output
Support optional alignment keyword rows (left, right, center, auto) and headless tables for both included CSV files and inline CSV/TSV data
Preserve ```table fences without commas or tabs by leaving them as code blocks so users can show literal CSV/TSV without conversion
Fixed
Removed unused variable ‘row_idx’ from advanced_tables.c to eliminate compiler warnings.
Rows before the === separator are now correctly placed in tbody instead of being incorrectly placed in tfoot or skipped entirely. The fix includes HTML position verification to ensure rows that appear before the === row in the rendered HTML are always in tbody, regardless of AST marking.
Prevent === separator rows from appearing as table content
Ensure footer rows render in tfoot without losing body rows
Preserve legitimate empty cells such as missing Q4 values
Apply ^^ rowspans correctly for all table sections
Apply ^^ rowspans correctly across table sections without leaking into unrelated rows
Support footer colspans so footer cells can span multiple columns like headers and body rows
Preserve legitimate empty table cells that are not part of colspans or rowspans
KaTeX auto-render now properly configures delimiters and manually renders math spans to prevent plain text from appearing after rendered equations
Relaxed table header conversion now only runs when relaxed_tables option is enabled
HTML document wrapping now strips existing </body></html> tags to prevent duplicates when content already contains them
KaTeX auto-render now properly configures delimiters and manually renders math spans to prevent plain text from appearing after rendered equations
Relaxed table header conversion now only runs when relaxed_tables option is enabled
HTML document wrapping now strips existing </body></html> tags to prevent duplicates when content already contains them
Table captions appearing after their tables now correctly link via aria-describedby attributes
Table post-processing now tracks all cells (including removed ones) to correctly map HTML column positions to original AST column indices, ensuring attributes are applied to the correct cells.
Added content-based detection for ^^ marker cells during HTML post-processing to ensure they are properly removed even when attribute matching fails.
New
Added content verification to prevent false matches when cells are covered by rowspans
Added tracking of previous cell’s colspan to detect and remove empty cells after colspan
Added detection and removal of === marker rows in tfoot sections
Improved
Rowspan cell tracking now uses a per-column active cell approach (inspired by Jekyll Spaceship) for more reliable rowspan calculation across complex table structures.
Cell matching now uses position-based fallback to previous row when current row match fails
Row mapping accounts for rowspan coverage to correctly identify visible columns
Tfoot row detection now uses AST row indices instead of HTML row indices for accuracy
Fixed
Table rowspan rendering now correctly handles rows where most cells use ^^ markers. Previously, cells like “Beta” and “Gamma” in rows with multiple ^^ cells would be missing or appear in the wrong position. The fix includes proper mapping between HTML row indices and AST row indices, accounting for separator rows that are removed from HTML output.
Missing cells after colspan (e.g., “92.00” cell was missing when “Absent” had colspan=”2”)
Rowspan not applying correctly when HTML row mapping was off by one
Footer alignment rows (=== markers) appearing in output instead of being removed
Empty cells after colspan not being removed from rendered HTML
Image attributes are mode-dependent: work in Unified and MultiMarkdown modes only
URL encoding is mode-dependent: works in Unified, MultiMarkdown, and Kramdown modes
Improved caption detection to check all table rows for caption markers, not just the last row, to handle cases where captions come after tfoot rows.
New
Support for MultiMarkdown-style image attributes in unified and MultiMarkdown modes
Inline image attributes:
Reference-style image attributes: ![][ref] with [ref]: url width=300
Automatic URL encoding for links with spaces in unified, MultiMarkdown, and Kramdown modes
URLs with spaces are automatically percent-encoded (e.g., “path with spaces.png” becomes “path%20with%20spaces.png”)
Added support for MultiMarkdown-style image attributes in reference-style images. Reference definitions can now include attributes: [img1]: image.png width=300 style=”float:left”
Added support for inline image attributes:
Added automatic URL encoding for all link URLs (images and regular links). URLs with spaces are automatically percent-encoded (e.g., “path to/image.png” becomes “path%20to/image.png”)
Added detection and removal of table alignment separator rows that were incorrectly being rendered as table rows.
Added test cases for table captions appearing before and after tables.
Added support for tfoot sections in tables using === row markers. Rows containing === markers are now placed in <tfoot> sections, and all subsequent rows after the first === row are also placed in tfoot.
Added comprehensive table feature tests that validate rowspan,
Improved
Improved attribute injection in HTML renderer to correctly place attributes before closing > or /> in img and link tags
Enhanced URL parsing to distinguish between spaces within URLs vs spaces before attributes using forward-scanning pattern detection
Self-closing img tags now consistently use “ />” (space before slash) when attributes are injected, matching the format used by cmark-gfm for img tags without injected attributes
Rowspan and colspan attribute handling now properly appends to existing attributes instead of replacing them, allowing multiple attributes to coexist on table cells.
Alignment rows (rows containing only ‘’ characters) are now detected and marked for removal, preventing them from appearing in HTML output.
Fixed
Fixed bug where image prefix “![” was incorrectly removed during preprocessing of expanded reference-style images
URL encoding now only encodes unsafe characters (space, control chars, non-ASCII). Valid URL characters like /, :, ?, #, ~, etc. are preserved and no longer incorrectly encoded.
Titles in links and images are now correctly detected and excluded from URL encoding. Supports quoted titles (“title”, ‘title’) and parentheses titles ((title)). URLs with parentheses (like Wikipedia links) are correctly distinguished from titles based on whether a space precedes the opening parenthesis.
Reference-style images with attributes now render correctly. Reference definitions with image attributes are removed from output, while those without attributes are preserved (with URL encoding) so cmark can resolve the references.
Spacing between attributes in HTML output. Attributes injected into img and link tags now have proper spacing, preventing malformed HTML like alt=”text”width=”100”.
Table attributes now render correctly with proper spacing. Fixed missing space in table tag when id attribute immediately follows (e.g., <tableid=”…” now renders as <table id=”…”).
Rowspan and colspan injection now works correctly in all cases. Fixed bug where table tracking variables weren’t set when fixing missing space in table tag (e.g., <tableid=”…”), causing row and cell processing to be skipped. Table tracking is now properly initialized even when correcting tag spacing.
Captions after tables were not being detected when tables had IAL attributes, as IAL processing replaced the caption data stored in user_data. Added fallback logic to check for caption paragraphs directly in the AST when user_data lookup fails.
Rows containing only === markers are now properly skipped entirely rather than rendering as empty cells in tfoot sections.
Caption paragraphs before tables are now properly removed,
Resolve CMake error when building framework where file(GLOB) returns multiple dylib files, causing semicolon-concatenated paths in file(COPY) command. Now extracts first file from glob result before copying.
Homebrew installation now correctly links to system libyaml instead of hardcoded CI path
Update Homebrew formula to install from a precompiled macOS universal binary instead of building from source with cmake.
Allow –install-plugin to accept a Git URL or GitHub shorthand (user/repo) in addition to directory IDs when installing plugins.
Improved
Simplify Homebrew installation so users no longer need cmake or Xcode build tools to install apex.
Add an interactive security confirmation when installing plugins from a direct Git URL or GitHub repo name, reminding users that plugins execute unverified code.
Add citation processing with support for Pandoc, MultiMarkdown, and mmark syntaxes
Add bibliography loading from BibTeX, CSL JSON, and CSL YAML formats
Add –bibliography CLI option to specify bibliography files (can be used multiple times)
Add –csl CLI option to specify citation style file
Add –no-bibliography CLI option to suppress bibliography output
Add –link-citations CLI option to link citations to bibliography entries
Add –show-tooltips CLI option for citation tooltips
Add bibliography generation and insertion at marker
Add support for bibliography specified in document metadata
Improved
Only process citations when bibliography is actually provided for better performance
Fixed
Raw HTML tags and comments are now preserved in definition lists by default in unified mode. Previously, HTML content in definition list definitions was being replaced with “raw HTML omitted” even when using –unsafe or in unified mode.
Unified mode now explicitly sets unsafe=true by default to ensure raw HTML is allowed.
Prevent autolinking of @ symbols in citation syntax (e.g., [@key])
Handle HTML comments in autolinker to preserve citation placeholders
Add –embed-images flag to embed local images as base64 data URLs in HTML output
Add –base-dir flag to set base directory for resolving relative paths (images, includes, wiki links)
Add automatic base directory detection from input file directory when reading from file
Add base64 encoding utility for image data
Add MIME type detection from file extensions (supports jpg, png, gif, webp, svg, bmp, ico)
Add image embedding function that processes HTML and replaces local image src attributes with data URLs
Add test suite for image embedding functionality
Improved
Wiki link scanner now processes all links in a text node in a single pass instead of recursively processing one at a time, significantly improving performance for documents with multiple wiki links per text node.
Added early-exit optimization to skip wiki link AST traversal entirely when no wiki link markers are present in the document.
Improve error handling in transform execution to return original value instead of NULL on failure
Add comprehensive test coverage for all transforms including edge cases
Relative path resolution for images now uses base_directory option
Base directory is automatically set from input file location when not specified
Fixed
Fix bracket handling in regex patterns - properly match closing brackets in [%…] syntax when patterns contain brackets
Fix YAML metadata parsing to strip quotes from quoted string values
Raw HTML tags and comments are now preserved in definition lists by default in unified mode. Previously, HTML content in definition list definitions was being replaced with “raw HTML omitted” even when using –unsafe or in unified mode.
Unified mode now explicitly sets unsafe=true by default to ensure raw HTML is allowed.
Added man page generation and installation support. Man pages can be generated from Markdown source using pandoc or go-md2man, with pre-generated man pages included in the repository as fallback. CMake build system now handles man page installation, and Homebrew formula installs the man page.
Added comprehensive test suite for MMD 6 features including multi-line setext headers and link/image titles with different quote styles (single quotes, double quotes, parentheses). Tests verify these features work in both MultiMarkdown and unified modes.
Added build-test man_page_copy target for man page installation.
Added –obfuscate-emails flag to hex-encode mailto links.
IMPROVED
Superscript processing now stops at sentence terminators (. , ; : ! ?) instead of including them in the superscript content. This prevents punctuation from being incorrectly included in superscripts.
Enhanced subscript and underline detection logic. The processor now correctly differentiates between subscript (tildes within a word, e.g., H~2~O) and underline (tildes at word boundaries, e.g., ~text~) by checking if tildes are within alphanumeric words or at word boundaries.
Expanded test coverage for superscript, subscript, underline, strikethrough, and highlight features with additional edge case tests.
HTML comments now replaced with “raw HTML omitted” in CommonMark and GFM modes by default
Added enable_sup_sub flag to apex_options struct
Updated mode configurations to enable sup/sub in appropriate modes
Added sup_sub.c to CMakeLists.txt build configuration
Removed unused variables to resolve compiler warnings
Tag filter (GFM security feature) now only applies in GFM mode, not Unified mode, allowing raw HTML and autolinks in Unified mode as intended.
Autolink extension registration now respects the enable_autolink option flag.
NEW
Added MultiMarkdown-style superscript (^text^) and subscript (~text~) syntax support
Added –[no-]sup-sub command-line option to enable/disable superscript/subscript
Superscript/subscript enabled by default in unified and MultiMarkdown modes
Created sup_sub extension (sup_sub.c and sup_sub.h) for processing ^ and ~ syntax
Added –[no-]unsafe command-line option to control raw HTML handling
Added test_sup_sub() function with 13 tests covering superscript and
Added test_mixed_lists() function with 10 tests covering mixed list
Added test_unsafe_mode() function with 8 tests covering raw HTML
Added preprocessing for angle-bracket autolinks (http://...) to convert them to explicit markdown links, ensuring they work correctly with custom rendering paths.
Added –[no-]autolink CLI option to control automatic linking of URLs and email addresses. Autolinking is enabled by default in GFM, MultiMarkdown, Kramdown, and unified modes, and disabled in CommonMark mode.
Added enable_autolink field to apex_options structure to control autolink behavior programmatically.
Added underline syntax support: ~text~ now renders as text when there’s a closing ~ with no space before it.
IMPROVED
Test suite now includes 36 additional tests, increasing total test
Autolink preprocessing now skips processing inside code spans (...) and code blocks (...), preventing URLs from being converted to links when they appear in code examples.
Metadata replacement retains HTML edge-case handling and properly cleans up intermediate buffers.
FIXED
Unified mode now correctly enables mixed list markers and alpha lists by default when no –mode is specified
^ marker now properly separates lists by creating a paragraph break instead of just blank lines
Empty paragraphs created by ^ marker are now removed from final HTML output
Superscript and subscript processing now skips ^ and ~ characters
Superscript processing now skips ^ when part of footnote reference
Subscript processing now skips ~ when part of critic markup patterns
Setext headers are no longer broken when followed by highlight syntax (==text==). Highlight processing now stops at line breaks to prevent interference with header parsing.
Metadata parser no longer incorrectly treats URLs and angle-bracket autolinks as metadata. Lines containing < or URLs (http://, https://, mailto:) are now skipped during metadata extraction.
Superscript/subscript processor now correctly differentiates between ~text~ (underline), ~word (subscript), and ~~text~~ (strikethrough). Double-tilde sequences are skipped so strikethrough extension can handle them.
Subscript processing now stops at sentence terminators (. , ; : ! ?) instead of including them in the subscript content.
Metadata variable replacement now runs before autolinking so [%key] values containing URLs are turned into links when autolinking is enabled.
MMD metadata parsing no longer incorrectly rejects entries with URL values; only URL-like keys or ‘<’ characters in keys are rejected, allowing “URL: https://example.com” as valid metadata.
Headers starting with # are now correctly recognized instead of being treated as autolinks. The autolink preprocessor now skips # at the start of a line when followed by whitespace.
Math processor now validates that \(...\) sequences contain actual math content (letters, numbers, or operators) before processing them. This prevents false positives like \(%\) from being treated as math when they only contain special characters.
Core Features:
- Initial release of Apex unified Markdown processor
- Based on cmark-gfm for CommonMark + GFM support
- Support for 5 processor modes: CommonMark, GFM, MultiMarkdown, Kramdown, Unified
Metadata:
- YAML front matter parsing
- MultiMarkdown metadata format
- Pandoc title block format
- Metadata variable replacement with [%key] syntax
Build System:
- CMake build system for cross-platform support
- Builds shared library, static library, CLI binary, and macOS framework
- Clean compilation on macOS with Apple Clang
CLI Tool:
- apex command-line binary
- Support for all processor modes via --mode flag
- Stdin/stdout support for Unix pipes
- Comprehensive help and version information
Integration:
- Objective-C wrapper (NSString+Apex) for Marked integration
- macOS framework with proper exports
- Detailed integration documentation and examples
Testing:
- Automated test suite with 31 tests
- 90% pass rate across all feature areas
- Manual testing validated
Documentation:
- Comprehensive user guide
- Complete API reference
- Architecture documentation
- Integration guides
- Code examples
Known Issues
Critic Markup substitutions have edge cases with certain inputs