0
# Rendering and Output
1
2
HTML rendering system with customizable render rules and support for custom renderers to generate different output formats from parsed markdown tokens.
3
4
## Capabilities
5
6
### HTML Renderer
7
8
Default renderer that converts tokens to HTML output.
9
10
```python { .api }
11
class RendererHTML:
12
"""HTML renderer with customizable render rules."""
13
14
__output__ = "html" # Output format identifier
15
16
def __init__(self, parser: MarkdownIt = None):
17
"""
18
Initialize HTML renderer.
19
20
Parameters:
21
- parser: MarkdownIt instance (for reference)
22
"""
23
24
def render(self, tokens: list[Token], options: dict, env: dict) -> str:
25
"""
26
Render token stream to HTML.
27
28
Parameters:
29
- tokens: list of tokens to render
30
- options: parser options
31
- env: environment data
32
33
Returns:
34
- str: rendered HTML output
35
"""
36
37
def renderInline(self, tokens: list[Token], options: dict, env: dict) -> str:
38
"""
39
Render inline tokens to HTML.
40
41
Parameters:
42
- tokens: list of inline tokens
43
- options: parser options
44
- env: environment data
45
46
Returns:
47
- str: rendered HTML without block wrappers
48
"""
49
50
def renderToken(self, tokens: list[Token], idx: int, options: dict, env: dict) -> str:
51
"""
52
Render single token at specified index.
53
54
Parameters:
55
- tokens: full token list
56
- idx: index of token to render
57
- options: parser options
58
- env: environment data
59
60
Returns:
61
- str: rendered HTML for single token
62
"""
63
```
64
65
**Usage Example:**
66
67
```python
68
from markdown_it import MarkdownIt
69
from markdown_it.renderer import RendererHTML
70
71
md = MarkdownIt()
72
tokens = md.parse("# Hello\n\n**Bold** text.")
73
74
# Direct rendering
75
html = md.renderer.render(tokens, md.options, {})
76
print(html)
77
78
# Custom renderer instance
79
custom_renderer = RendererHTML()
80
html = custom_renderer.render(tokens, md.options, {})
81
```
82
83
### Custom Render Rules
84
85
Customize rendering for specific token types.
86
87
```python { .api }
88
# Render rules dictionary
89
rules: dict[str, callable] # Maps token types to render functions
90
91
def add_render_rule(self, name: str, function: callable, fmt: str = "html") -> None:
92
"""
93
Add custom render rule for token type.
94
95
Parameters:
96
- name: token type name
97
- function: render function with signature (tokens, idx, options, env) -> str
98
- fmt: output format (must match renderer.__output__)
99
"""
100
```
101
102
**Usage Example:**
103
104
```python
105
from markdown_it import MarkdownIt
106
107
def custom_heading_rule(tokens, idx, options, env):
108
"""Custom heading renderer with anchor links."""
109
token = tokens[idx]
110
if token.nesting == 1: # opening tag
111
# Get heading text from next inline token
112
heading_text = tokens[idx + 1].content if idx + 1 < len(tokens) else ""
113
anchor_id = heading_text.lower().replace(" ", "-")
114
return f'<{token.tag} id="{anchor_id}">'
115
else: # closing tag
116
return f'</{token.tag}>'
117
118
md = MarkdownIt()
119
md.add_render_rule("heading_open", custom_heading_rule)
120
md.add_render_rule("heading_close", custom_heading_rule)
121
122
html = md.render("# Hello World")
123
# Output: <h1 id="hello-world">Hello World</h1>
124
```
125
126
### Custom Renderer Classes
127
128
Create custom renderers for different output formats.
129
130
```python { .api }
131
class RendererProtocol(Protocol):
132
"""Protocol defining renderer interface."""
133
134
__output__: str # Output format identifier
135
136
def render(self, tokens: list[Token], options: dict, env: dict) -> Any:
137
"""Render tokens to output format."""
138
```
139
140
**Usage Example:**
141
142
```python
143
from markdown_it import MarkdownIt
144
from markdown_it.renderer import RendererProtocol
145
146
class MarkdownRenderer(RendererProtocol):
147
"""Custom renderer that outputs markdown (round-trip)."""
148
149
__output__ = "markdown"
150
151
def __init__(self, parser=None):
152
self.rules = {}
153
154
def render(self, tokens, options, env):
155
result = ""
156
for i, token in enumerate(tokens):
157
if token.type in self.rules:
158
result += self.rules[token.type](tokens, i, options, env)
159
else:
160
result += self.default_render(token)
161
return result
162
163
def default_render(self, token):
164
if token.type == "heading_open":
165
return "#" * int(token.tag[1]) + " "
166
elif token.type == "paragraph_open":
167
return ""
168
elif token.type == "inline":
169
return token.content
170
elif token.type in ["heading_close", "paragraph_close"]:
171
return "\n\n"
172
return ""
173
174
# Use custom renderer
175
md = MarkdownIt(renderer_cls=MarkdownRenderer)
176
markdown_output = md.render("# Title\n\nParagraph text.")
177
```
178
179
### Built-in Render Rules
180
181
Standard render rules for common token types:
182
183
```python { .api }
184
# Block elements
185
def code_block(self, tokens, idx, options, env): ...
186
def fence(self, tokens, idx, options, env): ...
187
def blockquote_open(self, tokens, idx, options, env): ...
188
def blockquote_close(self, tokens, idx, options, env): ...
189
def hr(self, tokens, idx, options, env): ...
190
191
# Lists
192
def bullet_list_open(self, tokens, idx, options, env): ...
193
def bullet_list_close(self, tokens, idx, options, env): ...
194
def list_item_open(self, tokens, idx, options, env): ...
195
def list_item_close(self, tokens, idx, options, env): ...
196
197
# Inline elements
198
def text(self, tokens, idx, options, env): ...
199
def html_inline(self, tokens, idx, options, env): ...
200
def html_block(self, tokens, idx, options, env): ...
201
def softbreak(self, tokens, idx, options, env): ...
202
def hardbreak(self, tokens, idx, options, env): ...
203
204
# Links and media
205
def link_open(self, tokens, idx, options, env): ...
206
def link_close(self, tokens, idx, options, env): ...
207
def image(self, tokens, idx, options, env): ...
208
209
# Tables
210
def table_open(self, tokens, idx, options, env): ...
211
def tr_open(self, tokens, idx, options, env): ...
212
def td_open(self, tokens, idx, options, env): ...
213
def th_open(self, tokens, idx, options, env): ...
214
```
215
216
### Render Rule Customization
217
218
Override built-in render rules for customization:
219
220
```python
221
from markdown_it import MarkdownIt
222
223
class CustomRenderer(RendererHTML):
224
"""Custom HTML renderer with modifications."""
225
226
def code_block(self, tokens, idx, options, env):
227
"""Custom code block rendering with line numbers."""
228
token = tokens[idx]
229
info = token.info.strip() if token.info else ""
230
lang = info.split()[0] if info else ""
231
232
code = token.content
233
lines = code.rstrip().split('\n')
234
235
html = f'<pre><code class="language-{lang}">'
236
for i, line in enumerate(lines, 1):
237
html += f'<span class="line-number">{i:3d}</span>{line}\n'
238
html += '</code></pre>'
239
240
return html
241
242
def strong_open(self, tokens, idx, options, env):
243
"""Use <b> instead of <strong>."""
244
return '<b>'
245
246
def strong_close(self, tokens, idx, options, env):
247
"""Use <b> instead of <strong>."""
248
return '</b>'
249
250
# Use custom renderer
251
md = MarkdownIt(renderer_cls=CustomRenderer)
252
html = md.render("```python\nprint('hello')\n```\n\n**Bold text**")
253
```
254
255
### Attribute Rendering
256
257
Utility methods for rendering HTML attributes:
258
259
```python { .api }
260
def renderAttrs(self, token: Token) -> str:
261
"""
262
Render token attributes to HTML attribute string.
263
264
Parameters:
265
- token: token with attributes
266
267
Returns:
268
- str: HTML attribute string
269
"""
270
271
def renderToken(self, tokens: list[Token], idx: int, options: dict, env: dict) -> str:
272
"""
273
Default token rendering with attribute support.
274
275
Parameters:
276
- tokens: token list
277
- idx: token index
278
- options: parser options
279
- env: environment data
280
281
Returns:
282
- str: rendered token HTML
283
"""
284
```
285
286
**Usage Example:**
287
288
```python
289
from markdown_it.token import Token
290
from markdown_it.renderer import RendererHTML
291
292
renderer = RendererHTML()
293
294
# Token with attributes
295
token = Token("div_open", "div", 1)
296
token.attrSet("class", "container")
297
token.attrSet("id", "main")
298
299
# Render attributes
300
attrs_html = renderer.renderAttrs(token)
301
print(attrs_html) # ' class="container" id="main"'
302
303
# Full token rendering
304
html = renderer.renderToken([token], 0, {}, {})
305
print(html) # '<div class="container" id="main">'
306
```
307
308
## Output Customization
309
310
### Escaping and Security
311
312
HTML escaping utilities for safe output:
313
314
```python { .api }
315
from markdown_it.common.utils import escapeHtml, unescapeAll
316
317
def escapeHtml(raw: str) -> str:
318
"""
319
Escape HTML characters for safe output.
320
321
Parameters:
322
- raw: raw text to escape
323
324
Returns:
325
- str: HTML-escaped text
326
"""
327
328
def unescapeAll(str: str) -> str:
329
"""
330
Unescape entities and backslash escapes.
331
332
Parameters:
333
- str: text to unescape
334
335
Returns:
336
- str: unescaped text
337
"""
338
```
339
340
### Link and URL Processing
341
342
URL handling in render context:
343
344
```python
345
def custom_link_render(tokens, idx, options, env):
346
"""Custom link rendering with security checks."""
347
token = tokens[idx]
348
349
if token.nesting == 1: # link_open
350
href = token.attrGet("href")
351
if href:
352
# Custom URL validation
353
if not is_safe_url(href):
354
return '<span class="invalid-link">'
355
# Add custom attributes
356
token.attrSet("rel", "noopener")
357
token.attrSet("target", "_blank")
358
359
# Render with attributes
360
return renderer.renderToken(tokens, idx, options, env)
361
else: # link_close
362
return '</a>' if token.attrGet("href") else '</span>'
363
364
md = MarkdownIt()
365
md.add_render_rule("link_open", custom_link_render)
366
md.add_render_rule("link_close", custom_link_render)
367
```
368
369
### Multiple Output Formats
370
371
Supporting multiple renderers:
372
373
```python
374
from markdown_it import MarkdownIt
375
376
class MultiFormatRenderer:
377
"""Wrapper supporting multiple output formats."""
378
379
def __init__(self):
380
self.html_renderer = RendererHTML()
381
self.markdown_renderer = MarkdownRenderer()
382
383
def render(self, tokens, options, env, format="html"):
384
if format == "html":
385
return self.html_renderer.render(tokens, options, env)
386
elif format == "markdown":
387
return self.markdown_renderer.render(tokens, options, env)
388
else:
389
raise ValueError(f"Unknown format: {format}")
390
391
# Usage
392
md = MarkdownIt()
393
tokens = md.parse("# Title\n\n**Bold** text.")
394
395
multi_renderer = MultiFormatRenderer()
396
html = multi_renderer.render(tokens, md.options, {}, "html")
397
markdown = multi_renderer.render(tokens, md.options, {}, "markdown")
398
```