1. 简介
最近因为项目需求需要将AI输出的结果导出到word中, 但AI输出的格式为markdown格式,因为word展示内容的时候需要有相应的格式(标题, 段落, 列表, 表格等), 所以不能直接将markdown输出到word中, 否则word中展示的就是markdown纯文本了, 调研一番后发现如果想要word展示效果好一点的话需要分成两步
- 将markdown→html
- 将html→ooxml(Office Open XML) word内容,word元信息本身就是个xml)
所以本章先实现第一步 markdown → html, 使用的组件为flexmark
2. 环境信息
为了兼容更多的场景, 所以并没有用一些高版本的SDK, 信息如下3. Maven
- <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
- <modelVersion>4.0.0</modelVersion>
- <groupId>com.ldx</groupId>
- md2html</artifactId>
- <version>1.0-SNAPSHOT</version>
- <properties>
- <flexmark.version>0.60.2</flexmark.version>
- </properties>
- <dependencies>
- <dependency>
- <groupId>com.vladsch.flexmark</groupId>
- flexmark</artifactId>
- <version>${flexmark.version}</version>
- </dependency>
- <dependency>
- <groupId>com.vladsch.flexmark</groupId>
- flexmark-ext-tables</artifactId>
- <version>${flexmark.version}</version>
- </dependency>
- </dependencies>
- </project>
复制代码 4. Markdown转Html
- import com.vladsch.flexmark.html.HtmlRenderer;
- import com.vladsch.flexmark.parser.Parser;
- import com.vladsch.flexmark.util.ast.Node;
- import com.vladsch.flexmark.util.data.MutableDataSet;
- public class MarkdownToHtml {
- public static String convertMarkdownToHtml(String markdown) {
- // 创建配置集
- MutableDataSet options = new MutableDataSet();
- // 创建解析器和渲染器
- Parser parser = Parser.builder(options).build();
- HtmlRenderer renderer = HtmlRenderer.builder(options).build();
- // 解析 Markdown 文本
- Node document = parser.parse(markdown);
- // 渲染为 HTML
- return renderer.render(document);
- }
- public static void main(String[] args) {
- String markdown = "## 嘉文四世\n" + "\n" + "> 德玛西亚\n" + "\n" + "**给我找些更强的敌人!**";
- final String html = convertMarkdownToHtml(markdown);
- System.out.println(html);
- }
- }
复制代码测试结果如下:
- <h2>嘉文四世</h2>
- <blockquote>
- <p>德玛西亚</p>
- </blockquote>
- <p><strong>给我找些更强的敌人!</strong></p>
复制代码 5. 高级用法
5.1 启用Table扩展
flexmark 支持多种扩展,需要通过 Extension 注册, 比如启用表格语法, flexmark默认没有启用表格语法比如测试- public static void main(String[] args) {
- String markdown = "| 列1 | 列2 |\n" + "| ----- | ----- |\n" + "| 数据1 | 数据2 |";
- final String html = convertMarkdownToHtml(markdown);
- System.out.println(html);
- }
复制代码测试结果如下:
- <p>| 列1 | 列2 |
- | ----- | ----- |
- | 数据1 | 数据2 |</p>
复制代码 没有将表格转换为html table标签, 所以需要启用表格扩展, 如下:- MutableDataSet options = new MutableDataSet();
- // 启用表格扩展,支持 Markdown 表格语法
- options.set(Parser.EXTENSIONS, Collections.singletonList(TablesExtension.create()));
- // 禁用跨列
- options.set(TablesExtension.COLUMN_SPANS, false);
- // 表头固定为 1 行
- options.set(TablesExtension.MIN_HEADER_ROWS, 1);
- options.set(TablesExtension.MAX_HEADER_ROWS, 1);
- // 自动补全缺失列、丢弃多余列
- options.set(TablesExtension.APPEND_MISSING_COLUMNS, true);
- options.set(TablesExtension.DISCARD_EXTRA_COLUMNS, true);
复制代码测试结果如下:
- <table>
- <thead>
- <tr><th>列1</th><th>列2</th></tr>
- </thead>
- <tbody>
- <tr><td>数据1</td><td>数据2</td></tr>
- </tbody>
- </table>
复制代码 5.2 标签属性扩展
flexmark支持对标签属性的操作, 需要实现其AttributeProviderFactory类, 比如给对应标签添加class属性, 如下:- HtmlRenderer renderer = HtmlRenderer.builder(options)
- .attributeProviderFactory(new IndependentAttributeProviderFactory() {
- @Override
- public @NotNull AttributeProvider apply(@NotNull LinkResolverContext context) {
- return (node, part, attributes) -> {
- // 标题
- if (node instanceof Heading) {
- Heading heading = (Heading) node;
- attributes.addValue("class", "heading" + heading.getLevel());
- }
- // 正文
- if (node instanceof Text) {
- attributes.addValue("class", "Normal");
- }
- // 段落
- if (node instanceof Paragraph) {
- attributes.addValue("class", "paragraph");
- }
- // 无序列表
- if (node instanceof BulletList) {
- attributes.addValue("class", "bulletList");
- }
- // 有序列表
- if (node instanceof OrderedList) {
- attributes.addValue("class", "bulletList");
- }
- // 表格
- if (node instanceof TableBlock) {
- attributes.addValue("class", "tableBlock");
- }
- };
- }
- })
- .build();
复制代码测试如下内容:
- public static void main(String[] args) {
- String markdown = "## 嘉文四世\n" + "\n" + "> 德玛西亚\n" + "\n" + "**给我找些更强的敌人!**\n" + "\n" + "| 列1 | 列2 |\n" + "| ----- | ----- |\n" + "| 数据1 | 数据2 |";
- final String html = convertMarkdownToHtml(markdown);
- System.out.println(html);
- }
复制代码测试结果如下:
- <h2>嘉文四世</h2>
- <blockquote>
- <p>德玛西亚</p>
- </blockquote>
- <p><strong>给我找些更强的敌人!</strong></p><table>
- <thead>
- <tr><th>列1</th><th>列2</th></tr>
- </thead>
- <tbody>
- <tr><td>数据1</td><td>数据2</td></tr>
- </tbody>
- </table>
复制代码 5.3 完善Html结构
上述的测试结果中输出的都是markdown语句翻译后的html代码块, 并不是一个完整的html页面内容, 比如要将结果输出成html文件并展示的话还需要html完整的骨架标签如:等, 这时候就需要使用jsoup进行优化
- 添加对应的坐标
- <jsoup.version>1.17.2</jsoup.version>
- <dependency>
- <groupId>org.jsoup</groupId>
- jsoup</artifactId>
- <version>${jsoup.version}</version>
- </dependency>
复制代码 - 完善html结构
- public static String wrapperHtml(String htmlContent) {
- org.jsoup.nodes.Document jsoupDoc = Jsoup.parse(htmlContent);
- jsoupDoc.outputSettings()
- // 内容输出时遵循XML语法规则
- .syntax(org.jsoup.nodes.Document.OutputSettings.Syntax.xml)
- // 内容转义时遵循xhtml规范
- .escapeMode(Entities.EscapeMode.xhtml)
- // 禁用格式化输出
- .prettyPrint(false);
- return jsoupDoc.html();
- }
- public static void main(String[] args) {
- String markdown = "## 嘉文四世\n" + "\n" + "> 德玛西亚\n" + "\n" + "**给我找些更强的敌人!**\n" + "\n" + "| 列1 | 列2 |\n" + "| ----- | ----- |\n" + "| 数据1 | 数据2 |";
- final String html = convertMarkdownToHtml(markdown);
- final String wrappedHtml = wrapperHtml(html);
- System.out.println(wrappedHtml);
- }
复制代码测试结果如下:
- <h2>嘉文四世</h2>
- <blockquote>
- <p>德玛西亚</p>
- </blockquote>
- <p><strong>给我找些更强的敌人!</strong></p><table>
- <thead>
- <tr><th>列1</th><th>列2</th></tr>
- </thead>
- <tbody>
- <tr><td>数据1</td><td>数据2</td></tr>
- </tbody>
- </table>
复制代码 6. 完整测试代码
- package md2html;
- import com.vladsch.flexmark.ast.BulletList;
- import com.vladsch.flexmark.ast.Heading;
- import com.vladsch.flexmark.ast.OrderedList;
- import com.vladsch.flexmark.ast.Paragraph;
- import com.vladsch.flexmark.ast.Text;
- import com.vladsch.flexmark.ext.tables.TableBlock;
- import com.vladsch.flexmark.ext.tables.TablesExtension;
- import com.vladsch.flexmark.html.AttributeProvider;
- import com.vladsch.flexmark.html.HtmlRenderer;
- import com.vladsch.flexmark.html.IndependentAttributeProviderFactory;
- import com.vladsch.flexmark.html.renderer.LinkResolverContext;
- import com.vladsch.flexmark.parser.Parser;
- import com.vladsch.flexmark.util.ast.Node;
- import com.vladsch.flexmark.util.data.MutableDataSet;
- import org.jetbrains.annotations.NotNull;
- import org.jsoup.Jsoup;
- import org.jsoup.nodes.Entities;
- import java.util.Collections;
- public class MarkdownToHtml {
- public static String convertMarkdownToHtml(String markdown) {
- // 创建配置集
- MutableDataSet options = new MutableDataSet();
- // 启用表格扩展,支持 Markdown 表格语法
- options.set(Parser.EXTENSIONS, Collections.singletonList(TablesExtension.create()));
- // 禁用跨列
- options.set(TablesExtension.COLUMN_SPANS, false);
- // 表头固定为 1 行
- options.set(TablesExtension.MIN_HEADER_ROWS, 1);
- options.set(TablesExtension.MAX_HEADER_ROWS, 1);
- // 自动补全缺失列、丢弃多余列
- options.set(TablesExtension.APPEND_MISSING_COLUMNS, true);
- options.set(TablesExtension.DISCARD_EXTRA_COLUMNS, true);
- // 创建解析器和渲染器
- Parser parser = Parser.builder(options)
- .build();
- HtmlRenderer renderer = HtmlRenderer.builder(options)
- .attributeProviderFactory(new IndependentAttributeProviderFactory() {
- @Override
- public @NotNull AttributeProvider apply(@NotNull LinkResolverContext context) {
- return (node, part, attributes) -> {
- // 标题
- if (node instanceof Heading) {
- Heading heading = (Heading) node;
- attributes.addValue("class", "heading" + heading.getLevel());
- }
- // 正文
- if (node instanceof Text) {
- attributes.addValue("class", "Normal");
- }
- // 段落
- if (node instanceof Paragraph) {
- attributes.addValue("class", "paragraph");
- }
- // 无序列表
- if (node instanceof BulletList) {
- attributes.addValue("class", "bulletList");
- }
- // 有序列表
- if (node instanceof OrderedList) {
- attributes.addValue("class", "bulletList");
- }
- // 表格
- if (node instanceof TableBlock) {
- attributes.addValue("class", "tableBlock");
- }
- };
- }
- })
- .build();
- // 解析 Markdown 文本
- Node document = parser.parse(markdown);
- // 渲染为 HTML
- return renderer.render(document);
- }
- public static String wrapperHtml(String htmlContent) {
- org.jsoup.nodes.Document jsoupDoc = Jsoup.parse(htmlContent);
- jsoupDoc.outputSettings()
- // 内容输出时遵循XML语法规则
- .syntax(org.jsoup.nodes.Document.OutputSettings.Syntax.xml)
- // 内容转义时遵循xhtml规范
- .escapeMode(Entities.EscapeMode.xhtml)
- // 禁用格式化输出
- .prettyPrint(false);
- return jsoupDoc.html();
- }
- public static void main(String[] args) {
- String markdown = "## 嘉文四世\n" + "\n" + "> 德玛西亚\n" + "\n" + "**给我找些更强的敌人!**\n" + "\n" + "| 列1 | 列2 |\n" + "| ----- | ----- |\n" + "| 数据1 | 数据2 |";
- final String html = convertMarkdownToHtml(markdown);
- final String wrappedHtml = wrapperHtml(html);
- System.out.println(wrappedHtml);
- }
- }
复制代码 7. 封装工具类
为了更方便的使用flexmark, 我将其常用的方法封装成链式调用的工具类, 内容如下:- import com.vladsch.flexmark.ast.BlockQuote;
- import com.vladsch.flexmark.ast.BulletList;
- import com.vladsch.flexmark.ast.Code;
- import com.vladsch.flexmark.ast.Emphasis;
- import com.vladsch.flexmark.ast.FencedCodeBlock;
- import com.vladsch.flexmark.ast.Heading;
- import com.vladsch.flexmark.ast.Image;
- import com.vladsch.flexmark.ast.IndentedCodeBlock;
- import com.vladsch.flexmark.ast.Link;
- import com.vladsch.flexmark.ast.ListItem;
- import com.vladsch.flexmark.ast.OrderedList;
- import com.vladsch.flexmark.ast.Paragraph;
- import com.vladsch.flexmark.ast.StrongEmphasis;
- import com.vladsch.flexmark.ast.ThematicBreak;
- import com.vladsch.flexmark.ext.tables.TableBlock;
- import com.vladsch.flexmark.ext.tables.TablesExtension;
- 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.LinkResolverContext;
- import com.vladsch.flexmark.parser.Parser;
- import com.vladsch.flexmark.util.ast.Document;
- import com.vladsch.flexmark.util.ast.Node;
- import com.vladsch.flexmark.util.data.MutableDataSet;
- import lombok.extern.slf4j.Slf4j;
- import org.jetbrains.annotations.NotNull;
- import org.jsoup.Jsoup;
- import org.jsoup.nodes.Entities;
- import java.io.BufferedReader;
- import java.io.File;
- import java.io.FileReader;
- import java.io.IOException;
- import java.io.InputStream;
- import java.io.InputStreamReader;
- import java.util.Collections;
- /**
- * markdown 工具类
- *
- * @author ludangxin
- * @since 2025/10/14
- */
- @Slf4j
- public class Markdowns {
- public static MarkdownBuilder builder(InputStream inputStream, String charset) {
- String markdownContent = readMarkdownContent(inputStream, charset);
- return builder(markdownContent);
- }
- public static MarkdownBuilder builder(InputStream inputStream) {
- String markdownContent = readMarkdownContent(inputStream);
- return builder(markdownContent);
- }
- public static MarkdownBuilder builder(File file) {
- String markdownContent = readMarkdownContent(file);
- return builder(markdownContent);
- }
- public static MarkdownBuilder builder(String markdownContent) {
- return new MarkdownBuilder().content(markdownContent);
- }
- public static String readMarkdownContent(File file) {
- if (file == null || !file.exists()) {
- return "";
- }
- try {
- return readMarkdownContent(new FileReader(file));
- }
- catch (Exception e) {
- log.error("failed to read markdown content", e);
- }
- return "";
- }
- public static String readMarkdownContent(InputStream inputStream) {
- try {
- return readMarkdownContent(new InputStreamReader(inputStream));
- }
- catch (Exception e) {
- log.error("failed to read markdown content", e);
- }
- return "";
- }
- public static String readMarkdownContent(InputStream inputStream, String charset) {
- if (charset == null || charset.isEmpty()) {
- return readMarkdownContent(new InputStreamReader(inputStream));
- }
- try {
- return readMarkdownContent(new InputStreamReader(inputStream, charset));
- }
- catch (Exception e) {
- log.error("failed to read markdown content", e);
- }
- return "";
- }
- public static String readMarkdownContent(InputStreamReader inputStreamReader) {
- try (BufferedReader reader = new BufferedReader(inputStreamReader)) {
- StringBuilder sb = new StringBuilder();
- String line;
- while ((line = reader.readLine()) != null) {
- sb.append(line);
- sb.append(System.lineSeparator());
- }
- return sb.toString();
- }
- catch (IOException e) {
- log.error("failed to read markdown content", e);
- }
- return "";
- }
- public static class MarkdownBuilder {
- private String content;
- private MutableDataSet options;
- private AttributeProviderFactory attributeProviderFactory;
- private AttributeProvider attributeProvider;
- private MarkdownBuilder content(String content) {
- this.content = content;
- return this;
- }
- public MarkdownBuilder options(MutableDataSet options) {
- this.options = options;
- return this;
- }
- public MarkdownBuilder attributeProviderFactory(AttributeProviderFactory attributeProviderFactory) {
- this.attributeProviderFactory = attributeProviderFactory;
- return this;
- }
- public MarkdownBuilder attributeProvider(AttributeProvider attributeProvider) {
- this.attributeProvider = attributeProvider;
- return this;
- }
- public MarkdownBuilder printContent() {
- System.out.println(content);
- return this;
- }
- public boolean isMarkdown() {
- if (content == null || content.trim()
- .isEmpty()) {
- return false;
- }
- final Document document = this.buildDocument();
- return hasMarkdownNodes(document);
- }
- public Document buildDocument() {
- Parser parser = Parser.builder(this.getOptionsOrDefault())
- .build();
- return parser.parse(content);
- }
- public String buildHtmlContent() {
- return this.wrapperHtml(this.getHtmlRenderer()
- .render(this.buildDocument()));
- }
- public String buildRawHtmlContent() {
- return this.getHtmlRenderer()
- .render(this.buildDocument());
- }
- public String buildRawHtmlIfMarkdown() {
- if (this.isMarkdown()) {
- return this.buildRawHtmlContent();
- }
- return content;
- }
- public String buildHtmlIfMarkdown() {
- if (this.isMarkdown()) {
- return this.buildHtmlContent();
- }
- return content;
- }
- private HtmlRenderer getHtmlRenderer() {
- final HtmlRenderer.Builder builder = HtmlRenderer.builder(getOptionsOrDefault());
- if (attributeProviderFactory != null) {
- builder.attributeProviderFactory(attributeProviderFactory);
- }
- if (attributeProviderFactory == null && attributeProvider != null) {
- final IndependentAttributeProviderFactory independentAttributeProviderFactory = new IndependentAttributeProviderFactory() {
- @Override
- public @NotNull AttributeProvider apply(@NotNull LinkResolverContext linkResolverContext) {
- return attributeProvider;
- }
- };
- builder.attributeProviderFactory(independentAttributeProviderFactory);
- }
- return builder.build();
- }
- private MutableDataSet getOptionsOrDefault() {
- if (options == null) {
- return this.defaultOptions();
- }
- else {
- return options;
- }
- }
- private MutableDataSet defaultOptions() {
- MutableDataSet options = new MutableDataSet();
- // 启用表格扩展,支持 Markdown 表格语法
- options.set(Parser.EXTENSIONS, Collections.singletonList(TablesExtension.create()));
- // 禁用跨列
- options.set(TablesExtension.COLUMN_SPANS, false);
- // 表头固定为 1 行
- options.set(TablesExtension.MIN_HEADER_ROWS, 1);
- options.set(TablesExtension.MAX_HEADER_ROWS, 1);
- // 自动补全缺失列、丢弃多余列
- options.set(TablesExtension.APPEND_MISSING_COLUMNS, true);
- options.set(TablesExtension.DISCARD_EXTRA_COLUMNS, true);
- return options;
- }
- private String wrapperHtml(String htmlContent) {
- org.jsoup.nodes.Document jsoupDoc = Jsoup.parse(htmlContent);
- jsoupDoc.outputSettings()
- // 内容输出时遵循XML语法规则
- .syntax(org.jsoup.nodes.Document.OutputSettings.Syntax.xml)
- // 内容转义时遵循xhtml规范
- .escapeMode(Entities.EscapeMode.xhtml)
- // 禁用格式化输出
- .prettyPrint(false);
- return jsoupDoc.html();
- }
- /**
- * 检查 AST 中是否存在 Markdown 特有节点(非纯文本段落)
- */
- private static boolean hasMarkdownNodes(Node node) {
- if (node == null) {
- return false;
- }
- // 判断当前节点是否为 Markdown 特有节点(非纯文本)
- if (isMarkdownSpecificNode(node)) {
- return true;
- }
- // 递归检查子节点
- Node child = node.getFirstChild();
- while (child != null) {
- if (hasMarkdownNodes(child)) {
- return true;
- }
- child = child.getNext();
- }
- return false;
- }
- /**
- * 判定节点是否为 Markdown 特有节点(非纯文本段落)
- * 纯文本段落(Paragraph)且无任何格式(如链接、粗体等)则视为非 Markdown
- */
- private static boolean isMarkdownSpecificNode(Node node) {
- // 标题(# 标题)
- if (node instanceof Heading) {
- return true;
- }
- // 列表(有序/无序)
- if (node instanceof BulletList || node instanceof OrderedList) {
- return true;
- }
- // 列表项
- if (node instanceof ListItem) {
- return true;
- }
- // 链接([文本](url))
- if (node instanceof Link) {
- return true;
- }
- // 图片()
- if (node instanceof Image) {
- return true;
- }
- // 粗体(**文本** 或 __文本__)
- if (node instanceof StrongEmphasis) {
- return true;
- }
- // 斜体(*文本* 或 _文本_)
- if (node instanceof Emphasis) {
- return true;
- }
- // 代码块(```代码```)
- if (node instanceof FencedCodeBlock || node instanceof IndentedCodeBlock) {
- return true;
- }
- // 表格(| 表头 | ... |)
- if (node instanceof TableBlock) {
- return true;
- }
- // 引用(> 引用内容)
- if (node instanceof BlockQuote) {
- return true;
- }
- // 水平线(--- 或 ***)
- if (node instanceof ThematicBreak) {
- return true;
- }
- // 段落节点需进一步检查是否包含 inline 格式(如粗体、链接等)
- if (node instanceof Paragraph) {
- return hasInlineMarkdownNodes(node);
- }
- // 其他节点(如文本节点)视为非特有
- return false;
- }
- /**
- * 检查段落中是否包含 inline 格式(如粗体、链接等)
- */
- private static boolean hasInlineMarkdownNodes(Node paragraph) {
- Node child = paragraph.getFirstChild();
- while (child != null) {
- // 若段落中包含任何 Markdown inline 节点,则视为 Markdown
- if (child instanceof Link || child instanceof Image || child instanceof StrongEmphasis || child instanceof Emphasis || child instanceof Code) {
- return true;
- }
- child = child.getNext();
- }
- return false;
- }
- }
- }
复制代码 8. 测试示例
- import com.vladsch.flexmark.ast.BulletList;
- import com.vladsch.flexmark.ast.Heading;
- import com.vladsch.flexmark.ast.OrderedList;
- import com.vladsch.flexmark.ast.Paragraph;
- import com.vladsch.flexmark.ast.Text;
- import com.vladsch.flexmark.ext.tables.TableBlock;
- import lombok.SneakyThrows;
- import lombok.extern.slf4j.Slf4j;
- import org.junit.Test;
- import java.io.File;
- import java.io.FileInputStream;
- import java.io.InputStream;
- import java.nio.file.Files;
- import java.nio.file.Paths;
- /**
- * 测试工具类
- *
- * @author ludangxin
- * @since 2025/10/14
- */
- @Slf4j
- public class Md2htmlTest {
- @Test
- public void given_md_str_then_print_complete_html() {
- final String html = Markdowns.builder("# 简介 \n hello world~")
- // 打印md内容
- .printContent()
- // 构建html内容, 自动完善html结构
- .buildHtmlContent();
- log.info(html);
- // # 简介
- // hello world~
- //[main] INFO md2html.Md2htmlTest -- <html><head></head><body><h1>简介</h1>
- //<p>hello world~</p>
- //</body></html>
- }
- @Test
- public void given_md_str_then_print_raw_html() {
- final String html = Markdowns.builder("# 简介 \n hello world~")
- // 构建raw html内容
- .buildRawHtmlContent();
- log.info(html);
- //[main] INFO md2html.Md2htmlTest -- <h1>简介</h1>
- //<p>hello world~</p>
- }
- @Test
- public void given_md_file_then_print_raw_html() {
- final String html = Markdowns.builder(new File("src/test/resources/test.md"))
- // 构建raw html内容
- .buildRawHtmlContent();
- log.info(html);
- //[main] INFO md2html.Md2htmlTest -- <h2>嘉文四世</h2>
- //<blockquote>
- //<p>德玛西亚</p>
- //</blockquote>
- //<p><strong>给我找些更强的敌人!</strong></p>
- //<table>
- //<thead>
- //<tr><th>列1</th><th>列2</th></tr>
- //</thead>
- //<tbody>
- //<tr><td>数据1</td><td>数据2</td></tr>
- //</tbody>
- //</table>
- }
- @Test
- @SneakyThrows
- public void given_md_stream_then_print_complete_html() {
- final InputStream fileInputStream = Files.newInputStream(Paths.get("src/test/resources/test.md"));
- final String html = Markdowns.builder(fileInputStream)
- // 构建html内容
- .buildHtmlContent();
- log.info(html);
- //[main] INFO md2html.Md2htmlTest -- <html><head></head><body><h2>嘉文四世</h2>
- //<blockquote>
- //<p>德玛西亚</p>
- //</blockquote>
- //<p><strong>给我找些更强的敌人!</strong></p>
- //<table>
- //<thead>
- //<tr><th>列1</th><th>列2</th></tr>
- //</thead>
- //<tbody>
- //<tr><td>数据1</td><td>数据2</td></tr>
- //</tbody>
- //</table>
- //</body></html>
- }
- @Test
- public void given_non_md_content_then_print_complete_html() {
- // 输入非markdown语法的内容
- final String html = Markdowns.builder("hello world~")
- // 构建html内容 (如果内容是md语法则转换为html, 如不不是 则原样输出)
- .buildHtmlIfMarkdown();
- // 输入非markdown语法的内容
- final String html2 = Markdowns.builder("## hello world~")
- // 构建html内容 (如果内容是md语法则转换为html, 如不不是 则原样输出)
- .buildHtmlIfMarkdown();
- log.info(html);
- //[main] INFO md2html.Md2htmlTest -- hello world~
- log.info(html2);
- //[main] INFO md2html.Md2htmlTest -- <html><head></head><body><h2>hello world~</h2>
- }
- @Test
- @SneakyThrows
- public void given_md_stream_and_attr_provider_then_print_raw_html() {
- final InputStream fileInputStream = Files.newInputStream(Paths.get("src/test/resources/test.md"));
- final String html = Markdowns.builder(fileInputStream)
- .attributeProvider((node, attributablePart, attributes) -> {
- // 标题
- if (node instanceof Heading) {
- Heading heading = (Heading) node;
- attributes.addValue("class", "heading" + heading.getLevel());
- }
- // 正文
- if (node instanceof Text) {
- attributes.addValue("class", "Normal");
- }
- // 段落
- if (node instanceof Paragraph) {
- attributes.addValue("class", "paragraph");
- }
- // 无序列表
- if (node instanceof BulletList) {
- attributes.addValue("class", "bulletList");
- }
- // 有序列表
- if (node instanceof OrderedList) {
- attributes.addValue("class", "bulletList");
- }
- // 表格
- if (node instanceof TableBlock) {
- attributes.addValue("class", "tableBlock");
- }
- })
- .buildRawHtmlContent();
- log.info(html);
- //[main] INFO md2html.Md2htmlTest -- <h2 >嘉文四世</h2>
- //<blockquote>
- //<p >德玛西亚</p>
- //</blockquote>
- //<p ><strong>给我找些更强的敌人!</strong></p>
- //<table >
- //<thead>
- //<tr><th>列1</th><th>列2</th></tr>
- //</thead>
- //<tbody>
- //<tr><td>数据1</td><td>数据2</td></tr>
- //</tbody>
- //</table>
- }
- }
复制代码 9. 小节
本章使用flexmark将markdown内容转换为html内容, 并介绍了其高级的配置功能和使用jsoup完善html结构,最后封装链式调用的工具类和对应的单元测试代码, 能够方便的将各种形式的markdown内容转换为html内容, 下一章将介绍将html转换为word内容
10. 源码
测试过程中的代码已全部上传至github, 欢迎点赞收藏 仓库地址: https://github.com/ludangxin/markdown2html
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |