公司最近做一個交易所項目,里面涉及一個需求就是將html模板,在填充數據后轉換為pdf,這樣防止數據更改,下面是具體實現
1 pom文件
<dependency> <groupId>com.itextpdf</groupId> <artifactId>html2pdf</artifactId> <version>2.0.2</version> </dependency> <dependency> <groupId>com.itextpdf</groupId> <artifactId>font-asian</artifactId> <version>7.1.2</version> </dependency>
2 html轉pdf
itext7進行html轉換使用類:com.itextpdf.html2pdf.HtmlConverter
它主要有三類操作:convertToPdf直接轉換為pdf文件
convertToDocument轉為document文檔,這樣有利於進行pdf頁面調整
convertToElements拆解pdf標簽
我這里因為html轉換后會有多頁,這里通過convertToDocument調整頁面大小,在一頁上顯示所有內容;
同時我使用ByteArrayOutputStream類,這個的好處是不在本地生成文件,減少磁盤操作,但是這種方式也有人說效率不高,使用者可以斟酌后使用.
因為spring boot存在打包后resources目錄文件獲取不到的問題,所以我將pdf依賴的字體文件放到項目的根路徑下(跟src目錄同級).
package com.ssth.exchanage.excenter.common.uitls;
import java.io.ByteArrayOutputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import com.itextpdf.html2pdf.ConverterProperties;
import com.itextpdf.html2pdf.HtmlConverter;
import com.itextpdf.html2pdf.resolver.font.DefaultFontProvider;
import com.itextpdf.kernel.colors.Color;
import com.itextpdf.kernel.geom.PageSize;
import com.itextpdf.kernel.geom.Rectangle;
import com.itextpdf.kernel.pdf.PdfDocument;
import com.itextpdf.kernel.pdf.PdfPage;
import com.itextpdf.kernel.pdf.PdfWriter;
import com.itextpdf.kernel.pdf.canvas.PdfCanvas;
import com.itextpdf.kernel.pdf.canvas.draw.ILineDrawer;
import com.itextpdf.layout.Document;
import com.itextpdf.layout.element.LineSeparator;
public class PDFUtil {
private static final String FONT = "./pdf/font/NotoSansCJKsc-Regular.otf";
/**
* @Description 將html轉換為pdf文件
* @param html html頁面字符串
* @return
* @throws FileNotFoundException
* @throws IOException
*/
public ByteArrayOutputStream html2Pdf(String html) throws FileNotFoundException, IOException {
ConverterProperties props = new ConverterProperties();
DefaultFontProvider defaultFontProvider = new DefaultFontProvider(false, false, false);
defaultFontProvider.addFont(font);
props.setFontProvider(defaultFontProvider);
ByteArrayOutputStream bao = new ByteArrayOutputStream();
PdfWriter writer = new PdfWriter(bao);
PdfDocument pdf = new PdfDocument(writer);
pdf.setDefaultPageSize(new PageSize(595, 14400));
Document document = HtmlConverter.convertToDocument(html, pdf, props);
EndPosition endPosition = new EndPosition();
LineSeparator separator = new LineSeparator(endPosition);
document.add(separator);
document.getRenderer().close();
PdfPage page = pdf.getPage(1);
float y = endPosition.getY() - 36;
page.setMediaBox(new Rectangle(0, y, 595, 14400 - y));
document.close();
return bao;
}
/**
* 定義操作區域
*/
class EndPosition implements ILineDrawer {
// y坐標
protected float y;
/**
* @Description: 獲取y坐標
* @return
*/
public float getY() {
return y;
}
/**
* @Description: 操作畫布特定區域
* @param pdfCanvas:操作畫布
* @param rect:操作區域
*/
@Override
public void draw(PdfCanvas pdfCanvas, Rectangle rect) {
this.y = rect.getY();
}
/**
* @Description: 獲取行顏色
* @return
*/
@Override
public Color getColor() {
return null;
}
/**
* @Description: 獲取行寬
* @return
*/
@Override
public float getLineWidth() {
return 0;
}
/**
* @Description: 設置行顏色
* @param color
*/
@Override
public void setColor(Color color) {
}
/**
* @Description: 設置行寬
* @param lineWidth:寬度
*/
@Override
public void setLineWidth(float lineWidth) {
}
}
}
3 流響應
核心是通過ByteArrayOutputStream.writeTo(HttpServletResponse.getOutputStream())方法
package com.huishi;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import javax.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Service;
import com.ssth.exchanage.excenter.common.exception.ResException;
import com.ssth.exchanage.excenter.common.uitls.DateUtil;
import com.ssth.exchanage.excenter.common.uitls.PDFUtil;
import com.ssth.exchanage.excenter.controller.response.RecordApplyParamRep;
import com.ssth.exchanage.excenter.service.ProductRecordService;
@Service
public class ProductRecordServiceImp implements ProductRecordService {
/**
* @Description 打印備案申請書
* @param res http響應
*/
@Override
public void printRecord(HttpServletResponse res){
try {
String htmlStr = FileUtil.readFile(null);
PDFUtil pdfUtil = new PDFUtil();
ByteArrayOutputStream stream = pdfUtil.html2Pdf(htmlStr);
res.setHeader("Expires", "0");
res.setHeader("Cache-Control", "must-revalidate, post-check=0, pre-check=0");
res.setHeader("Pragma", "public");
res.setContentType("application/pdf");
OutputStream os = res.getOutputStream();
stream.writeTo(os);
os.flush();
os.close();
} catch (IOException e) {
log.error("printRecord {}",e.getMessage());
throw new ResException("-1","服務內部錯誤,請稍后再試!");
}
}
}
備注:
itext7解決中文顯示問題有兩種解決方式:
1 引入對應的語言包,火狐瀏覽器預覽生成的pdf可能存在部分中文亂碼問題,同時因為加載了語言包,生成的pdf更大
使用NotoSansCJKsc-Regular.otf,同時在pom中引入com.itextpdf.font-asian包.
2 設置字體:通過默認字體生成,pdf文件和html大小幾乎相同,不存在瀏覽器預覽亂碼問題
PdfFont font = PdfFontFactory.createFont("STSongStd-Light", "UniGB-UCS2-H", false);
使用示例鏈接: https://github.com/liulei3/html2pdf
參考文件:
html轉pdf: https://developers.itextpdf.com/content/itext-7-examples/itext-7-converting-html-pdf
流響應:https://developers.itextpdf.com/content/best-itext-questions-stackoverview/general-questions-about-itext/itext7-how-can-i-serve-pdf-browser-without-storing-file-server-side
