Java工具箱-markdown文件生成PDF

1,577 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第3天,点击查看活动详情

Flexmark简介

Flexmark是作者基于commonmark-java开发的markdown转换工具。主要作用是依据markdown源文件生成标准的html页面,转换pdf是基于另一个工具包openhtmltopdf完成。

相比于commonmark-java,Flexmark性能上没有那么好,作者的初衷是替换JetBrains的markdown渲染器,但是文档和示例代码比较全,下面的例子是基于官方示例增加了中文乱码问题处理的代码。

示例

import com.vladsch.flexmark.ext.toc.TocExtension;
import com.vladsch.flexmark.html.HtmlRenderer;
import com.vladsch.flexmark.parser.Parser;
import com.vladsch.flexmark.pdf.converter.PdfConverterExtension;
import com.vladsch.flexmark.profile.pegdown.Extensions;
import com.vladsch.flexmark.profile.pegdown.PegdownOptionsAdapter;
import com.vladsch.flexmark.util.ast.Node;
import com.vladsch.flexmark.util.data.MutableDataHolder;
import org.apache.pdfbox.pdmodel.encryption.AccessPermission;
import org.apache.pdfbox.pdmodel.encryption.StandardProtectionPolicy;

public class PdfConverter {
    static final MutableDataHolder OPTIONS = PegdownOptionsAdapter.flexmarkOptions(Extensions.ALL & ~(Extensions.ANCHORLINKS | Extensions.EXTANCHORLINKS_WRAP), TocExtension.create()).toMutable();
    static final Parser PARSER = Parser.builder(OPTIONS).build();
    static final HtmlRenderer RENDERER = HtmlRenderer.builder(OPTIONS).build();

    public static void main(String[] args) throws Exception {
        String path = PdfConverter.class.getClassLoader().getResource("HanYiQiHei-55Jian-Regular-2.ttf").getPath();
        String markdown = "" +
                "# Heading\n\n" +
                "=======\n" +
                "\n" +
                "*** ** * ** ***\n" +
                "\n" +
                "paragraph text lazy continuation\n" +
                "\n" +
                "* list itemblock quote lazy continuation\n" +
                "\n" +
                "\~\~\~info with uneven indent with uneven indent indented code \~\~\~\n" +
                "\n" +
                "       with uneven indent\n" +
                "          with uneven indent\n" +
                "    indented code\n" +
                "\n" +
                "1. numbered item 1\n" +
                "2. numbered item 2\n" +
                "3. numbered item 3\n" +
                "   * bullet item 1\n" +
                "   * bullet item 2\n" +
                "   * bullet item 3\n" +
                "     1. numbered sub-item 1\n" +
                "     2. numbered sub-item 2\n" +
                "     3. numbered sub-item 3\n" +
                "\n" +
                "   \~\~\~info with uneven indent with uneven indent indented code \~\~\~\n" +
                "```java \n" +
                "   System.out.println("test print");\n" +
                "``` \n\n" +
                "## 中文测试\n\n" +
                "| 字符 | 说明 | \n" +
                "| --- | --- | \n" +
                "| var 变量名称    | 申明变量,弱类型 |";

        Node document = PARSER.parse(markdown);
        String html = RENDERER.render(document);
        System.out.println(html);
        html = "<!DOCTYPE html><html><head>\n" +
                "</head><body>" + html + "\n" +
                "</body></html>";
        String css = "@font-face {\n" +
                "  font-family: 'cnFont';\n" +
                "  src: url('file:" + path + "');\n" +
                "  font-weight: normal;\n" +
                "  font-style: normal;\n" +
                "}\n" +
                "* {\n" +
                "    font-family: 'cnFont';\n" +
                "}\n";
        html = PdfConverterExtension.embedCss(html, css);
        OPTIONS.set(PdfConverterExtension.PROTECTION_POLICY, new StandardProtectionPolicy("123", "123", new AccessPermission()));
        PdfConverterExtension.exportToPdf("flexmark-java-landscape.pdf", html, "", OPTIONS);
    }
}

关键点说明

  • 中文问题:产出html时对中文并没有什么问题,但是将html转换为pdf时会发现中文都显示为乱码,原因是pdf转换使用的是非中文的字体,使用css定义一个中文字体即可。
  • 字体使用getClassLoader获取是由于之前使用相对路径一直没找到位置,有了解url file定位规则的同学麻烦告知。
  • OPTIONS中屏蔽掉了链接相关配置,需要的话去掉即可
  • PROTECTION_POLICY:为pdf密码设置,可生成带密码的pdf。
  • 生成过程是先产出html,再转换为pdf,默认生成的html页面并没有携带很多样式,可自行增加。

参考资料

github.com/vsch/flexma…

github.com/vsch/flexma…