API Reference
DjotConverter
The main entry point for converting Djot to HTML.
use Djot\DjotConverter;
$converter = new DjotConverter();
$html = $converter->convert($djotString);Constructor
public function __construct(
bool $xhtml = false,
bool $warnings = false,
bool $strict = false,
bool|SafeMode|null $safeMode = null,
?Profile $profile = null,
bool $significantNewlines = false,
?SoftBreakMode $softBreakMode = null,
bool $roundTripMode = false,
?BlockParser $parser = null,
?RendererInterface $renderer = null,
bool $nestedBlocksInLists = false,
bool $blocksInterruptParagraphs = false,
bool $nestedListsWithoutBlankLine = false,
)$xhtml: Whentrue, produces XHTML-compatible output (self-closing tags like<br />).$warnings: Whentrue, collects warnings during parsing (see Error Handling).$strict: Whentrue, throwsParseExceptionon parse errors (see Error Handling).$safeMode: Whentrueor aSafeModeinstance, enables XSS protection (see Safe Mode).$profile: AProfileinstance for feature restriction (see Profiles).$significantNewlines: Deprecated. Convenience shorthand forblocksInterruptParagraphs: true, nestedListsWithoutBlankLine: true. Prefer the two granular levers. See Significant Newlines Mode.$softBreakMode: Override how soft breaks are rendered. Whennull, uses the renderer's default (newline for HTML).$roundTripMode: Whentrue, adds round-trip metadata for Djot→HTML→Djot workflows (HTML renderer only).$parser: Optional pre-configured parser. When provided, inline parser constructor flags such aswarnings,strict,significantNewlines(deprecated),nestedBlocksInLists,blocksInterruptParagraphs, andnestedListsWithoutBlankLineare ignored.$renderer: Optional pre-configured renderer. When provided, inline renderer constructor flags such asxhtml,safeMode,softBreakMode, androundTripModeare ignored.$nestedBlocksInLists: Deprecated. Whentrue, indentation alone introduces nested blocks of any type inside list items without a blank line (broad, eager - no lone-marker lookahead), while top-level paragraph interruption stays spec-compliant (see Nested Blocks in Lists Mode). Prefer$blocksInterruptParagraphs+$nestedListsWithoutBlankLine. No longer implied by$significantNewlines.$blocksInterruptParagraphs: Whentrue, top-level block elements (lists, blockquotes, headings, tables, thematic breaks, and code/div/comment fences) can interrupt a paragraph without a preceding blank line. It also interrupts a list item's lead paragraph, so an indented non-list block nests inside the item without a blank line, using the same lone-marker lookahead as the top level: unambiguous openers (#, fenced code,:::,---) and real multi-line blocks nest, while a single ambiguous marker line (>,|) stays literal. It does not nest sublists (see Block Interrupts Paragraphs Mode). Implied by$significantNewlines.$nestedListsWithoutBlankLine: Whentrue, a sublist nests inside a list item without a blank line. Only sublists nest; non-list blocks under the item stay literal and top-level paragraph interruption is unaffected (see Nested Lists Without Blank Line Mode). Implied by$significantNewlines.
Factory Methods
withSignificantNewlines()
Deprecated. Prefer using
new DjotConverter(blocksInterruptParagraphs: true, nestedListsWithoutBlankLine: true)or the two dedicated factory methods. See Significant Newlines Mode.
public static function withSignificantNewlines(
bool $xhtml = false,
bool $warnings = false,
bool $strict = false,
bool|SafeMode|null $safeMode = null,
?Profile $profile = null,
?SoftBreakMode $softBreakMode = null,
bool $roundTripMode = false,
): selfCreates a converter with significant newlines mode enabled (equivalent to blocksInterruptParagraphs: true + nestedListsWithoutBlankLine: true). See Significant Newlines Mode.
withNestedBlocksInLists()
public static function withNestedBlocksInLists(
bool $xhtml = false,
bool $warnings = false,
bool $strict = false,
bool|SafeMode|null $safeMode = null,
?Profile $profile = null,
?SoftBreakMode $softBreakMode = null,
bool $roundTripMode = false,
): selfCreates a converter that enables nested blocks in list items without requiring blank lines, while leaving top-level paragraph interruption at the spec default. See Nested Blocks in Lists Mode.
withBlocksInterruptParagraphs()
public static function withBlocksInterruptParagraphs(
bool $xhtml = false,
bool $warnings = false,
bool $strict = false,
bool|SafeMode|null $safeMode = null,
?Profile $profile = null,
?SoftBreakMode $softBreakMode = null,
bool $roundTripMode = false,
): selfCreates a converter that allows top-level block elements (lists, blockquotes, headings, tables, thematic breaks, and code/div/comment fences) to interrupt a paragraph without a preceding blank line, and nests indented non-list blocks inside list items without a blank line. Sublist nesting stays spec-compliant. See Block Interrupts Paragraphs Mode.
withNestedListsWithoutBlankLine()
public static function withNestedListsWithoutBlankLine(
bool $xhtml = false,
bool $warnings = false,
bool $strict = false,
bool|SafeMode|null $safeMode = null,
?Profile $profile = null,
?SoftBreakMode $softBreakMode = null,
bool $roundTripMode = false,
): selfCreates a converter that nests a sublist inside a list item without requiring a blank line. Only sublists nest; non-list blocks under the item stay literal and top-level paragraph interruption stays at the spec default. See Nested Lists Without Blank Line Mode.
Methods
convert()
public function convert(string $input): stringConverts Djot markup to HTML.
When a Profile with maxLength is configured, the source-length limit is enforced here because the converter still has access to the original input string.
convertFile()
public function convertFile(string $path): stringConverts a Djot file to HTML. Throws RuntimeException if the file cannot be read.
$html = $converter->convertFile('/path/to/document.djot');parse()
public function parse(string $input): DocumentParses Djot markup into an AST Document without rendering.
When a Profile with maxLength is configured, the source-length limit is also enforced here.
parseFile()
public function parseFile(string $path): DocumentParses a Djot file into an AST Document. Throws RuntimeException if the file cannot be read.
$document = $converter->parseFile('/path/to/document.djot');
// Manipulate the AST...
$html = $converter->render($document);render()
public function render(Document $document): stringRenders an AST Document to HTML.
render() does not enforce source-length limits from Profile::setMaxLength(). That limit only applies on source-based entry points such as convert(), convertFile(), parse(), and parseFile(), where the original input length is known.
Registered extensions are reset before each render, so repeated convert() calls on the same converter start with fresh per-document extension state.
transform()
public function transform(Document $document, TransformerInterface ...$transformers): DocumentApplies one or more AST transforms and returns the transformed Document.
Use this when you want an explicit, reusable derived tree instead of hidden AST mutation during render().
When you call transform() on a converter configured for a specific renderer, renderer-aware transforms can preserve renderer-specific metadata such as HTML round-trip source attributes.
use Djot\Transform\HeadingLevelShiftTransform;
$document = $converter->parse($input);
$shifted = $converter->transform($document, new HeadingLevelShiftTransform(1));
$html = $converter->render($shifted);getParser()
public function getParser(): BlockParserReturns the block parser for direct access (useful for custom pattern registration).
getRenderer()
public function getRenderer(): RendererInterfaceReturns the currently configured renderer.
This may be HtmlRenderer, MarkdownRenderer, PlainTextRenderer, AnsiRenderer, or a custom renderer passed to DjotConverter::create() or the constructor.
getHtmlRenderer()
public function getHtmlRenderer(): HtmlRendererReturns the HTML renderer for direct configuration.
Throws LogicException if the converter is not using HtmlRenderer.
on()
public function on(string $event, Closure $listener): selfRegister a listener for render events. See Event System below.
off()
public function off(?string $event = null): selfRemove listeners. Pass event name to remove specific listeners, or null to remove all.
getWarnings()
public function getWarnings(): arrayReturns an array of ParseWarning objects from the last parse operation. Only populated when warnings: true is set.
hasWarnings()
public function hasWarnings(): boolReturns true if there were any warnings during the last parse operation.
clearWarnings()
public function clearWarnings(): selfClears any collected warnings.
setSafeMode()
public function setSafeMode(bool|SafeMode|null $safeMode): selfEnable, disable, or configure safe mode after construction. Pass true for defaults, a SafeMode instance for custom configuration, or null/false to disable.
Safe Mode
Safe mode provides built-in XSS protection for user-generated content.
Basic Usage
use Djot\DjotConverter;
// Enable with sensible defaults
$converter = new DjotConverter(safeMode: true);
$html = $converter->convert($userInput);What Safe Mode Does
URL Sanitization: Blocks dangerous URL schemes in links and images
- Blocked by default:
javascript:,vbscript:,data:,file: - Safe URLs like
https:,mailto:, and relative paths are allowed
- Blocked by default:
Attribute Filtering: Strips event handler attributes
- Blocks attributes starting with
on(e.g.,onclick,onload,onerror) - Blocks specific dangerous attributes (
srcdoc,formaction) - Allows safe attributes like
class,id,data-*
- Blocks attributes starting with
Raw HTML Handling: Controls how raw HTML is processed
escape(default): HTML-encodes raw HTML so it displays as textstrip: Removes raw HTML entirelyallow: Passes raw HTML through (not recommended)
SafeMode Class
use Djot\SafeMode;
// Factory methods
$safeMode = SafeMode::defaults(); // Standard protection
$safeMode = SafeMode::strict(); // Strips raw HTML completelyConfiguration Methods
// URL scheme control
$safeMode->setDangerousSchemes(['javascript', 'vbscript', 'data']);
$safeMode->addDangerousScheme('ftp');
$safeMode->getDangerousSchemes();
// Whitelist approach (only these schemes allowed)
$safeMode->setAllowedSchemes(['https', 'mailto']);
$safeMode->getAllowedSchemes();
// Attribute filtering
$safeMode->setBlockedAttributePrefixes(['on']); // Blocks onclick, onload, etc.
$safeMode->setBlockedAttributes(['srcdoc', 'formaction']);
$safeMode->getBlockedAttributePrefixes();
$safeMode->getBlockedAttributes();
// Raw HTML handling
$safeMode->setRawHtmlMode(SafeMode::RAW_HTML_ESCAPE); // Default
$safeMode->setRawHtmlMode(SafeMode::RAW_HTML_STRIP); // Remove completely
$safeMode->setRawHtmlMode(SafeMode::RAW_HTML_ALLOW); // Pass through
$safeMode->getRawHtmlMode();Validation Methods
$safeMode->isUrlSafe('https://example.com'); // true
$safeMode->isUrlSafe('javascript:alert(1)'); // false
$safeMode->isAttributeSafe('class'); // true
$safeMode->isAttributeSafe('onclick'); // false
$safeMode->sanitizeUrl('javascript:alert(1)'); // ''
$safeMode->filterAttributes([
'class' => 'highlight',
'onclick' => 'hack()',
]); // ['class' => 'highlight']Custom Configuration Example
use Djot\DjotConverter;
use Djot\SafeMode;
// Only allow HTTPS links, strip raw HTML
$safeMode = SafeMode::defaults()
->setAllowedSchemes(['https'])
->setRawHtmlMode(SafeMode::RAW_HTML_STRIP);
$converter = new DjotConverter(safeMode: $safeMode);Enabling After Construction
$converter = new DjotConverter();
// Enable later
$converter->setSafeMode(true);
// Or with custom config
$converter->setSafeMode(SafeMode::strict());
// Disable
$converter->setSafeMode(false);Error Handling
The parser can optionally report warnings and errors with line/column information.
Warning Mode
Enable warning collection to detect issues without stopping parsing:
$converter = new DjotConverter(warnings: true);
$html = $converter->convert($djot);
if ($converter->hasWarnings()) {
foreach ($converter->getWarnings() as $warning) {
echo $warning->getMessage(); // "Undefined reference 'foo'"
echo $warning->getLine(); // 5
echo $warning->getColumn(); // 3
// Or as string
echo $warning; // "Undefined reference 'foo' at line 5, column 3"
// Or as array
$data = $warning->toArray();
// ['message' => '...', 'line' => 5, 'column' => 3]
}
}Warnings are reported for:
- Undefined reference links (
[text][missing]) - Undefined footnotes (
[^missing])
Strict Mode
Enable strict mode to throw exceptions on parse errors:
use Djot\Exception\ParseException;
$converter = new DjotConverter(strict: true);
try {
$html = $converter->convert($djot);
} catch (ParseException $e) {
echo $e->getMessage(); // "Unclosed code fence at line 3, column 1"
echo $e->getSourceLine(); // 3
echo $e->getSourceColumn(); // 1
}Errors that throw in strict mode:
- Unclosed code fences
- Unclosed divs (
:::) - Unclosed comments (
{% ... %}) - Unclosed raw blocks
Both Modes
You can enable both modes together - warnings will be collected for non-fatal issues, while fatal errors will throw:
$converter = new DjotConverter(warnings: true, strict: true);ParseWarning
use Djot\Exception\ParseWarning;
$warning->getMessage(): string // Warning message
$warning->getLine(): int // 1-indexed line number
$warning->getColumn(): int // 1-indexed column number
$warning->toArray(): array // ['message' => ..., 'line' => ..., 'column' => ...]
(string)$warning // "Message at line X, column Y"ParseException
use Djot\Exception\ParseException;
$e->getMessage(): string // Full message with location
$e->getSourceLine(): int // 1-indexed line number
$e->getSourceColumn(): int // 1-indexed column numberEvent System
The event system allows you to customize rendering without subclassing.
Event Names
Events are named render.{node_type}:
render.link,render.image,render.heading,render.paragraph, etc.render.*- wildcard, fires for all nodes
Modifying Nodes
Modify node attributes before rendering:
$converter->on('render.link', function (RenderEvent $event): void {
$link = $event->getNode();
// Add target="_blank" to external links
if (str_starts_with($link->getDestination(), 'http')) {
$link->setAttribute('target', '_blank');
$link->setAttribute('rel', 'noopener');
}
});Replacing Output
Replace the rendered HTML entirely:
$converter->on('render.symbol', function (RenderEvent $event): void {
$symbol = $event->getNode();
$event->setHtml(match($symbol->getName()) {
'heart' => '❤️',
'star' => '⭐',
default => ':' . $symbol->getName() . ':',
});
});Chaining
Methods return $this for chaining:
$html = $converter
->on('render.link', $linkHandler)
->on('render.image', $imageHandler)
->convert($djot);RenderEvent
use Djot\Event\RenderEvent;
$event->getNode(): Node // Get the node being rendered
$event->setHtml(string $html) // Replace output HTML
$event->getHtml(): ?string // Get custom HTML if set
$event->preventDefault() // Skip default rendering
$event->isDefaultPrevented(): boolParser Classes
For advanced use cases, you can work directly with the parser and renderer.
BlockParser
Parses Djot input into an AST (Abstract Syntax Tree).
use Djot\Parser\BlockParser;
$parser = new BlockParser(
collectWarnings: false,
strictMode: false,
significantNewlines: false,
nestedBlocksInLists: false,
blocksInterruptParagraphs: false,
nestedListsWithoutBlankLine: false,
);
$document = $parser->parse($djotString);
// Get warnings (if collectWarnings: true)
// Get exception on errors (if strictMode: true)
$warnings = $parser->getWarnings();
// Enable/disable significant newlines mode (deprecated: enables both levers below)
$parser->setSignificantNewlines(true);
$isEnabled = $parser->getSignificantNewlines();
// Enable/disable nested blocks in list items only
// (significant newlines mode enables this implicitly)
$parser->setNestedBlocksInLists(true);
$isEnabled = $parser->getNestedBlocksInLists();
// Enable/disable top-level paragraph interruption and non-list
// block nesting in list items
// (significant newlines mode enables this implicitly)
$parser->setBlocksInterruptParagraphs(true);
$isEnabled = $parser->getBlocksInterruptParagraphs();
// Enable/disable sublist nesting in list items only
// (significant newlines mode enables this implicitly)
$parser->setNestedListsWithoutBlankLine(true);
$isEnabled = $parser->getNestedListsWithoutBlankLine();Custom Block Patterns
Register custom block-level syntax patterns:
$parser = $converter->getParser();
// Register a custom block pattern
$parser->addBlockPattern('/^!!!\\s*(note|warning|danger)\\s*$/', function ($lines, $start, $parent, $p) {
preg_match('/^!!!\\s*(note|warning|danger)\\s*$/', $lines[$start], $m);
$type = $m[1];
// Collect indented content
$content = [];
$i = $start + 1;
while ($i < count($lines) && preg_match('/^\\s+(.*)$/', $lines[$i], $contentMatch)) {
$content[] = $contentMatch[1];
$i++;
}
$div = new Div();
$div->setAttribute('class', 'admonition ' . $type);
$p->parseBlockContent($div, $content); // Parse nested content
$parent->appendChild($div);
return $i - $start; // Return number of lines consumed
});
// Remove a pattern
$parser->removeBlockPattern('/^!!!\\s*(note|warning|danger)\\s*$/');
// List registered patterns
$patterns = $parser->getBlockPatterns();Callback signature:
function(array $lines, int $startIndex, Node $parent, BlockParser $parser): ?int$lines: Array of all input lines$startIndex: Index of the line that matched the pattern$parent: Parent node to append children to$parser: The BlockParser instance (useparseBlockContent()for nested parsing)- Returns: Number of lines consumed, or
nullto fall back to default parsing
Custom Inline Patterns
Register custom inline syntax patterns via the InlineParser:
$inlineParser = $converter->getParser()->getInlineParser();
// Register @mention pattern
$inlineParser->addInlinePattern('/@([a-zA-Z0-9_]+)/', function ($match, $groups, $p) {
$link = new Link('https://example.com/users/' . $groups[1]);
$link->appendChild(new Text('@' . $groups[1]));
return $link;
});
// Remove a pattern
$inlineParser->removeInlinePattern('/@([a-zA-Z0-9_]+)/');
// List registered patterns
$patterns = $inlineParser->getInlinePatterns();Callback signature:
function(string $match, array $groups, InlineParser $parser): ?Node$match: The full matched string$groups: Array of regex capture groups (index 0 is full match)$parser: The InlineParser instance- Returns: A Node to insert, or
nullto fall back to default parsing
Important notes:
- Custom patterns are checked before built-in syntax
- Returning
nullallows the default parser to handle the text - Patterns can override built-in syntax (e.g., override
**bold**)
HtmlRenderer
Renders an AST Document to HTML.
use Djot\Renderer\HtmlRenderer;
$renderer = new HtmlRenderer(xhtml: false);
$html = $renderer->render($document);Configuration:
// Convert tabs in code to 4 spaces (default preserves them)
$renderer->setCodeBlockTabWidth(4);
// Customize soft break rendering
$renderer->setSoftBreakMode(SoftBreakMode::Space);
// Enable safe mode for user-generated content
$renderer->setSafeMode(SafeMode::defaults());Tab Width in Code Blocks
By default, tabs in code blocks and inline code are preserved literally, which is spec-conformant (djot keeps tabs). A literal tab in <pre> renders at the browser's default tab width (usually 8), so if you want a fixed width, convert tabs to spaces:
// Convert tabs to 4 spaces
$renderer->setCodeBlockTabWidth(4);
// Use a different width (e.g., 2 spaces)
$renderer->setCodeBlockTabWidth(2);
// Preserve tabs (default)
$renderer->setCodeBlockTabWidth(null);The opt-in TabNormalizationExtension wraps this for the common case (new TabNormalizationExtension() converts tabs to 4 spaces).
This affects both fenced code blocks (<pre><code>) and inline code (<code>).
PlainTextRenderer
Renders an AST Document to plain text (useful for search indexing, SEO, email fallbacks).
use Djot\Renderer\PlainTextRenderer;
$renderer = new PlainTextRenderer();
$text = $renderer->render($document);Configuration:
// Customize list item prefix (default: "- ")
$renderer->setListItemPrefix('* ');
// Customize table cell separator (default: "\t")
$renderer->setTableCellSeparator(' | ');Behavior:
- Strips all formatting (emphasis, strong, etc.)
- Preserves text content from links and images (alt text)
- Renders lists with configurable prefixes
- Renders tables with configurable separators
- Strips raw HTML and comments
- Preserves footnote references as
[1]etc.
MarkdownRenderer
Renders an AST Document to CommonMark-compatible Markdown. Useful for:
- Converting Djot content to Markdown for systems that only support Markdown
- Migrating content between formats
- Generating Markdown documentation from Djot source
use Djot\DjotConverter;
use Djot\Renderer\MarkdownRenderer;
$converter = new DjotConverter();
$document = $converter->parse($djotText);
$renderer = new MarkdownRenderer();
$markdown = $renderer->render($document);Conversion Table:
| Djot | Markdown Output |
|---|---|
*strong* | **strong** |
_emphasis_ | *emphasis* |
{-deleted-} | ~~deleted~~ (GFM) |
{+inserted+} | <ins>inserted</ins> |
{=highlighted=} | <mark>highlighted</mark> |
^superscript^ | <sup>superscript</sup> |
~subscript~ | <sub>subscript</sub> |
`code` | `code` |
[text](url) | [text](url) |
 |  |
# Heading | # Heading |
> quote | > quote |
- list | - list |
1. ordered | 1. ordered |
- [ ] task | - [ ] task |
:symbol: | :symbol: |
[^note] | [^note] |
$math$ | $math$ |
$$display$$ | $$display$$ |
| Tables | GFM tables with alignment |
| Divs | Content only (no wrapper) |
| Spans | Content only (no wrapper) |
| Definition lists | Bold term + : description |
| Line blocks | Hard breaks ( \n) |
| Raw HTML | Passed through |
| Comments | Stripped |
Behavior:
- Produces CommonMark-compatible output
- Uses GFM extensions where available (strikethrough, tables, task lists, footnotes)
- Falls back to inline HTML for features without Markdown equivalents
- Escapes special Markdown characters in text content
- Handles nested backticks in code spans and fenced blocks
- Preserves table column alignment
- Normalizes multiple blank lines
Example:
$djot = <<<'DJOT'
# Hello *World*
This has {=highlighted=} and {-deleted-} text.
| Name | Score |
|-------|------:|
| Alice | 95 |
DJOT;
$document = $converter->parse($djot);
$markdown = (new MarkdownRenderer())->render($document);Output:
# Hello **World**
This has <mark>highlighted</mark> and ~~deleted~~ text.
| Name | Score |
| --- | ---: |
| Alice | 95 |Limitations:
- Djot divs (
::: class) lose their class/attributes (content is preserved) - Djot spans (
[text]{.class}) lose their attributes (content is preserved) - Definition lists are approximated (not native Markdown)
- Some whitespace/formatting may differ from original
AST Node Types
Block Nodes
All block nodes extend Djot\Node\Block\BlockNode:
| Class | Description |
|---|---|
Document | Root document node |
Paragraph | Text paragraph |
Heading | Heading (levels 1-6) |
CodeBlock | Fenced code block |
BlockQuote | Block quote |
ListBlock | Ordered, unordered, or task list |
ListItem | List item |
Table | Table |
TableRow | Table row |
TableCell | Table cell (th or td) |
Div | Generic div container |
Section | Section wrapper (used with HeadingPermalinksExtension) |
Figure | Figure container for images/blockquotes with captions |
Caption | Caption for figures and tables |
LineBlock | Line block (preserves line breaks) |
ThematicBreak | Horizontal rule |
DefinitionList | Definition list |
DefinitionTerm | Definition term |
DefinitionDescription | Definition description |
Footnote | Footnote definition |
RawBlock | Raw HTML block |
Comment | Comment (not rendered) |
Inline Nodes
All inline nodes extend Djot\Node\Inline\InlineNode:
| Class | Description |
|---|---|
Text | Plain text |
Emphasis | Emphasized text |
Strong | Strong text |
Code | Inline code |
Link | Hyperlink |
Image | Image |
HardBreak | Hard line break |
SoftBreak | Soft line break |
Span | Span with attributes |
Superscript | Superscript text |
Subscript | Subscript text |
Highlight | Highlighted text |
Insert | Inserted text |
Delete | Deleted text |
Abbreviation | Abbreviation with title (<abbr>) |
FootnoteRef | Footnote reference |
Math | Math expression |
Symbol | Symbol (e.g., :heart:) |
RawInline | Raw HTML inline |
Working with the AST
use Djot\Parser\BlockParser;
use Djot\Renderer\HtmlRenderer;
$parser = new BlockParser();
$renderer = new HtmlRenderer();
// Parse to AST
$document = $parser->parse('# Hello *world*');
// Manipulate AST
foreach ($document->getChildren() as $node) {
echo $node->getType() . "\n"; // "heading"
}
// Render to HTML
$html = $renderer->render($document);
// <h1>Hello <strong>world</strong></h1>Modifying Nodes
// Get/set attributes
$node->setAttribute('class', 'highlight');
$node->getAttribute('class'); // 'highlight'
$node->addClass('special');Node Methods
// Get node type
$node->getType(): string
// Children
$node->getChildren(): array
$node->appendChild(Node $child): void
$node->prependChild(Node $child): void
// Attributes
$node->getAttribute(string $key): mixed
$node->setAttribute(string $key, mixed $value): void
$node->getAttributes(): array
$node->setAttributes(array $attrs): void
$node->addClass(string $class): voidSignificant Newlines Mode
Deprecated.
significantNewlinesis a convenience shorthand forblocksInterruptParagraphs: true+nestedListsWithoutBlankLine: true. Prefer the two granular levers or their factory methods.
An optional parsing mode for chat messages, comments, and quick notes where markdown-like behavior is more intuitive.
Enabling
// Via factory method (deprecated)
$converter = DjotConverter::withSignificantNewlines();
// Via constructor parameter (deprecated)
$converter = new DjotConverter(significantNewlines: true);
// Preferred: use the two granular levers
$converter = new DjotConverter(
blocksInterruptParagraphs: true,
nestedListsWithoutBlankLine: true,
);
// Via parser directly (deprecated)
$parser = new BlockParser(significantNewlines: true);
// Via setter (for runtime switching)
$parser->setSignificantNewlines(true);Behavior Changes
| Feature | Standard Mode | Significant Newlines Mode |
|---|---|---|
| Block elements interrupt paragraphs | No (blank line required) | Yes |
| Nested lists need blank lines | Yes | No |
Note: Soft break rendering is controlled separately via SoftBreakMode - it is not affected by significant newlines mode.
Example
$converter = DjotConverter::withSignificantNewlines();
$djot = <<<'DJOT'
Here is a list:
- item one
- item two
DJOT;
echo $converter->convert($djot);Output:
<p>Here is a list:</p>
<ul>
<li>item one</li>
<li>item two</li>
</ul>In standard mode, the same input would produce:
<p>Here is a list:
- item one
- item two</p>Escaping Block Markers
In significant newlines mode, escape the first character of block markers to keep them literal:
They said:
\> This stays as literal text, not a blockquote
Steps:
\1. This is not an ordered listSoft Break Behavior
Soft break rendering is configured via SoftBreakMode and is independent of significantNewlines mode:
use Djot\DjotConverter;
use Djot\Renderer\SoftBreakMode;
// Set via constructor
$converter = new DjotConverter(softBreakMode: SoftBreakMode::Break);
// Combine with significant newlines for chat-style input
$converter = new DjotConverter(
significantNewlines: true,
softBreakMode: SoftBreakMode::Break,
);Available modes: Newline (default for HTML/Markdown), Space (default for PlainText/ANSI), Break (visible line break). Output varies by renderer - see Parser Options for details.
Feature Support Matrix
This matrix shows which features are supported by each renderer.
Block Elements
| Feature | HtmlRenderer | PlainTextRenderer | MarkdownRenderer | AnsiRenderer |
|---|---|---|---|---|
| Paragraph | ✓ | ✓ | ✓ | ✓ |
| Heading | ✓ | ✓ | ✓ | ✓ (styled) |
| Code Block | ✓ | ✓ | ✓ | ✓ (boxed) |
| Block Quote | ✓ | ✓ (prefixed) | ✓ | ✓ (styled) |
| Lists (ul/ol) | ✓ | ✓ | ✓ | ✓ |
| Task Lists | ✓ | ✓ | ✓ (GFM) | ✓ |
| Tables | ✓ | ✓ (tab-separated) | ✓ (GFM) | ✓ (boxed) |
| Table Captions | ✓ (<caption>) | – | – | ✓ |
| Definition Lists | ✓ | ✓ | ✓ (approximated) | ✓ |
| Divs | ✓ | ✓ (content only) | – (content only) | ✓ |
| Line Blocks | ✓ | ✓ | ✓ (hard breaks) | ✓ |
| Thematic Break | ✓ | ✓ (dashes) | ✓ | ✓ |
| Footnotes | ✓ | ✓ (numbered) | ✓ (GFM) | ✓ |
| Figure/Caption | ✓ | – | – | ✓ |
| Raw HTML | ✓ / escaped | – | ✓ | – |
| Comments | – (stripped) | – | – | – |
Inline Elements
| Feature | HtmlRenderer | PlainTextRenderer | MarkdownRenderer | AnsiRenderer |
|---|---|---|---|---|
| Emphasis | ✓ (<em>) | ✓ (content only) | ✓ (*text*) | ✓ (italic) |
| Strong | ✓ (<strong>) | ✓ (content only) | ✓ (**text**) | ✓ (bold) |
| Code | ✓ (<code>) | ✓ | ✓ (`code`) | ✓ (styled) |
| Links | ✓ | ✓ (text + URL) | ✓ | ✓ (text + URL) |
| Images | ✓ | ✓ (alt text) | ✓ | ✓ (alt text) |
| Superscript | ✓ (<sup>) | ✓ (content only) | ✓ (<sup>) | ✓ (styled) |
| Subscript | ✓ (<sub>) | ✓ (content only) | ✓ (<sub>) | ✓ (styled) |
| Highlight | ✓ (<mark>) | ✓ (content only) | ✓ (<mark>) | ✓ (styled) |
| Insert | ✓ (<ins>) | ✓ (content only) | ✓ (<ins>) | ✓ (styled) |
| Delete | ✓ (<del>) | ✓ (content only) | ✓ (~~text~~) | ✓ (strikethrough) |
| Abbreviation | ✓ (<abbr>) | ✓ (content only) | – | ✓ |
| Spans | ✓ | ✓ (content only) | – (content only) | ✓ (content only) |
| Math | ✓ ($...$) | ✓ (content only) | ✓ ($...$) | ✓ |
| Symbols | ✓ (:name:) | ✓ (content only) | ✓ (:name:) | ✓ (mapped) |
| Footnote Refs | ✓ | ✓ ([n]) | ✓ ([^n]) | ✓ |
| Soft Break | ✓ (configurable) | ✓ | ✓ | ✓ |
| Hard Break | ✓ (<br>) | ✓ (newline) | ✓ ( \n) | ✓ |
| Raw HTML | ✓ / escaped | – | ✓ | – |
Legend
- ✓ — Full support
- ✓ (note) — Supported with noted behavior
- – — Not supported / stripped
Renderer Use Cases
| Renderer | Primary Use Case |
|---|---|
| HtmlRenderer | Web pages, HTML emails, CMS content |
| PlainTextRenderer | Search indexing, SEO descriptions, email fallbacks, accessibility |
| MarkdownRenderer | Converting Djot to CommonMark/GFM for Markdown-only systems |
| AnsiRenderer | Terminal output, CLI tools, console applications |