Java-Bibliothek zum Parsen und Rendern von Markdown-Text gemäß der CommonMark-Spezifikation (und einigen Erweiterungen).
Stellt Klassen zum Parsen von Eingaben in einen abstrakten Syntaxbaum (AST), zum Besuchen und Bearbeiten von Knoten sowie zum Rendern in HTML oder zurück in Markdown bereit. Es begann als Portierung von commonmark.js, hat sich aber seitdem zu einer erweiterbaren Bibliothek mit den folgenden Funktionen entwickelt:
Klein (Kern hat keine Abhängigkeiten, Erweiterungen in separaten Artefakten)
Schnell (10–20 Mal schneller als Pegdown, das früher eine beliebte Markdown-Bibliothek war, siehe Benchmarks im Repo)
Flexibel (AST nach dem Parsen manipulieren, HTML-Rendering anpassen)
Erweiterbar (Tabellen, Durchstreichen, automatische Verlinkung und mehr, siehe unten)
Die Bibliothek wird auf Java 11 und höher unterstützt. Es funktioniert auch auf Android, aber das erfolgt nach bestem Wissen und Gewissen. Bitte melden Sie Probleme. Für Android beträgt die minimale API-Stufe 19, siehe Verzeichnis commonmark-android-test.
Koordinaten für die Kernbibliothek (alles auf Maven Central sehen):
<Abhängigkeit> <groupId>org.commonmark</groupId> <artifactId>commonmark</artifactId> <version>0.24.0</version> </Abhängigkeit>
Die in Java 9 zu verwendenden Modulnamen sind org.commonmark
, org.commonmark.ext.autolink
usw. und entsprechen den Paketnamen.
Beachten Sie, dass die API für 0.x-Versionen dieser Bibliothek noch nicht als stabil gilt und zwischen Nebenversionen möglicherweise nicht mehr verfügbar ist. Nach 1.0 wird die semantische Versionierung befolgt. Ein Paket, das beta
enthält, bedeutet, dass es noch keinen stabilen API-Garantien unterliegt; Für den normalen Gebrauch sollte die Verwendung jedoch nicht erforderlich sein.
Sehen Sie sich die Datei spec.txt an, wenn Sie sich fragen, welche Version der Spezifikation derzeit implementiert ist. Schauen Sie sich auch den CommonMark-Dingus an, um sich mit der Syntax vertraut zu machen oder Randfälle auszuprobieren. Wenn Sie das Repository klonen, können Sie die DingusApp
-Klasse auch zum interaktiven Ausprobieren verwenden.
import org.commonmark.node.*;import org.commonmark.parser.Parser;import org.commonmark.renderer.html.HtmlRenderer;Parser parser = Parser.builder().build();Node document = parser.parse(" Dies ist *Markdown*");HtmlRenderer renderer = HtmlRenderer.builder().build();renderer.render(document); // "<p>Das ist <em>Markdown</em></p>n"
Dabei werden der Parser und der Renderer mit Standardoptionen verwendet. Beide Builder verfügen über Methoden zum Konfigurieren ihres Verhaltens:
escapeHtml(true)
auf HtmlRenderer
maskiert rohe HTML-Tags und -Blöcke.
sanitizeUrls(true)
auf HtmlRenderer
entfernt potenziell unsichere URLs aus den Tags <a>
und <img>
Alle verfügbaren Optionen finden Sie unter Methoden der Builder.
Beachten Sie, dass diese Bibliothek nicht versucht, den resultierenden HTML-Code im Hinblick darauf zu bereinigen, welche Tags zulässig sind usw. Das liegt in der Verantwortung des Aufrufers, und wenn Sie den resultierenden HTML-Code offenlegen, möchten Sie danach wahrscheinlich eine Bereinigung darauf ausführen .
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("My title"));document.appendChild(heading);renderer.render(document); // "## Mein Titeln"
Zum Rendern in einfachen Text mit minimalem Markup gibt es auch TextContentRenderer
.
Nachdem der Quelltext analysiert wurde, ist das Ergebnis ein Knotenbaum. Dieser Baum kann vor dem Rendern geändert oder einfach ohne Rendern überprüft werden:
Knoten node = parser.parse("Beispieln=======nnEtwas mehr Text");WordCountVisitor Visitors = new WordCountVisitor();node.accept(visitor);visitor.wordCount; // 4class WordCountVisitor erweitert AbstractVisitor { int wordCount = 0; @Override public void visit(Text text) { // Dies wird für alle Textknoten aufgerufen. Überschreiben Sie andere Besuchsmethoden für andere Knotentypen. // Wörter zählen (dies ist nur ein Beispiel, machen Sie es aus verschiedenen Gründen nicht so). wordCount += text.getLiteral().split("\W+").length; // In untergeordnete Knoten absteigen (könnte in diesem Fall weggelassen werden, da Textknoten keine untergeordneten Knoten haben). visitChildren(text); } }
Wenn Sie wissen möchten, wo ein geparster Node
im Eingabequelltext vorkam, können Sie den Parser auffordern, Quellpositionen wie folgt zurückzugeben:
var parser = Parser.builder().includeSourceSpans(IncludeSourceSpans.BLOCKS_AND_INLINES).build();
Analysieren Sie dann die Knoten und überprüfen Sie die Quellpositionen:
var source = "foonnbar *baz*";var doc = parser.parse(source);var yield = doc.getLastChild().getLastChild();var s = yield.getSourceSpans().get(0) ;s.getLineIndex(); // 2 (dritte Zeile)s.getColumnIndex(); // 4 (fünfte Spalte)s.getInputIndex(); // 9 (Stringindex 9)s.getLength(); // 5source.substring(s.getInputIndex(), s.getInputIndex() + s.getLength()); // "*baz*"
Wenn Sie nur an Blöcken und nicht an Inlines interessiert sind, verwenden Sie IncludeSourceSpans.BLOCKS
.
Manchmal möchten Sie vielleicht anpassen, wie HTML gerendert wird. Wenn Sie lediglich Attribute für einige Elemente hinzufügen oder ändern möchten, gibt es eine einfache Möglichkeit, dies zu tun.
In diesem Beispiel registrieren wir eine Factory für einen AttributeProvider
im Renderer, um ein class="border"
-Attribut für img
Elemente festzulegen.
Parser parser = Parser.builder().build();HtmlRenderer renderer = 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 implementiert AttributeProvider { @Override public void setAttributes(Node node, String tagName, Map<String, String> attributes) { if (node exampleof Image) { attributes.put("class", "border"); } } }
Wenn Sie mehr als nur Attribute ändern möchten, gibt es auch eine Möglichkeit, die vollständige Kontrolle darüber zu übernehmen, wie HTML gerendert wird.
In diesem Beispiel ändern wir die Darstellung eingerückter Codeblöcke so, dass sie nur in pre
statt in pre
und code
eingeschlossen werden:
Parser parser = Parser.builder().build();HtmlRenderer renderer = HtmlRenderer.builder() .nodeRendererFactory(new HtmlNodeRendererFactory() { public NodeRenderer create(HtmlNodeRendererContext context) { return new IndentedCodeBlockNodeRenderer(context); } }) .build();Node document = parser.parse("Beispiel:nn Code");renderer.render(document);// "<p>Beispiel:</p>n<pre>coden </pre>n"class IndentedCodeBlockNodeRenderer implementiert NodeRenderer { private final HtmlWriter html; IndentedCodeBlockNodeRenderer(HtmlNodeRendererContext context) { this.html = context.getWriter(); } @Override public Set<Class<? erweitert Node>> getNodeTypes() { // Gibt die Knotentypen zurück, für die wir diesen Renderer verwenden möchten. return Set.of(IndentedCodeBlock.class); } @Override public void render(Node node) { // Wir verarbeiten nur einen Typ gemäß getNodeTypes, daher können wir ihn einfach hier umwandeln. IndentedCodeBlock codeBlock = (IndentedCodeBlock) node; html.line(); html.tag("pre"); html.text(codeBlock.getLiteral()); html.tag("/pre"); html.line(); } }
Falls Sie zusätzliche Daten im Dokument speichern oder benutzerdefinierte Elemente im resultierenden HTML haben möchten, können Sie Ihre eigene Unterklasse von CustomNode
erstellen und Instanzen als untergeordnete Knoten zu vorhandenen Knoten hinzufügen.
Um das HTML-Rendering für sie zu definieren, können Sie wie oben erläutert einen NodeRenderer
verwenden.
Es gibt einige Möglichkeiten, das Parsing zu erweitern oder sogar das integrierte Parsing zu überschreiben, alle über Methoden auf Parser.Builder
(siehe Blöcke und Inlines in der Spezifikation für eine Übersicht über Blöcke/Inlines):
Das Parsen bestimmter Blocktypen (z. B. Überschriften, Codeblöcke usw.) kann mit enabledBlockTypes
aktiviert/deaktiviert werden
Das Parsen von Blöcken kann mit customBlockParserFactory
erweitert/überschrieben werden
Das Parsen von Inline-Inhalten kann mit customInlineContentParserFactory
erweitert/überschrieben werden
Das Parsen von Trennzeichen in Inline-Inhalten kann mit customDelimiterProcessor
erweitert werden
Die Verarbeitung von Links kann mit linkProcessor
und linkMarker
angepasst werden
Sowohl der Parser
als auch HtmlRenderer
sind so konzipiert, dass Sie sie einmal mit den Buildern konfigurieren und sie dann mehrmals/aus mehreren Threads verwenden können. Dies geschieht durch die Trennung des Status zum Parsen/Rendering von der Konfiguration.
Allerdings kann es natürlich zu Fehlern kommen. Wenn Sie eines finden, melden Sie bitte ein Problem.
Javadocs sind online auf javadoc.io verfügbar.
Erweiterungen müssen den Parser oder den HTML-Renderer oder beides erweitern. Um eine Erweiterung zu verwenden, können die Builder-Objekte mit einer Liste von Erweiterungen konfiguriert werden. Da Erweiterungen optional sind, befinden sie sich in separaten Artefakten, sodass auch zusätzliche Abhängigkeiten hinzugefügt werden müssen.
Sehen wir uns an, wie man Tabellen aus GitHub Flavored Markdown aktiviert. Fügen Sie zunächst eine zusätzliche Abhängigkeit hinzu (weitere Informationen finden Sie in Maven Central):
<Abhängigkeit> <groupId>org.commonmark</groupId> <artifactId>commonmark-ext-gfm-tables</artifactId> <version>0.24.0</version> </Abhängigkeit>
Konfigurieren Sie dann die Erweiterung auf den Buildern:
import org.commonmark.ext.gfm.tables.TablesExtension;List<Extension> extensions = List.of(TablesExtension.create());Parser parser = Parser.builder() .extensions(Erweiterungen) .build();HtmlRenderer renderer = HtmlRenderer.builder() .extensions(Erweiterungen) .bauen();
Um im obigen Beispiel eine weitere Erweiterung zu konfigurieren, fügen Sie diese einfach zur Liste hinzu.
Die folgenden Erweiterungen werden mit dieser Bibliothek entwickelt, jede in ihrem eigenen Artefakt.
Wandelt einfache Links wie URLs und E-Mail-Adressen in Links um (basierend auf Autolink-Java).
Verwenden Sie die Klasse AutolinkExtension
aus dem Artefakt commonmark-ext-autolink
.
Ermöglicht das Durchstreichen von Text durch Einschließen in ~~
. Beispielsweise wird you
in hey ~~you~~
als durchgestrichener Text dargestellt.
Verwenden Sie die Klasse StrikethroughExtension
im Artefakt commonmark-ext-gfm-strikethrough
.
Ermöglicht Tabellen mit Pipes wie in GitHub Flavored Markdown.
Verwenden Sie die Klasse TablesExtension
im Artefakt commonmark-ext-gfm-tables
.
Ermöglicht Fußnoten wie in GitHub oder Pandoc:
Main text[^1] [^1]: Additional text in a footnote
Inline-Fußnoten wie ^[inline footnote]
werden ebenfalls unterstützt, wenn sie über FootnotesExtension.Builder#inlineFootnotes
aktiviert werden.
Verwenden Sie die Klasse FootnotesExtension
im Artefakt commonmark-ext-footnotes
.
Ermöglicht das Hinzufügen automatisch generierter „id“-Attribute zu Überschriften-Tags. Die „id“ basiert auf dem Text der Überschrift.
# Heading
wird wie folgt gerendert:
<h1 id="heading">Heading</h1>
Verwenden Sie die Klasse HeadingAnchorExtension
im Artefakt commonmark-ext-heading-anchor
.
Falls Sie stattdessen eine benutzerdefinierte Darstellung der Überschrift wünschen, können Sie die IdGenerator
-Klasse direkt zusammen mit einer HtmlNodeRendererFactory
verwenden (siehe Beispiel oben).
Ermöglicht das Unterstreichen von Text durch Einschließen in ++
. Beispielsweise werden you
in hey ++you++
als unterstrichener Text dargestellt. Verwendet das <ins>-Tag.
Verwenden Sie die Klasse InsExtension
im Artefakt commonmark-ext-ins
.
Fügt Unterstützung für Metadaten über einen YAML-Frontmaterie-Block hinzu. Diese Erweiterung unterstützt nur eine Teilmenge der YAML-Syntax. Hier ist ein Beispiel dafür, was unterstützt wird:
--- key: value list: - value 1 - value 2 literal: | this is literal value. literal values 2 --- document start here
Verwenden Sie die Klasse YamlFrontMatterExtension
im Artefakt commonmark-ext-yaml-front-matter
. Verwenden Sie zum Abrufen von Metadaten YamlFrontMatterVisitor
.
Integriert Unterstützung für die Angabe von Attributen (insbesondere Höhe und Breite) für Bilder.
Die Attributelemente werden als key=value
-Paare in geschweiften Klammern { }
nach dem Bildknoten angegeben, für den sie gelten, zum Beispiel:
![text](/url.png){width=640 height=480}
wird wie folgt wiedergegeben:
<img src="/url.png" alt="text" width="640" height="480" />
Verwenden Sie die Klasse ImageAttributesExtension
im Artefakt commonmark-ext-image-attributes
.
Hinweis: Da diese Erweiterung geschweifte Klammern {
}
als Trennzeichen verwendet (in StylesDelimiterProcessor
), bedeutet dies, dass andere Trennzeichenprozessoren keine geschweiften Klammern zum Trennen verwenden können .
Fügt Unterstützung für Aufgaben als Listenelemente hinzu.
Eine Aufgabe kann als Listenelement dargestellt werden, bei dem das erste Nicht-Leerzeichen eine linke Klammer [
, dann ein einzelnes Leerzeichen oder der Buchstabe x
in Klein- oder Großbuchstaben, dann eine rechte Klammer ]
ist, gefolgt von mindestens einem Leerzeichen vor allen anderen Inhalt.
Zum Beispiel:
- [ ] task #1 - [x] task #2
wird wie folgt wiedergegeben:
<ul> <li><input type="checkbox" disabled=""> task #1</li> <li><input type="checkbox" disabled="" checked=""> task #2</li> </ul>
Verwenden Sie die Klasse TaskListItemsExtension
im Artefakt commonmark-ext-task-list-items
.
Sie können auch andere Erweiterungen in freier Wildbahn finden:
commonmark-ext-notifications: Diese Erweiterung ermöglicht das einfache Erstellen von Benachrichtigungs-/Ermahnungsabsätzen wie INFO
, SUCCESS
, WARNING
oder ERROR
Einige Benutzer dieser Bibliothek (Sie können gerne eine PR erstellen, wenn Sie hinzugefügt werden möchten):
Atlassian (wo die Bibliothek ursprünglich entwickelt wurde)
Java (OpenJDK) (Link)
Gerrit-Codeüberprüfung/Gitiles (Link)
Sachbearbeiter für formbare Live-Programmierung für Clojure
Znai
Markwon: Android-Bibliothek zum Rendern von Markdown als systemnative Spannables
flexmark-java: Fork, der viel mehr Syntax und Flexibilität unterstützt
Siehe Datei CONTRIBUTING.md.
Copyright (c) 2015, Robin Stocker
BSD (2-Klausel) lizenziert, siehe Datei LICENSE.txt.