Biblioteca Java para análise e renderização de texto Markdown de acordo com a especificação CommonMark (e algumas extensões).
Fornece classes para analisar a entrada em uma árvore de sintaxe abstrata (AST), visitar e manipular nós e renderizar em HTML ou voltar para Markdown. Começou como uma versão do commonmark.js, mas desde então evoluiu para uma biblioteca extensível com os seguintes recursos:
Pequeno (núcleo não tem dependências, extensões em artefatos separados)
Rápido (10 a 20 vezes mais rápido que o pegdown, que costumava ser uma biblioteca Markdown popular, consulte os benchmarks no repositório)
Flexível (manipule o AST após a análise, personalize a renderização HTML)
Extensível (tabelas, tachado, link automático e muito mais, veja abaixo)
A biblioteca é compatível com Java 11 e posterior. Também funciona no Android, mas com base no melhor esforço. Por favor, relate problemas. Para Android, o nível mínimo de API é 19, consulte o diretório commonmark-android-test.
Coordenadas para a biblioteca principal (veja tudo no Maven Central):
<dependência> <groupId>org.commonmark</groupId> <artifactId>marca comum</artifactId> <versão>0.24.0</versão> </dependency>
Os nomes dos módulos a serem usados no Java 9 são org.commonmark
, org.commonmark.ext.autolink
, etc, correspondendo aos nomes dos pacotes.
Observe que para versões 0.x desta biblioteca, a API ainda não é considerada estável e pode falhar entre versões secundárias. Após 1.0, o versionamento semântico será seguido. Um pacote contendo beta
significa que ainda não está sujeito a garantias de API estável; mas para uso normal não deve ser necessário usar.
Consulte o arquivo spec.txt se estiver se perguntando qual versão da especificação está implementada atualmente. Verifique também o CommonMark dingus para se familiarizar com a sintaxe ou experimentar casos extremos. Se você clonar o repositório, também poderá usar a classe DingusApp
para experimentar coisas de forma interativa.
importar org.commonmark.node.*;importar org.commonmark.parser.Parser;importar org.commonmark.renderer.html.HtmlRenderer;Parser analisador = Parser.builder().build();Node document = parser.parse(" Este é *Markdown*");HtmlRenderer renderizador = HtmlRenderer.builder().build();renderer.render(documento); // "<p>Este é o <em>Markdown</em></p>n"
Isso usa o analisador e o renderizador com opções padrão. Ambos os construtores possuem métodos para configurar seu comportamento:
escapeHtml(true)
em HtmlRenderer
escapará de tags e blocos HTML brutos.
sanitizeUrls(true)
no HtmlRenderer
removerá URLs potencialmente inseguros das tags <a>
e <img>
Para todas as opções disponíveis, consulte os métodos nos construtores.
Observe que esta biblioteca não tenta higienizar o HTML resultante em relação a quais tags são permitidas, etc. Isso é responsabilidade do chamador, e se você expor o HTML resultante, provavelmente desejará executar um higienizador nele depois disso .
import org.commonmark.node.*;importar org.commonmark.renderer.markdown.MarkdownRenderer;MarkdownRenderer renderer = MarkdownRenderer.builder().build();Documento do nó = novo Documento();Título título = novo Título();título .setLevel(2);heading.appendChild(new Text("Meu título"));document.appendChild(título);renderer.render(documento); // "## Meu títulon"
Para renderização em texto simples com marcação mínima, há também TextContentRenderer
.
Após a análise do texto fonte, o resultado é uma árvore de nós. Essa árvore pode ser modificada antes da renderização ou apenas inspecionada sem renderizar:
Node node = parser.parse("Exemplon=======nnMais algum texto");WordCountVisitor visitante = new WordCountVisitor();node.accept(visitor);visitor.wordCount; // 4class WordCountVisitor estende AbstractVisitor { int wordCount = 0; @Override public void visit(Text text) { // Isso é chamado para todos os nós de texto. Substitua outros métodos de visita para outros tipos de nós. // Contar palavras (este é apenas um exemplo, na verdade não faça desta forma por vários motivos). contagem de palavras += text.getLiteral().split("\W+").length; // Desce até os filhos (pode ser omitido neste caso porque os nós de texto não têm filhos). visitarCrianças(texto); } }
Se quiser saber onde um Node
analisado apareceu no texto de origem de entrada, você pode solicitar ao analisador que retorne as posições de origem como esta:
var analisador = Parser.builder().includeSourceSpans(IncludeSourceSpans.BLOCKS_AND_INLINES).build();
Em seguida, analise os nós e inspecione as posições de origem:
var fonte = "foonnbar *baz*";var doc = parser.parse(fonte);var ênfase = doc.getLastChild().getLastChild();var s = ênfase.getSourceSpans().get(0) ;s.getLineIndex(); // 2 (terceira linha)s.getColumnIndex(); // 4 (quinta coluna)s.getInputIndex(); // 9 (índice de string 9)s.getLength(); // 5source.substring(s.getInputIndex(), s.getInputIndex() + s.getLength()); // "*baz*"
Se você estiver interessado apenas em blocos e não em linha, use IncludeSourceSpans.BLOCKS
.
Às vezes você pode querer personalizar como o HTML é renderizado. Se tudo o que você deseja fazer é adicionar ou alterar atributos em alguns elementos, existe uma maneira simples de fazer isso.
Neste exemplo, registramos uma fábrica para um AttributeProvider
no renderizador para definir um atributo class="border"
em elementos img
.
Analisador analisador = Parser.builder().build();HtmlRenderer renderizador = 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 implementa AttributeProvider { @Override public void setAttributes(Node node, String tagName, Map<String, String> atributos) { if (node instanceof Image) { atributos.put("class", "border"); } } }
Se você quiser fazer mais do que apenas alterar atributos, também existe uma maneira de assumir controle total sobre como o HTML é renderizado.
Neste exemplo, estamos alterando a renderização de blocos de código recuados para envolvê-los apenas em pre
em vez de pre
e code
:
Analisador analisador = Parser.builder().build();HtmlRenderer renderizador = HtmlRenderer.builder() .nodeRendererFactory(new HtmlNodeRendererFactory() { public NodeRenderer create(HtmlNodeRendererContext context) { return new IndentedCodeBlockNodeRenderer(context); } }) .build();Node document = parser.parse("Exemplo:nn código");renderer.render(document);// "<p>Exemplo:</p>n<pre>códigon </pre>n"class IndentedCodeBlockNodeRenderer implements NodeRenderer { private final HtmlWriter html; IndentedCodeBlockNodeRenderer (contexto HtmlNodeRendererContext) { this.html = context.getWriter (); } @Override public Set<Class<? estende Node>> getNodeTypes() { // Retorna os tipos de nós para os quais queremos usar este renderizador. retornar Set.of(IndentedCodeBlock.class); } @Override public void render(Node node) { // Lidamos apenas com um tipo conforme getNodeTypes, então podemos lançá-lo aqui. IndentedCodeBlock codeBlock = (IndentedCodeBlock) nó; html.line(); html.tag("pré"); html.text(codeBlock.getLiteral()); html.tag("/pré"); html.line(); } }
Caso queira armazenar dados adicionais no documento ou ter elementos customizados no HTML resultante, você pode criar sua própria subclasse de CustomNode
e adicionar instâncias como nós filhos aos nós existentes.
Para definir a renderização HTML para eles, você pode usar um NodeRenderer
conforme explicado acima.
Existem algumas maneiras de estender a análise ou até mesmo substituir a análise integrada, todas elas por meio de métodos em Parser.Builder
(consulte Blocos e inlines nas especificações para uma visão geral de blocos/inlines):
A análise de tipos de blocos específicos (por exemplo, títulos, blocos de código, etc.) pode ser ativada/desativada com enabledBlockTypes
A análise de blocos pode ser estendida/substituída com customBlockParserFactory
A análise do conteúdo embutido pode ser estendida/substituída com customInlineContentParserFactory
A análise de delimitadores em conteúdo embutido pode ser estendida com customDelimiterProcessor
O processamento de links pode ser personalizado com linkProcessor
e linkMarker
Tanto o Parser
quanto HtmlRenderer
são projetados para que você possa configurá-los uma vez usando os construtores e depois usá-los várias vezes/a partir de vários threads. Isso é feito separando o estado para análise/renderização da configuração.
Dito isto, pode haver bugs, é claro. Se você encontrar um, relate um problema.
Javadocs estão disponíveis online em javadoc.io.
As extensões precisam estender o analisador, ou o renderizador HTML, ou ambos. Para usar uma extensão, os objetos construtores podem ser configurados com uma lista de extensões. Como as extensões são opcionais, elas residem em artefatos separados, portanto, dependências adicionais também precisam ser adicionadas.
Vejamos como habilitar tabelas do GitHub Flavored Markdown. Primeiro, adicione uma dependência adicional (consulte Maven Central para outras):
<dependência> <groupId>org.commonmark</groupId> <artifactId>commonmark-ext-gfm-tables</artifactId> <versão>0.24.0</versão> </dependency>
Em seguida, configure a extensão nos construtores:
import org.commonmark.ext.gfm.tables.TablesExtension;List<Extension> extensions = List.of(TablesExtension.create());Parser parser = Parser.builder() .extensões(extensões) .build();HtmlRenderer renderizador = HtmlRenderer.builder() .extensões(extensões) .construir();
Para configurar outra extensão no exemplo acima, basta adicioná-la à lista.
As seguintes extensões são desenvolvidas com esta biblioteca, cada uma em seu próprio artefato.
Transforma links simples, como URLs e endereços de e-mail, em links (com base em autolink-java).
Use a classe AutolinkExtension
do artefato commonmark-ext-autolink
.
Ativa o tachado do texto colocando-o entre ~~
. Por exemplo, em hey ~~you~~
, you
será renderizado como texto tachado.
Use a classe StrikethroughExtension
no artefato commonmark-ext-gfm-strikethrough
.
Habilita tabelas usando pipes como no GitHub Flavored Markdown.
Use a classe TablesExtension
no artefato commonmark-ext-gfm-tables
.
Permite notas de rodapé como no GitHub ou Pandoc:
Main text[^1] [^1]: Additional text in a footnote
Notas de rodapé embutidas como ^[inline footnote]
também são suportadas quando habilitadas por meio de FootnotesExtension.Builder#inlineFootnotes
.
Use a classe FootnotesExtension
no artefato commonmark-ext-footnotes
.
Permite adicionar atributos "id" gerados automaticamente às tags de título. O "id" é baseado no texto do título.
# Heading
será renderizado como:
<h1 id="heading">Heading</h1>
Use a classe HeadingAnchorExtension
no artefato commonmark-ext-heading-anchor
.
Caso você queira uma renderização personalizada do título, você pode usar a classe IdGenerator
diretamente junto com um HtmlNodeRendererFactory
(veja o exemplo acima).
Ativa o sublinhado do texto colocando-o entre ++
. Por exemplo, em hey ++you++
, you
será renderizado como texto sublinhado. Usa a tag <ins>.
Use a classe InsExtension
no artefato commonmark-ext-ins
.
Adiciona suporte para metadados por meio de um bloco frontal YAML. Esta extensão oferece suporte apenas a um subconjunto de sintaxe YAML. Aqui está um exemplo do que é compatível:
--- key: value list: - value 1 - value 2 literal: | this is literal value. literal values 2 --- document start here
Use a classe YamlFrontMatterExtension
no artefato commonmark-ext-yaml-front-matter
. Para buscar metadados, use YamlFrontMatterVisitor
.
Adiciona suporte para especificação de atributos (especificamente altura e largura) para imagens.
Os elementos do atributo são fornecidos como pares key=value
entre chaves { }
após o nó da imagem ao qual eles se aplicam, por exemplo:
![text](/url.png){width=640 height=480}
será renderizado como:
<img src="/url.png" alt="text" width="640" height="480" />
Use a classe ImageAttributesExtension
no artefato commonmark-ext-image-attributes
.
Nota: como esta extensão usa chaves {
}
como delimitadores (em StylesDelimiterProcessor
), isso significa que outros processadores delimitadores não podem usar chaves para delimitar.
Adiciona suporte para tarefas como itens de lista.
Uma tarefa pode ser representada como um item de lista onde o primeiro caractere que não seja um espaço em branco é um colchete esquerdo [
, depois um único caractere de espaço em branco ou a letra x
em minúscula ou maiúscula, depois um colchete direito ]
seguido por pelo menos um espaço em branco antes de qualquer outro contente.
Por exemplo:
- [ ] task #1 - [x] task #2
será renderizado como:
<ul> <li><input type="checkbox" disabled=""> task #1</li> <li><input type="checkbox" disabled="" checked=""> task #2</li> </ul>
Use a classe TaskListItemsExtension
no artefato commonmark-ext-task-list-items
.
Você também pode encontrar outras extensões disponíveis:
commonmark-ext-notifications: esta extensão permite criar facilmente parágrafos de notificações/advertências como INFO
, SUCCESS
, WARNING
ou ERROR
Alguns usuários desta biblioteca (sinta-se à vontade para levantar um PR se quiser ser adicionado):
Atlassian (onde a biblioteca foi desenvolvida inicialmente)
Java (OpenJDK) (link)
Revisão do código Gerrit/Giles (link)
Programação ao vivo moldável de funcionário para Clojure
Znai
Markwon: biblioteca Android para renderizar markdown como Spannables nativos do sistema
flexmark-java: Fork que adicionou suporte para muito mais sintaxe e flexibilidade
Consulte o arquivo CONTRIBUTING.md.
Direitos autorais (c) 2015, Robin Stocker
Licenciado BSD (2 cláusulas), consulte o arquivo LICENSE.txt.