HTML转PDF

Maven依赖

<dependency>
    <groupId>com.itextpdf</groupId>
    <artifactId>itextpdf</artifactId>
    <version>5.5.7</version>
</dependency>
<dependency>
    <groupId>com.itextpdf.tool</groupId>
    <artifactId>xmlworker</artifactId>
    <version>5.5.7</version>
</dependency>

类图及API

类图

IHtml2PdfService

HTML转PDF的服务接口

write (htmlContent: java.lang.String, os: java.io.OutputStream): void

将指定的html转换成pdf内容后,写到指定的输出流。

Html2PdfServiceImpl

HTML转PDF的服务实现类

write (htmlContent: java.lang.String, os: java.io.OutputStream): void

实现:将指定的html转换成pdf内容后,写到指定的输出流。

getIntaceHtml (htmlContent: String): String

私有方法,用于获取完整的html文档;如果传入的只是html片断,需要使用模板将其完整化。

doWrite (htmlContent: String, os: OutputStream): void

私有方法,将完整的html文档转换成pdf之后,写到指定的输出流。

代码

IHtml2PdfService

package cn.ljl.javaweb.demo.ckeditor.service;

public interface IHtml2PdfService {
    /** html完整内容的前缀标识 */
    public static final String INTACT_FLAG = "<html>";
    
    /**
     * html模板,当待转换的html只是片断时,需将其插入到模板的body内.
     */
    public static final String TEMPLATE_HTML = 
            "<html>" +
            "    <head>" +
            "        <style type='text/css'>body {font-family: SimSun;}</style>" +
            "    </head>" +
            "    <body>" +
            "        ${content}" +
            "    </body>" +
            "</html>";
    
    /** 将指定的html内容转化成pdf文档之后,写入到指定的输出流. */
    public void write(java.lang.String htmlContent, java.io.OutputStream os);
    
}

Html2PdfServiceImpl

package cn.ljl.javaweb.demo.ckeditor.service;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.Charset;

import com.itextpdf.text.DocumentException;

public class Html2PdfServiceImpl implements IHtml2PdfService {
    @Override
    public void write(String htmlContent, OutputStream os) {
        if (htmlContent == null || htmlContent.length() == 0)
            return;

        if (os == null)
            return;

        htmlContent = getIntaceHtml(htmlContent);
        doWrite(htmlContent, os);
    }
    
    /**
     * 根据提供的html内容,获取完整的html内容.<br>
     * @param htmlContent
     * @return
     */
    private String getIntaceHtml(String htmlContent) {
        boolean intact = htmlContent.trim().toLowerCase()
                .startsWith(INTACT_FLAG);
        if (!intact) {
            htmlContent = TEMPLATE_HTML.replaceFirst("\\$\\{content\\}", htmlContent);
        }

        return htmlContent;
    }
    
    /**
     * 实施写操作.<br>
     * @param htmlContent
     * @param os
     */
    private void doWrite(String htmlContent, OutputStream os) {
        InputStream is = new ByteArrayInputStream(htmlContent.getBytes());

        com.itextpdf.text.Document document = new com.itextpdf.text.Document();
        com.itextpdf.text.pdf.PdfWriter writer = null;
        try {
            writer = com.itextpdf.text.pdf.PdfWriter
                    .getInstance(document, os);
            document.open();
            
            com.itextpdf.tool.xml.XMLWorkerHelper.getInstance().parseXHtml(writer,
                    document, is, Charset.forName("gbk"));
        } catch (DocumentException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        
        writer.flush();
        // SINOBEST HTML2PDF 如果我们调用writer.close(),可能无法正常的生成pdf,甚至会遇到
        // Exception:The page 1 was requested but the document has only 0 pages.
        // writer.close();
        document.close();
    }
}

可能遇到的问题

1. 中文字符显示问题

如果是完整的html文档,需要使用css为整个文档设置一个默认的字体,如IHtml2PdfService的模板中的代码片段:

<style type='text/css'>body {font-family: SimSun;}</style>

2. 部分内容空白

可能是html文档使用了服务器中没有的字体。比如html文档的某个div,设置了仿宋字体,而机器上又没有安装,那么转换成pdf的时候,对应的内容是空白。