md4c:Markdown 渲染
两个符号管好一件事:CommonMark 解析与 HTML 渲染。小而正确的选择——事件流驱动自定义渲染,一步渲染管好文档站。
本章导读
build.bat md4c 变体。37.1 定位
md4c 是符合 CommonMark 的解析器:不生成 DOM,不分配字符串——解析即事件流。这种极简性正好匹配 XServer 的脚本场景:输入是不可信内容时,渲染成受控 HTML 比执行模板安全得多(ch19 的受控区块思想)。两个导入符号(md_parse / md_html)就是全部 API 面。
37.2 事件流模型
md_parse 按文档顺序回调:块的进出(DOC/P/LI/CODE/TABLE…)、span 的进出(EM/STRONG/CODE_SPAN/LINK/IMG…)与文本片段。回调结构必须完整填充——空指针无保护(这是与多数库不同的硬约束);每个回调返回非零即中止解析。自定义渲染器(摘要提取、目录生成、纯文本化)都建立在这层。
static int enter_block(MD_BLOCKTYPE t, void* d, void* u)
{
return 0; /* 继续 */
}
/* leave_block / enter_span / leave_span / text 同形 */
MD_PARSER tParser = { 0, NULL, enter_block, leave_block,
enter_span, leave_span, text_cb };
md_parse(sDoc, iLen, &tParser, &tCtx);
37.3 一步渲染
md_html 内建完整回调,把 Markdown 渲染成 HTML 片段经输出回调交给你——签名与 md_parse 相同的心智。flag 开启扩展方言:表格、删除线、任务列表、维基链接。HTML 逃逸默认开启(原文里的标签会被转义),这是把不可信输入变安全输出的关键默认。
37.4 与模板层组合
典型管线:请求带来的 Markdown → md_html 渲染进缓冲 → 作为变量注入 ch19 模板的受控区块。文档字段存 Markdown、渲染在请求期完成——编辑体验与安全性兼得。静态文档站则可以在 Init 期一次性渲染(ch41 的热点缓存纪律)。
37.5 契约
| 关注 | 要点 |
|---|---|
| 内存 | 解析零分配;md_html 的输出经回调交付,无库侧持有缓冲 |
| 回调 | 结构必须完整填充;返回非零中止(错误传播通道) |
| 字符 | 默认 char;定义 _UNICODE 时 MD_CHAR 为 WCHAR(TCC 脚本默认窄字符) |
| 探测 | #ifdef XS_USE_MD4C 或 xsExtensionEnabled("md4c") |
常见错误
- 症状
- md_parse 传 {0} 的解析器,进程访问违例。
- 原因
- 回调指针无空保护——库会直接调用。要么填完整结构,要么用自带回调的 md_html。
- 症状
- 渲染出的 HTML 直接 echo 进自制页面,脚本注入复活。
- 原因
- md_html 逃逸的是 Markdown 原文里的标签;你自己在输出后追加的内容仍需走模板的转义规则(ch19)。
编程练习
同一份带表格与代码块的 Markdown 分别用默认与 GFM flag 渲染,对比输出。
基于 md_parse 事件流提取 H1-H3 生成目录树(锚点跳转)。
GET /docs/:name 读 Markdown 文件 → 渲染 → 注入模板受控区块,Init 期缓存。
小结
| 要点 | 结论 |
|---|---|
| 定位 | CommonMark 解析 + HTML 渲染,2 符号全 API |
| 事件流 | md_parse 零分配回调式,自定义渲染的地基 |
| 一步渲染 | md_html 内建回调 + flag 方言 + 默认逃逸 |
| 组合 | Markdown 进模板受控区块;静态内容 Init 期渲染 |
| 硬约束 | 回调结构必须完整;返回非零即中止 |
- xrt 参考 · template — 受控区块的组合对象