跳至主要內容

Itext PDF 导出

gavin-james开发随手记大约 3 分钟

Itext PDF 导出

给我的文档系统添加了导出,本文记录PDF填坑小结。

PDF导出的实现

导出流

@RequestMapping(value = "export")
public void exportPdfNew(String mdContent, String type, HttpServletResponse response) {
	response.reset();
	response.setContentType("multipart/form-data");

	String title = "file";
	String author = "gavin";
	String mdFileContent = "";

	ServletOutputStream out = null;
	try {
		byte[] fileBytes = null;
		
		String htmlFile = StringEscapeUtils.unescapeHtml4(mdContent);
		
		String cleanedHtmlFile = htmlFile.replace("<img", "<img style=\"display:inline-block;width:100%;max-width:650px;\" ");
		cleanedHtmlFile = cleanHtml(cleanedHtmlFile);
		fileBytes = convert(author, cleanedHtmlFile);
		response.setHeader("Content-Disposition",
				"inline; filename=\"" + title + "_" + System.currentTimeMillis() + ".pdf\"");
			
		out = response.getOutputStream();
		out.write(fileBytes);
		out.close();
		out.flush();
	} catch (Exception e) {
		System.out.println(e);
	}
}

清理HTML

public static String cleanHtml(String input) throws ConversionException, IOException {
	InputStream stringAsStream;
	try {
		stringAsStream = new ByteArrayInputStream(input.getBytes("UTF-8"));
	} catch (UnsupportedEncodingException e) {
		throw ConversionException.HTML_TO_PDF_EXCEPTION;
	}
	HtmlCleaner cleaner = new HtmlCleaner();
	TagNode node = cleaner.clean(stringAsStream, "UTF-8");

	TagNode t = new TagNode("root");
	t.addChild(node);

	String html = cleaner.getInnerHtml(t);
	return html;
}

页眉页脚支持

public class HeaderFooterEvent extends PdfPageEventHelper {
	String header, author;
	PdfTemplate total;

	public void setHeader(String header) {
		this.header = header;
	}
	
	public void setAuthor(String author) {
		this.author = author;
	}

	public void onOpenDocument(PdfWriter writer, Document document) {
		total = writer.getDirectContent().createTemplate(30, 16);
	}

//	public void onEndPage(PdfWriter writer, Document document) {
//		PdfPTable table = new PdfPTable(3);
//		try {
//			table.setWidths(new int[] { 60, 24, 2 });
//			table.setTotalWidth(600);
//			table.setLockedWidth(true);
//			table.getDefaultCell().setFixedHeight(40);
//			table.getDefaultCell().setBorder(Rectangle.BOX);
//			table.getDefaultCell().setBorderWidth(0.1f);
//			table.getDefaultCell().setBorderColor(Color.gray);
//
//			table.addCell(header);
//			table.getDefaultCell().setHorizontalAlignment(Element.ALIGN_RIGHT);
//			table.addCell(String.format("Page %d of", writer.getPageNumber()));
//			PdfPCell cell = new PdfPCell(Image.getInstance(total));
//			cell.setBorder(Rectangle.BOTTOM);
//			table.addCell(cell);
//			table.writeSelectedRows(0, -1, 34, 803, writer.getDirectContent());
//
//		} catch (DocumentException de) {
//			throw new ExceptionConverter(de);
//		}
//	}
	
	public void onEndPage(PdfWriter writer, Document document) {  
        
        PdfContentByte cb = writer.getDirectContent();  
        cb.saveState();  
  
        cb.beginText();  
        BaseFont bf = null;  
        try {  
            bf = BaseFont.createFont(BaseFont.HELVETICA, BaseFont.WINANSI, BaseFont.EMBEDDED);
        } catch (Exception e) {  
            e.printStackTrace();  
        }  
        cb.setColorFill(Color.GRAY);
        cb.setFontAndSize(bf, 10);  
        
          
        //Header  
        float x = document.top(-10);  
          
        //左  
        cb.showTextAligned(PdfContentByte.ALIGN_LEFT,  
                           "Author: " + author,   
                           document.left(), x, 0);  
        //中  
//        cb.showTextAligned(PdfContentByte.ALIGN_CENTER,  
//                            writer.getPageNumber()+ " page",  
//                           (document.right() + document.left())/2,  
//                           x, 0);  
        //右  
        cb.showTextAligned(PdfContentByte.ALIGN_RIGHT,  
                           "Email: suzhou.daipeng@gmail.com",  
                           document.right(), x, 0);  
  
        //Footer  
        float y = document.bottom(-10);  
  
        //左  
        cb.showTextAligned(PdfContentByte.ALIGN_LEFT,  
                           "Confidential",  
                           document.left(), y, 0);  
        //中  
		cb.showTextAligned(PdfContentByte.ALIGN_CENTER,  
				String.format("- %d -", writer.getPageNumber()),  
		                   (document.right() + document.left())/2,  
		                   y, 0);
        //右  
//        cb.showTextAligned(PdfContentByte.ALIGN_RIGHT,  
//                           "F-Right",  
//                           document.right(), y, 0);  
  
        cb.endText();  
          
        cb.restoreState();  
    }  

