The first and most important change is that I fixed multiple bugs that caused Apex to hang on some files. In all of my testing, it no longer chokes on any file, no matter how complex. And while the rendering speed is slower than plain cmark, it’s far faster than it was previously, and slots into the middle of the pack for efficiency, with about a 70ms render time on a complex document.
The Big Stuff
Bibliography and Index Support
I’ve added bibliography and index support. Citations work with multiple types of bibliography files, and Apex handles syntax from both Pandoc and MultiMarkdown. And you can create automatic indices for your documents using mmark or TextIndex format (a cool project by Matt Gemmell with a clever syntax that I like a lot).
Given that bibliography support is the #1 reason Marked users need to use Pandoc as an external processor, building citation support into Apex — as well as Pandoc metadata and other features — means a lot of users won’t need an external processor at all. I’m in no way trying to replace or replicate Pandoc entirely; it’s a brilliant tool with a ton of capabilities that Apex does not aspire to to include. I just want to support the features that make the most sense for a universal Markdown tool.
Metadata Improvements
Almost all command line options can now also be controlled with metadata (YAML or MMD), allowing per-document control of options when operating on batches. It also means you can create a single configuration for Apex and then load it with something like apex --meta-file ~/.config/apex/main_config.yml my_document.md, and all of the options defined in main_config.yml will be applied to my_document.md.
MultiMarkdown allows you to include metadata in a document with [%key] syntax. Apex supports this, and adds on to it by allowing “transforms” — string transforms like [%title:title] or [%source:urlencode], and array transforms like [%tags:split:join(, )]. Transforms also include date parsing and transformation with strftime formatting, e.g. [%date:format(%B)]. This feature allows you to use metadata in a document, or common metadata across multiple files, to control text generation.
Advanced File Inclusion/Transclusion
I’ve added some cool features to file inclusion, including “addressing”, inspired by mmark’s syntax. This allows you to include only parts of a file, based on line number ranges ({{filename.sh[START,LEN]}}) or regular expression searches ({{filename.md[/^## Introduction/,/^## Conclusion/]}}). You can also prefix every line of an included file ({{quotes.md[139,15;prefix:> ]}}).
The Future
I don’t have any specific next steps planned as far as feature additions — I want to see if I can get enough people to start using it that I get feature requests and feedback to guide development. Let me know what you want to see. It’s one of those projects that I start dreaming about around 1am and then end up awake and adding new features to by 3am.
I plan to add Apex to Marked. It will start as an optional “beta” processor, in addition to the built-in MultiMarkdown, Kramdown, CommonMark, and Discount processors, but I’d like to eventually replace all of the processor options with Apex and just allow it to run in different compatibility modes, with the “unified” mode being the default so users don’t even have to think about which processor they need. Marked will just render all syntaxes as expected. And having Apex available as a command line tool that supports relevant parts of Marked’s special syntax will mean users can get results similar to Marked from the command line and other automations.
If you have feature requests or questions about implementation, I’d love to hear about it in a GitHub Issue, or if you’re not a GitHub user, feel free to use the forum. I’m open to any and all feature suggestions, in the interest of making this as universal as possible.
Give It a Shot
If you want to try Apex out, you can find installation instructions on the Apex project page, and the full documentation in the wiki. Apex can be run as a command line tool (which you can build from source, or just download a pre-built binary for macOS or Linux), and there’s a full C API that can be used in your own projects, including an Xcode framework.
Apex is open source and code contributions are welcome. If you want to contribute money instead, donations are always appreciated.
Boolean metadata values accept true/false, yes/no, or 1/0 (case-insensitive, downcased)
MultiMarkdown-style superscript (^text) and subscript (~text) syntax support
--[no-]sup-sub command-line option to enable/disable superscript/subscript
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.
Superscript/subscript enabled by default in unified and MultiMarkdown modes
--[no-]unsafe command-line option to control raw HTML handling
Added preprocessing for angle-bracket autolinks (<http://...>) to convert them to explicit markdown links, ensuring they work correctly with custom rendering paths.
--[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.
Underline syntax support: ~text~ now renders as <u>text</u> when there’s a closing ~ with no space before it.
Man page
--obfuscate-emails flag to hex-encode mailto links.
--[no-]wikilinks to enable/disable wiki link processing
Definition lists now work inside block quotes (e.g., > Term\n> : Definition)
Optimize file I/O by using fwrite() with known length instead of fputs()
Add markdown syntax detection in definition lists to skip parser creation for plain text
Optimize definition lists by selectively extracting only needed reference definitions instead of prepending all
Fixed
IAL markers appearing immediately after content without a blank line are now correctly parsed as separate attribute lists instead of being treated as part of the preceding paragraph. This ensures Kramdown-style IAL syntax works correctly with cmark-gfm parser.
Relaxed tables are now disabled by default for CommonMark, GFM, and MultiMarkdown modes
Header ID extraction no longer incorrectly parses metadata variables like [%title]
Relaxed tables now preserve input newline behavior in output
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
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 are now skipped during metadata extraction.
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.
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.
Improved autolink functionality to only wrap valid URLs/emails and ensured email autolinks use mailto: hrefs.
Reference-style markdown links now convert correctly in definition list terms (dt tags) and definitions (dd tags). Previously, reference-style links like [text][ref] were not being resolved when used in definition list terms or definitions, even when the reference definition was present elsewhere in the document.