Skip to content

Markdown

phil/markup

Reader\Markdown\MarkdownReader reads Markdown into a UJO document. It never throws: a construct that is not well formed stays text. The document is normalized by phil/ujo before it is returned, so its inline HTML is in the canonical form of the inline tools — <b>, <i>, <s class="cdx-strikethrough">, <code class="inline-code">.

$reader = new MarkdownReader();                  // raw HTML escaped
$reader = new MarkdownReader(rawHtml: true);     // raw HTML kept, for a purifier at the level `all`
$reading = $reader->read($source);               // Reading: document and metadata

A second argument replaces the Normalizer; the default one registers every type of phil/ujo and raw. A third gives a MarkdownDialect, the syntax a dialect adds or reads differently: see Extension points.

Blocks

MarkdownUJO
A paragraphparagraph; split around the images it holds, each an image
# Heading, Heading over === or ---header, level 1 to 6, with an anchor tune
- item, + item, * item, 1. item, 1) itemlist, unordered or ordered with its start; nested lists are nested items
- [ ] task, - [x] done, on every itemlist, style checklist, each item checked
> quotequote, its blocks folded into its text
``` or ~~~ fence, four spaces of indentationcode, the first word of the fence its language
***, ---, ___delimiter
A table of GitHubtable, withHeadings; the alignment of columns is not kept
Term then : DefinitiondefinitionList; consecutive lists are one
[^label]: texta footnote at the end of the document, numbered by first reference
[label]: address "title"a reference for links and images, its title the caption of an image; no block
[TOC] alone on its linetoc
::: note Title, ::: warning, ::: info, ::: wide, ::: {.class #id} … :::note, warning, alert, wide, container (see below)
{{note …}} and the other {{ }} blocks, a span alone on its linesee Blocks in braces
\page, \pagebreak, \column, \columnbreakpageBreak, columnBreak; see Pages and columns
HTML blockraw, or a paragraph of escaped text
Front matter, YAML between two --- lines at the startReading::$metadata

A container — a quote, a list item, a footnote — holds only inline content in UJO: its paragraphs are joined by line breaks, a code block becomes inline code, a heading its text. Containers nest five deep; deeper, their markers are text.

A heading's anchor is the {#id} written after it, or a slug of its text as GitHub makes it — lower case, accents of Latin letters dropped, other runs of characters a hyphen —, unique in the document: a second heading of the same text takes -1.

Inline elements

MarkdownUJO
*i*, _i_, **b**, __b__<i>, <b>, paired as CommonMark pairs them
~s~, ~~s~~<s>
==text==<mark>
{漢字|かんじ}a ruby: the base and its reading, each up to 100 characters without a brace or a bar
`code`<code>
[text](address "title"), [text][label], [text][], [label]<a href>; the title is left out
<https://…>, <name@example.com>, https://…, www.…<a href>; www. addresses take https://
![alt](address "caption"), ![alt][label]an image block in a paragraph, its text its alternative text and its title — "…", '…' or (…) — its caption, none without a title; <a href> elsewhere; in a link, its alternative text, or its caption without one
[^label]<sup data-footnote="fn-1"><a href="#footnote-fn-1">1</a></sup>
\*, &copy;, &#65;the character
Two spaces or \ at the end of a line<br>
Inline HTMLkept for the sanitizer of phil/ujo, or escaped

Attributes

Braces after a heading, # Title {#id .class}, or alone on the line right under a block, {.class #id} — or, in the grammar of the {{ }} blocks, {class,#id,property:value} — apply to that block:

AttributeUJO
#idthe anchor tune
text-align:left, center, right, justifythe alignment tune
position, top, right, bottom, left, width, height, z-index; the classes wrapLeft and wrapRightthe layout tune
any other classa container around the block, for a look that declares the class
key=value, other propertiesleft out

Braces right after an image, ![a](a.png){width:200px,wrapLeft}, apply to the image.

Raw HTML

Raw HTML is kept or escaped, as the reader is built. DefaultCompiler keeps it for the level all, where an editor writes and the purifier keeps safe elements, and escapes it below, where it would only be removed: a member's <script> reads as text instead of vanishing. A kept HTML block is a raw block, sanitized by phil/ujo then purified at the level; an HTML comment alone is dropped. Inline HTML is sanitized with the inline tools of phil/ujo.

::: blocks

::: name Title opens a block closed by a line of :::; blocks nest. The name is a class, mapped as the classes of the {{ }} blocks are: note and descriptive take the title, or the heading they start with; warning makes a warning; info, success, danger, primary, secondary, light and dark an alert of that type; wide a wide block; any other class a container. ::: {.class #id} takes attributes instead.

Blocks in braces

A line {{tags opens a block, a line }} closes it, and blocks nest. The tags are separated by commas: a word is a class, #id the identifier, property:value a style. The first class UJO knows decides the type:

ClassUJO
note, descriptivenote, descriptive; the heading it starts with is its title
warning; info, success, danger, primary, secondary, light, darkwarning; an alert of that type
monster (frame, wide)statblock (see below)
statblock with spell or itemstatblock of that kind
toc (wide, tocDepthH4 to tocDepthH6)toc, its title the heading; the written list is dropped, the table being generated
quote with a line {{attribution …}}quote, the attribution as its caption
artistcredit, the heading as its title
classTable (frame, decoration)its tables with the variant tune
spellListcolumnFlow of two columns
frontCover, insideCover, partCover, backCoverpageStyle with that variant, before the blocks
widewide; with another class, around that block
Any othercontainer with its classes, for a look that declares them

The identifier becomes the anchor tune, and text-align, position, offsets and size the tunes of attributes. Braces on the line under any block, {color:purple,text-align:center}, apply the same way; a CSS property UJO has no place for is left out.

A span alone on its line, {{tags content}}, may be a block of the page; any other stays text:

SpanUJO
{{pageNumber 1}}pageNumber, fixed, value 1
{{pageNumber,auto}}, {{pageNumber}}pageNumber, auto
{{skipCounting}}, {{resetCounting}}pageNumber, skip or reset
{{footnote PART 1 | TITLE}}runningFooter: the label at the foot of the pages, not a footnote
{{watermark text}}, {{banner text}}watermark, banner
{{frontCover}} and the other coverspageStyle
{{watercolor8,background-color:#BBAD82}}decoration, stain, variant 8, its colour

Spans nest 8 deep within their line.

Pages and columns

MarkdownUJO
\page, \pagebreakpageBreak
\page {frontCover,class}pageBreak, then pageStyle with the cover as variant and the classes
\column, \columnbreakcolumnBreak

Stat blocks

{{monster,frame
## Gibbering Duck
*Small beast, chaotic gossipy*
___
**Armor Class**
: 15 (chain mail, shield)

___
|  STR  |  DEX  |
|:-----:|:-----:|
|10 (+0)|14 (+2)|
___
***Pack Tactics.*** These guys work together.
### Actions
***Bite.*** *Melee Weapon Attack:* +4 to hit.
}}

The first heading is the name, an italic line right after it the subtitle. A rule ___ or a heading starts a section, the heading its title. Definitions are the section's properties, the label without its colon; a table of two rows whose second holds scores — 10 (+0), 14 — its abilities, each a label, a score and a modifier; any other block stays in the section's blocks. frame and wide set the flags of the same name.