用于根据 CommonMark 规范(和一些扩展)解析和渲染 Markdown 文本的 Java 库。
提供用于解析抽象语法树 (AST) 的输入、访问和操作节点以及渲染为 HTML 或返回为 Markdown 的类。它最初是 commonmark.js 的一个端口,但后来发展成为一个具有以下功能的可扩展库:
小(核心没有依赖项,扩展在单独的工件中)
快速(比过去流行的 Markdown 库 pegdown 快 10-20 倍,请参阅 repo 中的基准测试)
灵活(解析后操作AST,自定义HTML渲染)
可扩展(表格、删除线、自动链接等,见下文)
Java 11 及更高版本支持该库。它也适用于 Android,但这是在尽最大努力的基础上,请报告问题。对于 Android,最低 API 级别为 19,请参阅 commonmark-android-test 目录。
核心库的坐标(在 Maven Central 上查看所有内容):
<依赖关系> <groupId>org.commonmark</groupId> <artifactId>通用标记</artifactId> <版本>0.24.0</版本> </依赖>
Java 9 中使用的模块名称是org.commonmark
、 org.commonmark.ext.autolink
等,对应于包名称。
请注意,对于此库的 0.x 版本,该 API 尚未被视为稳定,并且可能会在次要版本之间中断。 1.0之后,将遵循语义版本控制。包含beta
的包意味着它还不受稳定的 API 保证;但对于正常使用来说,没有必要使用。
如果您想知道当前实现了哪个版本的规范,请参阅 spec.txt 文件。另请查看 CommonMark dingus 以熟悉语法或尝试边缘情况。如果您克隆存储库,您还可以使用DingusApp
类以交互方式进行尝试。
导入 org.commonmark.node.*;导入 org.commonmark.parser.Parser;导入 org.commonmark.renderer.html.HtmlRenderer;解析器解析器 = Parser.builder().build();节点文档 = parser.parse("这是 *Markdown*");HtmlRenderer 渲染器 = HtmlRenderer.builder().build();renderer.render(document); //“<p>这是<em>Markdown</em></p>n”
这使用带有默认选项的解析器和渲染器。两个构建器都有配置其行为的方法:
HtmlRenderer
上的escapeHtml(true)
将转义原始 HTML 标签和块。
HtmlRenderer
上的sanitizeUrls(true)
将从<a>
和<img>
标记中去除潜在不安全的 URL
对于所有可用选项,请参阅构建器上的方法。
请注意,该库不会尝试清理生成的 HTML(关于允许使用哪些标签等)。这是调用者的责任,如果您公开生成的 HTML,您可能希望在此之后对其运行清理程序。
导入 org.commonmark.node.*;导入 org.commonmark.renderer.markdown.MarkdownRenderer;MarkdownRenderer 渲染器 = MarkdownRenderer.builder().build();节点文档 = new Document();Heading 标题 = new Heading();heading .setLevel(2);heading.appendChild(new Text("我的标题"));document.appendChild(标题);renderer.render(文档); // "## 我的标题n"
为了以最少的标记渲染为纯文本,还有TextContentRenderer
。
解析源文本后,结果是一棵节点树。该树可以在渲染之前修改,或者只是在不渲染的情况下进行检查:
Node node = parser.parse("示例n========nn更多文本");WordCountVisitor guest = new WordCountVisitor();node.accept(visitor);visitor.wordCount; // 4class WordCountVisitor 扩展 AbstractVisitor { int wordCount = 0; @Override public void Visit(Text text) { // 为所有 Text 节点调用此方法。覆盖其他节点类型的其他访问方法。 // 计算单词数(这只是一个例子,由于各种原因实际上不要这样做)。 wordCount += text.getLiteral().split("\W+").length; // 下降到子节点(在这种情况下可以省略,因为文本节点没有子节点)。 访问儿童(文本); } }
如果您想知道已解析的Node
出现在输入源文本中的位置,您可以请求解析器返回源位置,如下所示:
var parser = Parser.builder().includeSourceSpans(IncludeSourceSpans.BLOCKS_AND_INLINES).build();
然后解析节点并检查源位置:
var source = "foonnbar *baz*";var doc = parser.parse(source);var 强调 = doc.getLastChild().getLastChild();var s = 强调.getSourceSpans().get(0) ;s.getLineIndex(); // 2(第三行)s.getColumnIndex(); // 4(第五列)s.getInputIndex(); // 9(字符串索引 9)s.getLength(); // 5source.substring(s.getInputIndex(), s.getInputIndex() + s.getLength()); //“*巴兹*”
如果您只对块而不是内联感兴趣,请使用IncludeSourceSpans.BLOCKS
。
有时您可能想要自定义 HTML 的呈现方式。如果您只想添加或更改某些元素的属性,有一个简单的方法可以做到这一点。
在此示例中,我们在渲染器上为AttributeProvider
注册一个工厂,以在img
元素上设置class="border"
属性。
Parser 解析器 = Parser.builder().build();HtmlRenderer 渲染器 = HtmlRenderer.builder() .attributeProviderFactory(new AttributeProviderFactory() { public AttributeProvider create(AttributeProviderContext context) { return new ImageAttributeProvider(); } } }) .build();Node document = parser.parse("![text](/url.png)");renderer.render(document);// "<p><img src="/url.png " alt="text" class="border" /></p>n"class ImageAttributeProvider 实现 AttributeProvider { @Override public void setAttributes(Node 节点, String tagName, Map<String, String> 属性) { if (节点实例图像) { attribute.put("class", "border"); } } }
如果您想要做的不仅仅是更改属性,还有一种方法可以完全控制 HTML 的呈现方式。
在此示例中,我们将更改缩进代码块的呈现,以仅将它们包装在pre
而不是pre
和code
中:
Parser 解析器 = Parser.builder().build();HtmlRenderer 渲染器 = HtmlRenderer.builder() .nodeRendererFactory(new HtmlNodeRendererFactory() { public NodeRenderer create(HtmlNodeRendererContext context) { return new IndentedCodeBlockNodeRenderer(context); } } }) .build();Node document = parser.parse("示例:nn 代码");renderer.render(document);// "<p>示例:</p>n<pre>代码n </pre>n"类 IndentedCodeBlockNodeRenderer 实现 NodeRenderer { private Final HtmlWriter html; IndentedCodeBlockNodeRenderer(HtmlNodeRendererContext context) { this.html = context.getWriter(); } @Override public Set<Class<? extends Node>> getNodeTypes() { // 返回我们想要使用此渲染器的节点类型。 返回 Set.of(IndentedCodeBlock.class); } @Override public void render(Node node) { // 我们只根据 getNodeTypes 处理一种类型,所以我们可以在这里强制转换它。 IndentedCodeBlock codeBlock = (IndentedCodeBlock) 节点; html.line(); html.tag("预"); html.text(codeBlock.getLiteral()); html.tag("/pre"); html.line(); } }
如果您想要在文档中存储其他数据或在生成的 HTML 中包含自定义元素,您可以创建自己的CustomNode
子类并将实例作为子节点添加到现有节点。
要为它们定义 HTML 渲染,您可以使用NodeRenderer
,如上所述。
有几种方法可以扩展解析甚至覆盖内置解析,所有这些方法都通过Parser.Builder
上的方法(有关块/内联的概述,请参阅规范中的块和内联):
可以使用enabledBlockTypes
启用/禁用特定块类型(例如标题、代码块等)的解析
可以使用customBlockParserFactory
扩展/覆盖块的解析
可以使用customInlineContentParserFactory
扩展/覆盖内联内容的解析
可以使用customDelimiterProcessor
扩展内联内容中分隔符的解析
可以使用linkProcessor
和linkMarker
自定义链接的处理
Parser
和HtmlRenderer
的设计使您可以使用构建器配置它们一次,然后多次/从多个线程使用它们。这是通过将解析/渲染的状态与配置分离来完成的。
话虽如此,当然可能存在错误。如果您发现,请报告问题。
Javadoc 可在 javadoc.io 上在线获取。
扩展需要扩展解析器或 HTML 渲染器,或两者兼而有之。要使用扩展,可以使用扩展列表来配置构建器对象。由于扩展是可选的,因此它们存在于单独的工件中,因此还需要添加其他依赖项。
让我们看看如何启用 GitHub Flavored Markdown 中的表。首先,添加一个额外的依赖项(有关其他内容,请参阅 Maven Central):
<依赖关系> <groupId>org.commonmark</groupId> <artifactId>commonmark-ext-gfm-tables</artifactId> <版本>0.24.0</版本> </依赖>
然后,在构建器上配置扩展:
导入 org.commonmark.ext.gfm.tables.TablesExtension;List<Extension> 扩展 = List.of(TablesExtension.create());解析器解析器 = Parser.builder() .扩展名(扩展名) .build();HtmlRenderer 渲染器 = HtmlRenderer.builder() .扩展名(扩展名) 。建造();
要配置上例中的另一个扩展,只需将其添加到列表中即可。
以下扩展是使用此库开发的,每个扩展都有自己的工件。
将 URL 和电子邮件地址等普通链接转换为链接(基于 autolink-java)。
使用工件commonmark-ext-autolink
中的类AutolinkExtension
。
通过将文本括在~~
中来启用文本删除线。例如,在hey ~~you~~
中, you
将呈现为带删除线的文本。
在工件commonmark-ext-gfm-strikethrough
中使用类StrikethroughExtension
。
启用使用管道的表,如 GitHub Flavored Markdown 中所示。
在工件commonmark-ext-gfm-tables
中使用类TablesExtension
。
启用脚注,如 GitHub 或 Pandoc 中的脚注:
Main text[^1] [^1]: Additional text in a footnote
通过FootnotesExtension.Builder#inlineFootnotes
启用时,还支持像^[inline footnote]
这样的内联脚注。
在工件commonmark-ext-footnotes
中使用类FootnotesExtension
。
允许将自动生成的“id”属性添加到标题标签。 “id”基于标题的文本。
# Heading
将呈现为:
<h1 id="heading">Heading</h1>
在工件commonmark-ext-heading-anchor
中使用类HeadingAnchorExtension
。
如果您想要自定义标题呈现,您可以直接将IdGenerator
类与HtmlNodeRendererFactory
一起使用(请参见上面的示例)。
通过将文本括在++
中来启用文本下划线。例如,在hey ++you++
中, you
将呈现为下划线文本。使用 <ins> 标签。
在工件commonmark-ext-ins
中使用类InsExtension
。
通过 YAML Front Matter 块添加对元数据的支持。此扩展仅支持 YAML 语法的子集。以下是支持的示例:
--- key: value list: - value 1 - value 2 literal: | this is literal value. literal values 2 --- document start here
在工件commonmark-ext-yaml-front-matter
中使用类YamlFrontMatterExtension
。要获取元数据,请使用YamlFrontMatterVisitor
。
添加对指定图像属性(特别是高度和宽度)的支持。
属性元素在它们所应用的图像节点之后以大括号{ }
内的key=value
对形式给出,例如:
![text](/url.png){width=640 height=480}
将呈现为:
<img src="/url.png" alt="text" width="640" height="480" />
在工件commonmark-ext-image-attributes
中使用类ImageAttributesExtension
。
注意:由于此扩展使用大括号{
}
作为其分隔符(在StylesDelimiterProcessor
中),这意味着其他分隔符处理器不能使用大括号进行分隔。
添加对任务作为列表项的支持。
任务可以表示为一个列表项,其中第一个非空白字符是左括号[
,然后是单个空白字符或小写或大写的字母x
,然后是右括号]
后跟至少一个空白字符,然后是任何其他空白字符内容。
例如:
- [ ] task #1 - [x] task #2
将呈现为:
<ul> <li><input type="checkbox" disabled=""> task #1</li> <li><input type="checkbox" disabled="" checked=""> task #2</li> </ul>
在工件commonmark-ext-task-list-items
中使用类TaskListItemsExtension
。
您还可以在野外找到其他扩展:
commonmark-ext-notifications:此扩展允许轻松创建通知/警告段落,例如INFO
、 SUCCESS
、 WARNING
或ERROR
该库的一些用户(如果您想添加,请随时提出 PR):
Atlassian(最初开发该库的地方)
Java(OpenJDK)(链接)
Gerrit 代码审查/Gtiles(链接)
Clojure 的 Clerk 可塑实时编程
兹奈
Markwon:用于将 Markdown 渲染为系统原生 Spannables 的 Android 库
flexmark-java:添加了对更多语法和灵活性的支持的分叉
请参阅 CONTRIBUTING.md 文件。
版权所有 (c) 2015,罗宾·斯托克
BSD(2-clause)许可,请参阅 LICENSE.txt 文件。