iText是一个文件转换的jar包,可实现html文件,xml文件转PDF,word的jar包。
maven依赖:
<dependency> <groupId>com.itextpdf</groupId> <artifactId>itextpdf</artifactId> <version>5.5.9</version> </dependency>
<dependency> <groupId>com.itextpdf.tool</groupId> <artifactId>xmlworker</artifactId> <version>5.5.9</version> </dependency>
使用首先要到的一个大问题就是不能处理中文,之前用网上教的字体是一直报这个错,com.itextpdf.text.DocumentException: Font "STSongStd-Light" with "UniGB-UCS2-H" is not recognized。
在网上痛苦的找了很久,大家给的最多的答案就是去改jar的引用路径,这方法太不靠谱了,jar一直在升级,你总不能每次都去改吧,最后终于找到了解决办法,问题根源就是这个工具没有在jar包中打包中文字体,不过它给我们提供了一个字体设置工厂,上代码,
public class PDFUtil { private static final Logger logger = LoggerFactory.getLogger(PDFUtil.class); private static String fontPath = "/Users/feixiaobo/Downloads/simsun.ttf"; public static InputStream htmlToPDF(InputStream htmlInputStream) { ByteArrayOutputStream out = null; ByteArrayInputStream inputStream = null; Document document = new Document(); XMLWorkerFontProvider provider = new XMLWorkerFontProvider(); provider.register(fontPath); try { out = new ByteArrayOutputStream(); PdfWriter writer = PdfWriter.getInstance(document, out); document.open(); XMLWorkerHelper.getInstance().parseXHtml(writer, document, new BufferedInputStream(htmlInputStream), Charset.forName("utf-8"), provider); document.close(); inputStream = new ByteArrayInputStream(out.toByteArray());} catch (DocumentException e) { logger.error(e.getMessage(), e); } catch (IOException e) { document.close(); logger.error(e.getMessage(), e); }finally {if (inputStream != null) {try {
inputStream.close();} catch (IOException e) {
logger.error(e.getMessage(), e);
}}if (out != null) {try {
out.close();} catch (IOException e) {
logger.error(e.getMessage(), e);
}}} return inputStream;
}public static void main(String[] args) throws IOException{ String html = "/Users/feixiaobo/Desktop/test.html"; String pdf = "/Users/feixiaobo/Desktop/test.pdf"; File pdfFile = new File(pdf); InputStream inputStream = htmlToPDF(new FileInputStream(new File(html))); try{ if(!pdfFile.exists()){ pdfFile.createNewFile(); } }catch (IOException e ){// } IOUtil.copyCompletely(inputStream,new FileOutputStream(pdf)); }}这样的一个方法便实现了将html 流转为pdf流,其中解决中文问题的关键代码XMLWorkerFontProvider provider = new XMLWorkerFontProvider(); provider.register(fontPath);我自己下载了一个宋体字体,然后通过这个方法引入中文字体,simsun.ttf便是宋体字体,不过html模版里的字体要全部改为宋体,至此中文问题解决了,传入一个html文件的输入流掉用这个方法便得到了一个PDF输出流,特别注意,这个方法返回的是一个字节流,不是字符流,如果你用字符流的方法写入文件生成的PDF文件是打不开的。
那么很多人又遇到问题了,字节流如何写入文件,当然网上有很多方法,通过定义一个缓冲区然后用InputStream的read()方法一段一段读,再用OutputStream的write()方法一段一段写,这里我给大家一个很好(懒)的方法,IOUtil.copyCompletely(InputStream input, OutputStream output);这是javaIO包直接给我们封装好的方法,看下这个方法的源码,public static void copyCompletely(InputStream input, OutputStream output) throws IOException { if(output instanceof FileOutputStream && input instanceof FileInputStream) { try { FileChannel buf1 = ((FileOutputStream)output).getChannel(); FileChannel ignore1 = ((FileInputStream)input).getChannel(); ignore1.transferTo(0L, 2147483647L, buf1); ignore1.close(); buf1.close(); return; } catch (Exception var6) { ; } } byte[] buf = new byte[8192]; while(true) { int ignore = input.read(buf); if(ignore < 0) { try { input.close(); } catch (IOException var5) { ; } try { output.close(); } catch (IOException var4) { ; } return; } output.write(buf, 0, ignore); } }这个方法会先判断输入流是字节流还是文件流,然后根据不同的流选择不同的方法,就不用我们自己去判断到底是字符流还是字符流了,无脑调这个方法便是,当然它对字节流的实现还是依然是我上面说的方法,但是不用我们自己写了啊。最后提一点关于流要注意的问题,我们用完流之后一定记得关闭流啊,否则会有各种问题的,还需注意的是我们关闭流的代码的位置,不要觉得我上面的代码太冗余了,流的关闭不应该放在try{}catch(){}中,为什么呢?如果你的代码执行到中途抛异常了,而你关闭流的代码在异常代码后面,那问题就来了,你关闭流的代码已经执行不到了,各个流的关闭也不要写在一个try{}catch(){}中,还是这个问题,close()方法会抛异常,如果一个流的close()方法抛异常流,就会导致它后面的流执行不到关闭流的代码。还有上面方法中第一个document.close();不可省略,document不关闭,转换后的流是写不到out里的,那样生成的PDF文件是空,不能打开。第二个document.close()是因为.parseXHtm()方法会抛异常,如果这里抛了异常,那么后面的close()便执行不到了,所以需要在异常处理里关闭。