Библиотека Java для анализа и рендеринга текста Markdown в соответствии со спецификацией CommonMark (и некоторыми расширениями).
Предоставляет классы для анализа входных данных в абстрактное синтаксическое дерево (AST), посещения узлов и управления ими, а также рендеринга в HTML или обратно в Markdown. Она начиналась как порт commonmark.js, но с тех пор превратилась в расширяемую библиотеку со следующими функциями:
Маленький (ядро не имеет зависимостей, расширения в отдельных артефактах)
Быстро (в 10-20 раз быстрее, чем Pegdown, которая раньше была популярной библиотекой Markdown, см. тесты в репозитории)
Гибкость (управление 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
для интерактивного тестирования.
import org.commonmark.node.*;import org.commonmark.parser.Parser;import org.commonmark.renderer.html.HtmlRenderer;Parser parser = Parser.builder().build();Node document = parser.parse(" Это *Markdown*");HtmlRenderer рендерер = HtmlRenderer.builder().build();renderer.render(документ); // "<p>Это <em>Markdown</em></p>n"
При этом используются парсер и рендерер с параметрами по умолчанию. У обоих сборщиков есть методы настройки их поведения:
escapeHtml(true)
в HtmlRenderer
экранирует необработанные HTML-теги и блоки.
sanitizeUrls(true)
в HtmlRenderer
удалит потенциально небезопасные URL-адреса из тегов <a>
и <img>
.
Все доступные варианты см. в методах конструкторов.
Обратите внимание, что эта библиотека не пытается очистить полученный HTML-код в отношении разрешенных тегов и т. д. Это ответственность вызывающего объекта, и если вы предоставляете полученный HTML-код, вы, вероятно, захотите после этого запустить на нем дезинфицирующее средство. .
import org.commonmark.node.*;import org.commonmark.renderer.markdown.MarkdownRenderer;MarkdownRenderer renderer = MarkdownRenderer.builder().build();Node document = new Document();Heading heading = new Heading();heading .setLevel(2);heading.appendChild(new Text("Мой title"));document.appendChild(заголовок);renderer.render(документ); // "## Мой заголовокn"
Для рендеринга в обычный текст с минимальной разметкой также существует TextContentRenderer
.
После анализа исходного текста результатом является дерево узлов. Это дерево можно изменить перед рендерингом или просто просмотреть без рендеринга:
Node node = parser.parse("Примерn=======nnЕще текст");WordCountVisitor посетитель = новый WordCountVisitor();node.accept(visitor);visitor.wordCount; // 4класс WordCountVisitor расширяет AbstractVisitor { int wordCount = 0; @Override public void visit(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 focus = doc.getLastChild().getLastChild();var s = focus.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
в средстве визуализации, чтобы установить атрибут class="border"
для элементов img
.
Парсер парсер = Parser.builder().build();HtmlRenderer рендерер = HtmlRenderer.builder() .attributeProviderFactory(new AttributeProviderFactory() {public AttributeProvider create(AttributeProviderContext context) {вернуть новый 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 node, String tagName, Атрибуты Map<String, String>) { if (node instanceof Image) {attributes.put("class", "border"); } } }
Если вы хотите сделать больше, чем просто изменить атрибуты, есть также способ получить полный контроль над отображением HTML.
В этом примере мы изменяем рендеринг блоков кода с отступами, чтобы они заключались только в pre
вместо pre
и code
:
Парсер парсер = 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) { this.html = context.getWriter(); } @Override public Set<Class<? расширяет Node>> getNodeTypes() { // Возвращаем типы узлов, для которых мы хотим использовать этот рендерер. вернуть Set.of(IndentedCodeBlock.class); } @Override public void render(Node node) { // Мы обрабатываем только один тип согласно getNodeTypes, поэтому можем просто привести его сюда. IndentedCodeBlock codeBlock = (IndentedCodeBlock) узел; html.линия(); html.tag("предварительно"); html.text(codeBlock.getLiteral()); html.tag("/pre"); html.линия(); } }
Если вы хотите сохранить дополнительные данные в документе или иметь собственные элементы в результирующем HTML, вы можете создать свой собственный подкласс CustomNode
и добавить экземпляры в качестве дочерних узлов к существующим узлам.
Чтобы определить для них рендеринг HTML, вы можете использовать NodeRenderer
как описано выше.
Есть несколько способов расширить синтаксический анализ или даже переопределить встроенный синтаксический анализ, все они с помощью методов Parser.Builder
(обзор блоков/встроенных строк см. в спецификации):
Анализ определенных типов блоков (например, заголовков, блоков кода и т. д.) можно включить/отключить с помощью enabledBlockTypes
Анализ блоков можно расширить/переопределить с помощью customBlockParserFactory
Анализ встроенного контента можно расширить/переопределить с помощью customInlineContentParserFactory
Анализ разделителей во встроенном содержимом можно расширить с помощью customDelimiterProcessor
Обработку ссылок можно настроить с помощью linkProcessor
и linkMarker
И Parser
, и HtmlRenderer
спроектированы так, что вы можете настроить их один раз с помощью построителей, а затем использовать их несколько раз/из нескольких потоков. Это делается путем отделения состояния синтаксического анализа/рендеринга от конфигурации.
При этом, конечно, могут быть ошибки. Если вы найдете такой, пожалуйста, сообщите о проблеме.
Документация Java доступна онлайн на сайте javadoc.io.
Расширения должны расширять синтаксический анализатор или средство визуализации HTML, или и то, и другое. Чтобы использовать расширение, объекты компоновщика можно настроить со списком расширений. Поскольку расширения не являются обязательными, они находятся в отдельных артефактах, поэтому необходимо также добавить дополнительные зависимости.
Давайте посмотрим, как включить таблицы из GitHub Flavored Markdown. Сначала добавьте дополнительную зависимость (другие см. в Maven Central):
<зависимость> <groupId>org.commonmark</groupId> <artifactId>commonmark-ext-gfm-tables</artifactId> <версия>0.24.0</версия> </зависимость>
Затем настройте расширение на сборщиках:
import org.commonmark.ext.gfm.tables.TablesExtension;List<Extension> Extensions = List.of(TablesExtension.create());Парсер парсера = Parser.builder() .extensions(расширения) .build(); Средство рендеринга HtmlRenderer = HtmlRenderer.builder() .extensions(расширения) .строить();
Чтобы настроить другое расширение в приведенном выше примере, просто добавьте его в список.
С помощью этой библиотеки разработаны следующие расширения, каждое в своем собственном артефакте.
Превращает простые ссылки, такие как URL-адреса и адреса электронной почты, в ссылки (на основе autolink-java).
Используйте класс AutolinkExtension
из артефакта commonmark-ext-autolink
.
Включает зачеркивание текста, заключая его в ~~
. Например, в hey ~~you~~
» you
будете отображаться как зачеркнутый текст.
Используйте класс StrikethroughExtension
в артефакте commonmark-ext-gfm-strikethrough
.
Включает таблицы с использованием каналов, как в GitHub Flavored Markdown.
Используйте класс TablesExtension
в артефакте commonmark-ext-gfm-tables
.
Включает сноски, как в GitHub или Pandoc:
Main text[^1] [^1]: Additional text in a footnote
Встроенные сноски, такие как ^[inline footnote]
также поддерживаются, если они включены через FootnotesExtension.Builder#inlineFootnotes
.
Используйте класс FootnotesExtension
в артефакте commonmark-ext-footnotes
.
Позволяет добавлять автоматически сгенерированные атрибуты «id» к тегам заголовков. «Идентификатор» основан на тексте заголовка.
# Heading
будет отображаться как:
<h1 id="heading">Heading</h1>
Используйте класс HeadingAnchorExtension
в артефакте commonmark-ext-heading-anchor
.
Если вместо этого вам нужен собственный рендеринг заголовка, вы можете использовать класс IdGenerator
непосредственно вместе с HtmlNodeRendererFactory
(см. пример выше).
Включает подчеркивание текста, заключая его в ++
. Например, в hey ++you++
you
будете отображаться как подчеркнутый текст. Использует тег <ins>.
Используйте класс InsExtension
в артефакте commonmark-ext-ins
.
Добавляет поддержку метаданных через вводный блок YAML. Это расширение поддерживает только часть синтаксиса YAML. Вот пример того, что поддерживается:
--- key: value list: - value 1 - value 2 literal: | this is literal value. literal values 2 --- document start here
Используйте класс YamlFrontMatterExtension
в артефакте commonmark-ext-yaml-front-matter
. Чтобы получить метаданные, используйте YamlFrontMatterVisitor
.
Добавляет поддержку указания атрибутов (в частности, высоты и ширины) для изображений.
Элементы атрибута задаются в виде пар key=value
внутри фигурных скобок { }
после узла изображения, к которому они применяются, например:
![text](/url.png){width=640 height=480}
будет отображаться как:
<img src="/url.png" alt="text" width="640" height="480" />
Используйте класс ImageAttributesExtension
в артефакте commonmark-ext-image-attributes
.
Примечание. Поскольку это расширение использует фигурные скобки {
}
в качестве разделителей (в 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>
Используйте класс TaskListItemsExtension
в артефакте commonmark-ext-task-list-items
.
Вы также можете найти другие расширения:
commonmark-ext-notifications: это расширение позволяет легко создавать абзацы уведомлений/предупреждений, такие как INFO
, SUCCESS
, WARNING
или ERROR
Некоторые пользователи этой библиотеки (не стесняйтесь ставить пиар, если хотите, чтобы вас добавили):
Atlassian (где изначально была разработана библиотека)
Java (OpenJDK) (ссылка)
Обзор кода Gerrit/Giles (ссылка)
Формируемое живое программирование Clerk для Clojure
Знай
Markwon: библиотека Android для рендеринга уценки как системных Spannables.
flexmark-java: форк, который добавил поддержку гораздо большего синтаксиса и гибкости.
См. файл CONTRIBUTING.md.
Авторские права (c) 2015, Робин Стокер
Лицензия BSD (2 пункта), см. файл LICENSE.txt.