LangInteger

Java Html to PDF

百度/必应/谷歌一下,使用 Java 生成 PDF 文档的常用工具为

但是最新的 iText7 使用 AGPL 协议,需要购买 license 才能够合理合法的在商业项目中使用。本着省钱的原则,使用 iText5 进行开发。

1 需求及痛点

调研 iText 的 Html 转 PDF 使用后,其主要问题如下

  • 由于 iText 并非国人开发,内置的字体是不支持中文字符渲染,需要引入额外的字体依赖

网上有不少教程解决了这个问题,但是随之而来引入了另一个问题

  • 由于大部分教程引入额外字体依赖时仅考虑了全中文的情况,使用的字体并不能对英文字体进行很好的渲染,造成英文字符错位难看

2 依赖配置

// for pdf rendering
compile group: 'com.itextpdf', name: 'itextpdf', version: '5.5.13.1'

// for pdf rendering
compile group: 'com.itextpdf.tool', name: 'xmlworker', version: '5.5.13.1'

// for chinese font in pdf rendering
compile group: 'com.itextpdf', name: 'itext-asian', version: '5.2.0'

3 字体注册代码

public class PdfFontProvider extends XMLWorkerFontProvider {

  private static final Logger LOG = LoggerFactory.getLogger(PdfFontProvider.class);

  public PdfFontProvider() {
    super(null, null);
  }

  @Override
  public Font getFont(final String fontName, String encoding, float size, final int style) {
    BaseFont font = null;
    try {
      if (StringUtils.equals(fontName, "STSong-Light")) {
        font = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
      } else {
        font = BaseFont.createFont(FontFactory.TIMES_ROMAN, FontFactory.defaultEncoding, true);
      }
    } catch (Exception e) {
      LOG.error("未找到 STSong-Light 字体库,可能是 com.itextpdf.itext-asian 依赖未载入");
    }
    return new Font(font, size, style);
  }

}

上述代码会根据 html 节点的 style 属性的 font-family 属性配置,对指明使用 STSong-Light 字体的内容使用宋体进行渲染,而其它部分则会使用其内置的 TIMES_ROMAN 这一英文字体进行渲染。根据对字体的更多需求,可对这一类进一步定制,搭配 Html 实现更美观的字体渲染。

4 PDF 生成代码

public static File html2Pdf(String html, String outputPath) {
  try {
    // step 1
    Document document = new Document(PageSize.A4);
    document.setMargins(20, 20, 0, 0);
    // step 2
    PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream(path));
    // step 3
    document.open();
    // step 4
    InputStream cssInput = null;
    XMLWorkerHelper.getInstance().parseXHtml(writer, document, new ByteArrayInputStream(html.getBytes(StandardCharsets.UTF_8)), cssInput, new PdfFontProvider());
    // step 5
    document.close();
    LOG.info("PDF file: {} rendering successfully", outputPath);
    return new File(outputPath);
  } catch (IOException ex) {
    // do something
  } catch (DocumentException ex) {
    // do something
  }
}

5 使用要点

  • 必须引入 itext-asian 依赖以支持中文字体
  • 待转换 Html 中的中文所在节点的属性一定要指定为 style="font-family:STSong-Light" 才能正常转换
  • 对于中英文夹杂的部分,可以对个别文字使用 span 标签包裹并指定 font-family 以达到精确字体渲染的目的