- 
                Notifications
    You must be signed in to change notification settings 
- Fork 296
Usage
- Building via Gradle
- Building with Android Studio
- Parse and render to HTML
- Use a visitor to process parsed nodes
- Customize HTML attributes via Attribute Provider
- Include Markdown and HTML File Content
- Render AST as Markdown with Formatting Options
- Render HTML to PDF
For Maven, add flexmark-all as a dependency which includes core and all modules to the
following sample:
<dependency>
    <groupId>com.vladsch.flexmark</groupId>
    <artifactId>flexmark-all</artifactId>
    <version>0.64.0</version>
</dependency>compile 'com.vladsch.flexmark:flexmark-all:0.64.0'Additional settings due to duplicate files:
packagingOptions {
    exclude 'META-INF/LICENSE-LGPL-2.1.txt'
    exclude 'META-INF/LICENSE-LGPL-3.txt'
    exclude 'META-INF/LICENSE-W3C-TEST'
}
Please use the code listed here with the understanding that it is probably a bit out of date. Use the links to get the most current version.
Source: ProfileEmulationFamilySamples.java
package com.vladsch.flexmark.samples;
import com.vladsch.flexmark.util.ast.Node;
import com.vladsch.flexmark.html.HtmlRenderer;
import com.vladsch.flexmark.parser.Parser;
import com.vladsch.flexmark.parser.ParserEmulationProfile;
import com.vladsch.flexmark.util.data.MutableDataHolder;
import com.vladsch.flexmark.util.data.MutableDataSet;
public class BasicSample {
    void commonMark() {
        Parser parser = Parser.builder().build();
        Node document = parser.parse("This is *Sparta*");
        HtmlRenderer renderer = HtmlRenderer.builder().build();
        renderer.render(document);  // "<p>This is <em>Sparta</em></p>\n"
    }
    void kramdown() {
        MutableDataSet options = new MutableDataSet();
        options.setFrom(ParserEmulationProfile.KRAMDOWN);
        options.set(Parser.EXTENSIONS, Arrays.asList(
                AbbreviationExtension.create(),
                DefinitionExtension.create(),
                FootnoteExtension.create(),
                TablesExtension.create(),
                TypographicExtension.create()
        ));
        Parser parser = Parser.builder(options).build();
        HtmlRenderer renderer = HtmlRenderer.builder(options).build();
        Node document = parser.parse("This is *Sparta*");
        renderer.render(document);  // "<p>This is <em>Sparta</em></p>\n"
    }
    void multiMarkdown() {
        MutableDataHolder options = new MutableDataSet();
        options.setFrom(ParserEmulationProfile.MULTI_MARKDOWN);
        Parser parser = Parser.builder(options).build();
        HtmlRenderer renderer = HtmlRenderer.builder(options).build();
        Node document = parser.parse("This is *Sparta*");
        renderer.render(document);  // "<p>This is <em>Sparta</em></p>\n"
    }
    void markdown() {
        MutableDataHolder options = new MutableDataSet();
        options.setFrom(ParserEmulationProfile.MARKDOWN);
        Parser parser = Parser.builder(options).build();
        HtmlRenderer renderer = HtmlRenderer.builder(options).build();
        Node document = parser.parse("This is *Sparta*");
        renderer.render(document);  // "<p>This is <em>Sparta</em></p>\n"
    }
}This uses the parser and renderer with default options. Both builders have methods for
configuring their behavior, e.g. calling escapeHtml(true) on HtmlRenderer will escape raw
HTML tags and blocks. For all available options, see methods on the builders.
Note that this library does not try to sanitize the resulting HTML; that is the responsibility of the caller.
Despite its name, commonmark is neither a superset nor a subset of other markdown flavors. Rather, it proposes a standard, unambiguous syntax specification for the original, "core" Markdown, thus effectively introducing yet another flavor. While flexmark is by default commonmark compliant, its parser can be tweaked in various ways. The sets of tweaks required to emulate the most commonly used markdown parsers around are available in flexmark as ParserEmulationProfiles.
As the name ParserEmulationProfile implies, it's only the parser that is adjusted to the
specific markdown flavor. Applying the profile does not add features beyond those available in
commonmark. If you want to use flexmark to fully emulate another markdown processor's behavior,
you have to adjust the parser and configure the flexmark extensions that provide the additional
features available in the parser that you want to emulate.
Source: VisitorSample.java
package com.vladsch.flexmark.samples;
import com.vladsch.flexmark.ast.Text;
import com.vladsch.flexmark.parser.Parser;
import com.vladsch.flexmark.util.ast.Node;
import com.vladsch.flexmark.util.ast.NodeVisitor;
import com.vladsch.flexmark.util.ast.VisitHandler;
@SuppressWarnings({ "unchecked", "WeakerAccess" })
public class VisitorSample {
    int wordCount;
    // example of visitor for a node or nodes, just add VisitHandlers<> to the list
    // any node type not handled by the visitor will default to visiting its children
    NodeVisitor visitor = new NodeVisitor(
            new VisitHandler<>(Text.class, this::visit)
    );
    public void visit(Text text) {
        // This is called for all Text nodes. Override other visit handlers for other node types.
        wordCount += text.getChars().unescape().split("\\W+").length;
        // Descending into children
        visitor.visitChildren(text);
        // Count words (this is just an example, don't actually do it this way for various reasons).
    }
    void countWords() {
        Parser parser = Parser.builder().build();
        Node document = parser.parse("Example\n=======\n\nSome more text");
        visitor.visit(document);
        System.out.println(wordCount);  // 4
    }
}Source: AttributeProviderSample.java
package com.vladsch.flexmark.samples;
import com.vladsch.flexmark.ast.AutoLink;
import com.vladsch.flexmark.util.ast.Node;
import com.vladsch.flexmark.ext.autolink.AutolinkExtension;
import com.vladsch.flexmark.html.AttributeProvider;
import com.vladsch.flexmark.html.AttributeProviderFactory;
import com.vladsch.flexmark.html.HtmlRenderer;
import com.vladsch.flexmark.html.IndependentAttributeProviderFactory;
import com.vladsch.flexmark.html.renderer.AttributablePart;
import com.vladsch.flexmark.html.renderer.NodeRendererContext;
import com.vladsch.flexmark.parser.Parser;
import com.vladsch.flexmark.util.html.Attributes;
import com.vladsch.flexmark.util.data.MutableDataHolder;
import com.vladsch.flexmark.util.data.MutableDataSet;import org.jetbrains.annotations.NotNull;
import java.util.Arrays;import java.util.Collections;
public class AttributeProviderSample {
    static class SampleExtension implements HtmlRenderer.HtmlRendererExtension {
        @Override
        public void rendererOptions(@NotNull final MutableDataHolder options) {
            // add any configuration settings to options you want to apply to everything, here
        }
        @Override
        public void extend(final HtmlRenderer.Builder rendererBuilder, @NotNull final String rendererType) {
            rendererBuilder.attributeProviderFactory(SampleAttributeProvider.Factory());
        }
        static SampleExtension create() {
            return new SampleExtension();
        }
    }
    static class SampleAttributeProvider implements AttributeProvider {
        @Override
        public void setAttributes(@NotNull final Node node, @NotNull final AttributablePart part, @NotNull final Attributes attributes) {
            if (node instanceof AutoLink && part == AttributablePart.LINK) {
                // Put info in custom attribute instead
                attributes.replaceValue("class", "my-autolink-class");
            }
        }
        static AttributeProviderFactory Factory() {
            return new IndependentAttributeProviderFactory() {
                @Override
                public AttributeProvider create(NodeRendererContext context) {
                    //noinspection ReturnOfInnerClass
                    return new SampleAttributeProvider();
                }
            };
        }
    }
    static String commonMark(String markdown) {
        MutableDataHolder options = new MutableDataSet();
        options.set(Parser.EXTENSIONS, Collections.singletonList(new Extension[] { AutolinkExtension.create(), SampleExtension.create() }));
        // change soft break to hard break
        options.set(HtmlRenderer.SOFT_BREAK, "<br/>");
        Parser parser = Parser.builder(options).build();
        Node document = parser.parse(markdown);
        HtmlRenderer renderer = HtmlRenderer.builder(options).build();
        final String html = renderer.render(document);
        return html;
    }
    public static void main(String[] args) {
        String html = commonMark("http://github.com/vsch/flexmark-java");
        System.out.println(html); // output: <p><a href="http://github.com/vsch/flexmark-java" class="my-autolink-class">http://github.com/vsch/flexmark-java</a></p>
        html = commonMark("hello\nworld");
        System.out.println(html); // output: <p>hello<br/>world</p>
    }
}Source: JekyllIncludeFileSample.java
Jekyll tag extension can be used to include content using the {% include file %} syntax.
Although the extension does not actually include the file since this operation is application
dependent, it does provide everything necessary to allow your application to easily add this
functionality.
In the code below, a hash map is used for file to file content mapping where you would include your own code to resolve the included file name to actual content and determine whether the file requires markdown conversion to HTML or should be included as is.
Markdown include sample is effectively:
- 
Included File test.md## Included Heading Included paragraph [ref]: http://example.com 
- 
Main Document http://github.com/vsch/flexmark-java [ref] {% include test.md %} 
- 
Rendered Html <p><a href="http://github.com/vsch/flexmark-java">http://github.com/vsch/flexmark-java</a></p> <p><a href="http://example.com">ref</a></p> <h2>Included Heading</h2> <p>Included paragraph</p> 
Raw content include sample is effectively:
- 
Included File test.html<p>some text</p> 
- 
Main Document http://github.com/vsch/flexmark-java [ref] {% include test.html %} 
- 
Rendered Html <p><a href="http://github.com/vsch/flexmark-java">http://github.com/vsch/flexmark-java</a></p> <p>[ref]</p> <p>some text</p> 
package com.vladsch.flexmark.samples;
import com.vladsch.flexmark.ext.autolink.AutolinkExtension;
import com.vladsch.flexmark.ext.jekyll.tag.JekyllTag;
import com.vladsch.flexmark.ext.jekyll.tag.JekyllTagExtension;
import com.vladsch.flexmark.html.HtmlRenderer;
import com.vladsch.flexmark.parser.Parser;
import com.vladsch.flexmark.util.ast.Document;
import com.vladsch.flexmark.util.data.MutableDataHolder;
import com.vladsch.flexmark.util.data.MutableDataSet;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class JekyllIncludeFileSample {
    static String commonMark(String markdown, Map<String, String> included) {
        MutableDataHolder options = new MutableDataSet();
        options.set(Parser.EXTENSIONS, Collections.singletonList(new Extension[] { AutolinkExtension.create(), JekyllTagExtension.create() }));
        // change soft break to hard break
        options.set(HtmlRenderer.SOFT_BREAK, "<br/>");
        Parser parser = Parser.builder(options).build();
        HtmlRenderer renderer = HtmlRenderer.builder(options).build();
        Document document = parser.parse(markdown);
        // see if document has includes
        Document doc = document;
        if (doc.contains(JekyllTagExtension.TAG_LIST)) {
            List<JekyllTag> tagList = JekyllTagExtension.TAG_LIST.get(doc);
            Map<String, String> includeHtmlMap = new HashMap<String, String>();
            for (JekyllTag tag : tagList) {
                String includeFile = tag.getParameters().toString();
                if (tag.getTag().equals("include") && !includeFile.isEmpty() && !includeHtmlMap.containsKey(includeFile)) {
                    // see if it exists
                    if (included.containsKey(includeFile)) {
                        // have the file
                        String text = included.get(includeFile);
                        if (includeFile.endsWith(".md")) {
                            Document includeDoc = parser.parse(text);
                            String includeHtml = renderer.render(includeDoc);
                            includeHtmlMap.put(includeFile, includeHtml);
                            // copy any definition of reference elements from included file to our document
                            parser.transferReferences(doc, includeDoc, null);
                        } else {
                            includeHtmlMap.put(includeFile, text);
                        }
                    }
                }
                if (!includeHtmlMap.isEmpty()) {
                    doc.set(JekyllTagExtension.INCLUDED_HTML, includeHtmlMap);
                }
            }
        }
        String html = renderer.render(document);
        return html;
    }
    public static void main(String[] args) {
        Map<String, String> included = new HashMap<>();
        included.put("test.md", "## Included Heading\n" +
                "\n" +
                "Included paragraph\n" +
                "\n" +
                "[ref]: http://example.com\n" +
                "");
        included.put("test.html", "<p>some text</p>\n" +
                "");
        String html = commonMark("http://github.com/vsch/flexmark-java\n" +
                "\n" +
                "[ref]\n" +
                "\n" +
                "{% include test.md %}\n" +
                "\n" +
                "", included);
        System.out.println(html);
        html = commonMark("http://github.com/vsch/flexmark-java\n" +
                "\n" +
                "[ref]\n" +
                "\n" +
                "{% include test.html %}\n" +
                "\n" +
                "", included);
        System.out.println(html);
    }
}Source: PegdownToCommonMark.java.
The flexmark-formatter module renders the AST as markdown with various formatting options to
clean up and make the source consistent. This also comes with an API to allow extensions to
provide formatting options and and handle rendering of markdown for custom nodes.
The Formatter class is a renderer that outputs markdown and formats it to specified options.
Use it in place of HtmlRenderer to get formatted markdown. It can also be used to convert
indentations from one ParserEmulationProfile to another:
package com.vladsch.flexmark.samples;
import com.vladsch.flexmark.util.ast.Node;
import com.vladsch.flexmark.formatter.Formatter;
import com.vladsch.flexmark.parser.Parser;
import com.vladsch.flexmark.profile.pegdown.Extensions;
import com.vladsch.flexmark.profile.pegdown.PegdownOptionsAdapter;
import com.vladsch.flexmark.util.data.DataHolder;
import com.vladsch.flexmark.util.data.MutableDataSet;
public class PegdownToCommonMark {
     final private static DataHolder OPTIONS = PegdownOptionsAdapter.flexmarkOptions(
            Extensions.ALL
    );
    static final MutableDataSet FORMAT_OPTIONS = new MutableDataSet();
    static {
        // copy extensions from Pegdown compatible to Formatting
        FORMAT_OPTIONS.set(Parser.EXTENSIONS, Parser.EXTENSIONS.get(OPTIONS));
    }
    static final Parser PARSER = Parser.builder(OPTIONS).build();
    static final Formatter RENDERER = Formatter.builder(FORMAT_OPTIONS).build();
    // use the PARSER to parse and RENDERER to parse pegdown indentation rules and render CommonMark
    public static void main(String[] args) {
        final String pegdown = "#Heading\n" +
                "-----\n" +
                "paragraph text \n" +
                "lazy continuation\n" +
                "\n" +
                "* list item\n" +
                "    > block quote\n" +
                "    lazy continuation\n" +
                "\n" +
                "~~~info\n" +
                "        with uneven indent\n" +
                "           with uneven indent\n" +
                "     indented code\n" +
                "~~~ \n" +
                "\n" +
                "        with uneven indent\n" +
                "           with uneven indent\n" +
                "     indented code\n" +
                "1. numbered item 1   \n" +
                "1. numbered item 2   \n" +
                "1. numbered item 3   \n" +
                "    - bullet item 1   \n" +
                "    - bullet item 2   \n" +
                "    - bullet item 3   \n" +
                "        1. numbered sub-item 1   \n" +
                "        1. numbered sub-item 2   \n" +
                "        1. numbered sub-item 3   \n" +
                "    \n" +
                "    ~~~info\n" +
                "            with uneven indent\n" +
                "               with uneven indent\n" +
                "         indented code\n" +
                "    ~~~ \n" +
                "    \n" +
                "            with uneven indent\n" +
                "               with uneven indent\n" +
                "         indented code\n" +
                "";
        System.out.println("pegdown\n");
        System.out.println(pegdown);
        Node document = PARSER.parse(pegdown);
        String commonmark = RENDERER.render(document);
        System.out.println("\n\nCommonMark\n");
        System.out.println(commonmark);
    }
}will convert pegdown 4 space indent to CommonMark list item text column indent.
#Heading
-----
paragraph text
lazy continuation
* list item
    > block quote
    lazy continuation
