MarkdownRenderService.java

package info.textgrid.rep.markdown;

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.List;

import org.apache.commons.io.IOUtils;
import org.apache.commons.io.input.BOMInputStream;
import org.commonmark.Extension;
import org.commonmark.ext.gfm.tables.TablesExtension;
import org.commonmark.ext.heading.anchor.HeadingAnchorExtension;
import org.commonmark.node.Node;
import org.commonmark.parser.Parser;
import org.commonmark.renderer.html.HtmlRenderer;

import jakarta.enterprise.context.ApplicationScoped;

@ApplicationScoped
public class MarkdownRenderService {

  // renderer and parser are meant to be threadsafe
  // https://github.com/atlassian/commonmark-java#thread-safety
  private static Parser parser;
  private static HtmlRenderer renderer;

  public MarkdownRenderService() {

    List<Extension> extensions = Arrays.asList(
        HeadingAnchorExtension.create(),
        TablesExtension.create());
    parser = Parser.builder()
        .extensions(extensions).build();
    renderer = HtmlRenderer.builder()
        .extensions(extensions).build();
  }

  /**
   * Render html from provided markdown
   *
   * @param in
   * @return
   * @throws IOException
   */
  public String renderHtml(InputStream in) throws IOException {
    // remove UTF8-BOM
    BOMInputStream bomIn = BOMInputStream.builder().setInputStream(in).get();
    InputStreamReader reader = new InputStreamReader(bomIn);
    Node document = parser.parseReader(reader);
    return renderer.render(document);
  }

  /**
   * Render html from provided markdown
   *
   * @param in
   * @return
   * @throws IOException
   */
  public String renderHtml(String markdown) throws IOException {
    InputStream in = IOUtils.toInputStream(markdown, "UTF-8");
    return renderHtml(in);
  }

  /**
   * Render full html and also the toc for provided markdown
   * This method reuses already parsed node for rendering both outputs in one record.
   *
   * @param in
   * @return
   * @throws IOException
   */
  public HtmlAndToc renderHtmlAndToc(InputStream in) throws IOException {
    // remove UTF8-BOM
    BOMInputStream bomIn = BOMInputStream.builder().setInputStream(in).get();
    InputStreamReader reader = new InputStreamReader(bomIn);
    Node document = parser.parseReader(reader);
    String markdown = renderer.render(document);
    String toc = new TocRenderer().renderTocFromNode(document);
    return new HtmlAndToc(markdown, toc);
  }

  /**
   * Render full html and also the toc for provided markdown
   * This method reuses already parsed node for rendering both outputs in one record.
   *
   * @param in
   * @return
   * @throws IOException
   */
  public HtmlAndToc renderHtmlAndToc(String markdown)  throws IOException {
    InputStream in = IOUtils.toInputStream(markdown, "UTF-8");
    return renderHtmlAndToc(in);
  }

}