	public void onCloseDocument(PdfWriter writer, Document document) {
		ColumnText.showTextAligned(total, Element.ALIGN_LEFT, new Phrase(String.valueOf(writer.getPageNumber() - 1)), 2,
				2, 0);

	}
}

PDF中文支持

常见的中文支持思路:

  • 添加itext-asian.jar
  • Windows字体添加到项目中,导出时通过创建该字体对象;
  • classpath设置

简单demo如下:

package oliver.itext.demo;  
import java.io.File;  
import java.io.FileOutputStream;  
import java.io.IOException;  
import java.io.OutputStream;  
import com.itextpdf.text.Document;  
import com.itextpdf.text.DocumentException;  
import com.itextpdf.text.Font;  
import com.itextpdf.text.Paragraph;  
import com.itextpdf.text.pdf.BaseFont;  
import com.itextpdf.text.pdf.PdfWriter;  
  
public class PDF2Chinese  
{  
    public static void main(String[] args) throws DocumentException, IOException  
    {  
        Document document = new Document();  
        OutputStream os = new FileOutputStream(new File("chinese.pdf"));  
        PdfWriter.getInstance(document,os);  
        document.open();  
        //方法一: 使用Windows系统字体(TrueType)  
        BaseFont baseFont = BaseFont.createFont("C:/Windows/Fonts/SIMYOU.TTF",BaseFont.IDENTITY_H,BaseFont.NOT_EMBEDDED);  
          
        //方法二: 使用iTextAsian.jar中的字体  
        //BaseFont baseFont = BaseFont.createFont("STSong-Light",BaseFont.IDENTITY_H,BaseFont.NOT_EMBEDDED);  
          
        //方法三: 使用资源字体(ClassPath)  
        ////BaseFont baseFont = BaseFont.createFont("/SIMYOU.TTF",BaseFont.IDENTITY_H,BaseFont.NOT_EMBEDDED);  
          
        Font font = new Font(baseFont);  
        document.add(new Paragraph("解决中文问题了!",font));  
        document.close();  
    }  
} 

通过ITextFontResolvert添加字体

将Windows下fonts中所需要的字体拷贝到项目classpath下:

在代码中通过ITextFontResolver添加字体,具体代码如下:

public byte[] convert(String author, String input)
			throws ConversionException, IOException, com.lowagie.text.DocumentException {
	ITextRenderer renderer = new ITextRenderer();
	renderer.setDocumentFromString(new String(input.getBytes()));
	renderer.setPDFVersion(PdfWriter.VERSION_1_7);
	HeaderFooterEvent headerFooterEvent = new HeaderFooterEvent();
	headerFooterEvent.setAuthor(author);
	renderer.setPdfPageEvent(headerFooterEvent);
	ITextFontResolver fontResolver = renderer.getFontResolver();
	fontResolver.addFont(fontPath+"MSYH.TTC", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);// 微软雅黑
	fontResolver.addFont(fontPath+"MSYHBD.TTC", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
	fontResolver.addFont(fontPath+"MSYHL.TTC", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
	
	renderer.layout();
	ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
	renderer.createPDF(outputStream);
	byte[] bytes = outputStream.toByteArray();
	return bytes;
}

PDF Linux环境下中文字体乱码

用itext生成pdf在windows环境下没有出现中文乱码而在linux下出现中文乱码,打开pdf查看pdf编码,以及显示的编码,发现编码并没有对应。原因是使用的宋体和微软雅黑在linux环境下并没有安装。

解决方法

  • 由于我这边PDF生成是通过HTML转化的,所以第一步先导出HTML,去查看需要用哪些字体文件;
  • 到windows环境下将所需字体拷贝到linux下。(simsun.ttc(宋体), msyf.ttf(微软雅黑))
  • 到linux环境下创建目录
mkdir -pv /usr/share/fonts/chinese/TrueType

将字体放入目录下

cd /usr/share/fonts/chinese/TrueType
chmod 755 * 为字体赋予可执行权限
  • 建立字体缓存
mkfontscale (如果提示 mkfontscale: command not found,需自行安装 # yum install mkfontscale )
mkfontdir
fc-cache -fv (如果提示 fc-cache: command not found,则需要安装# yum install fontconfig )
  • reboot重启系统

经不同Linux环境测试,以上步骤必须顺序全部执行, 建立字体缓存必须是在字体拷贝完成之后顺序执行 mkfontscale,mkfontdir,fc-cache -fv