~~~info
        with uneven indent
           with uneven indent
     indented code
~~~
        with uneven indent
           with uneven indent
     indented code
1. numbered item 1
1. numbered item 2
1. numbered item 3
    - bullet item 1
    - bullet item 2
    - bullet item 3
        1. numbered sub-item 1
        1. numbered sub-item 2
        1. numbered sub-item 3
    ~~~info
            with uneven indent
               with uneven indent
         indented code
    ~~~
            with uneven indent
               with uneven indent
         indented code
Converted to CommonMark indents, ATX heading spaces added, blank lines added, fenced and indented code indents minimized:
# Heading
-----
paragraph text
lazy continuation
* list item
  > block quote
  > lazy continuation
~~~info
   with uneven indent
      with uneven indent
indented code
~~~
       with uneven indent
          with uneven indent
    indented code
1. numbered item 1
2. numbered item 2
3. numbered item 3
   - bullet item 1
   - bullet item 2
   - bullet item 3
     1. numbered sub-item 1
     2. numbered sub-item 2
     3. numbered sub-item 3
   ~~~info
      with uneven indent
         with uneven indent
   indented code
   ~~~
          with uneven indent
             with uneven indent
       indented code
Source: PdfConverter.java.
The flexmark-pdf-converter module renders HTML to PDF using
Open HTML To PDF
package com.vladsch.flexmark.samples;
import com.vladsch.flexmark.util.ast.Node;
import com.vladsch.flexmark.html.HtmlRenderer;
import com.vladsch.flexmark.parser.Parser;
import com.vladsch.flexmark.parent.PdfConverterExtension;
import com.vladsch.flexmark.profile.pegdown.Extensions;
import com.vladsch.flexmark.profile.pegdown.PegdownOptionsAdapter;
import com.vladsch.flexmark.util.data.DataHolder;
import org.apache.commons.io.IOUtils;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringWriter;
public class PdfConverter {
    static final DataHolder OPTIONS = PegdownOptionsAdapter.flexmarkOptions(
            Extensions.ALL & ~(Extensions.ANCHORLINKS | Extensions.EXTANCHORLINKS_WRAP)
    ).toImmutable();
    static String getResourceFileContent(String resourcePath) {
        StringWriter writer = new StringWriter();
        getResourceFileContent(writer, resourcePath);
        return writer.toString();
    }
    private static void getResourceFileContent(final StringWriter writer, final String resourcePath) {
        InputStream inputStream = com.vladsch.flexmark.java.samples.PdfConverter.class.getResourceAsStream(resourcePath);
        try {
            IOUtils.copy(inputStream, writer, "UTF-8");
            inputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    public static void main(String[] args) {
        final String pegdown = "#Heading\n" +
                "-----\n" +
                "paragraph text \n" +
                "lazy continuation\n" +
                "\n" +
                "* list item\n" +
                "    > block quote\n" +
                "    lazy continuation\n" +
                "\n" +
                "~~~info\n" +
                "   with uneven indent\n" +
                "      with uneven indent\n" +
                "indented code\n" +
                "~~~ \n" +
                "\n" +
                "       with uneven indent\n" +
                "          with uneven indent\n" +
                "    indented code\n" +
                "1. numbered item 1   \n" +
                "1. numbered item 2   \n" +
                "1. numbered item 3   \n" +
                "    - bullet item 1   \n" +
                "    - bullet item 2   \n" +
                "    - bullet item 3   \n" +
                "        1. numbered sub-item 1   \n" +
                "        1. numbered sub-item 2   \n" +
                "        1. numbered sub-item 3   \n" +
                "    \n" +
                "    ~~~info\n" +
                "       with uneven indent\n" +
                "          with uneven indent\n" +
                "    indented code\n" +
                "    ~~~ \n" +
                "    \n" +
                "           with uneven indent\n" +
                "              with uneven indent\n" +
                "        indented code\n" +
                "";
        System.out.println("pegdown\n");
        System.out.println(pegdown);
        final Parser PARSER = Parser.builder(OPTIONS).build();
        final HtmlRenderer RENDERER = HtmlRenderer.builder(OPTIONS).build();
        Node document = PARSER.parse(pegdown);
        String html = RENDERER.render(document);
        html = "<!DOCTYPE html><html><head><meta charset=\"UTF-8\">\n" +
         "\n" +  // add your stylesheets, scripts styles etc.
         "</head><body>" + html + "\n" +
         "</body></html>";
        PdfConverterExtension.exportToPdf("~/flexmark-java.pdf", html,"", OPTIONS);
    }
}#Heading
-----
paragraph text
lazy continuation
* list item
    > block quote
    lazy continuation
~~~info
   with uneven indent
      with uneven indent
indented code
~~~
       with uneven indent
          with uneven indent
    indented code
1. numbered item 1
1. numbered item 2
1. numbered item 3
    - bullet item 1
    - bullet item 2
    - bullet item 3
        1. numbered sub-item 1
        1. numbered sub-item 2
        1. numbered sub-item 3
    ~~~info
       with uneven indent
          with uneven indent
    indented code
    ~~~
           with uneven indent
              with uneven indent
        indented code

See PDF Renderer Converter for details on rendering non-latin character sets.