在開發一些平臺中會遇到將資料庫中的資料渲染到PDF模板檔案中的場景,用itextPdf完全動態生成PDF檔案的太過複雜,通過itextPdf/AcroFields可以比較簡單的完成PDF資料渲染工作(PDF模板的表單域資料需定義名稱)
- Controller獲取HttpServletResponse 輸出流
package pdf.controller;
import com.itextpdf.text.DocumentException;
import service.PdfService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import java.io.IOException;
import java.io.OutputStream;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
@Controller
@RequestMapping(WebConstants.WEB_pdf + "/download")
@Api(description = "pdf下載相關", tags = "Pdf.download")
@NoAuth
public class PdfDownloadController {
@Autowired
private PdfService PdfService;
@Value("${pdf.template.path}")
private String templatePath ;
@ApiOperation(value = "申請表下載")
@RequestMapping(value = "/download/{id}", method = RequestMethod.GET)
@ResponseBody
@NoLogin
public void download(@PathVariable("id") Long id, HttpServletResponse response) throws IOException, DocumentException {
//設定響應contenType
response.setContentType("application/pdf");
//設定響應檔名稱
String fileName = new String("申請表.pdf".getBytes("UTF-8"),"iso-8859-1");
//設定檔名稱
response.setHeader("Content-Disposition", "attachment; filename="+fileName);
//獲取輸出流
OutputStream out = response.getOutputStream();
PdfService.download(id, templatePath ,out);
}
}
- Service生成pdf資料響應輸出流
1.業務service-負責實現獲取pfd模板資料,資料庫資料,實現動態賦值生成PDF
package service.impl;
import org.apache.commons.io.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.ResourceUtils;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.pdf.AcroFields;
import com.itextpdf.text.pdf.BaseFont;
import com.itextpdf.text.pdf.PdfReader;
import com.itextpdf.text.pdf.PdfStamper;
@Service
public class ApplyServiceImpl implements ApplyService {
@Override
public DetailDTO getDetail(Long id) {
// 獲取業務資料
return null;
}
@Override
public Map<String, String> getPdfMapping(DetailDTO dto) {
// TODO Auto-generated method stub
// 獲取pdf與資料庫的資料欄位對映map
}
@Override
public void download(Long id, String templatePath, OutputStream out) {
// TODO Auto-generated method stub
DetailDTO dto = getDetail(id);
Map<String, String> fieldMapping = getPdfMapping(dto);
String filePath;
byte[] pdfTemplate;
ByteArrayOutputStream pdfOutputStream = new ByteArrayOutputStream();
try {
//獲取模板檔案路徑
filePath = ResourceUtils.getURL(templatePath).getPath();
//獲取模板檔案位元組資料
pdfTemplate = IOUtils.toByteArray(new FileInputStream(filePath));
//獲取渲染資料後pdf位元組陣列資料
byte[] pdfByteArray = generatePdfByTemplate(pdfTemplate, fieldMapping);
pdfOutputStream.write(pdfByteArray);
pdfOutputStream.writeTo(out);
pdfOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
pdfOutputStream.close();
out.flush();
out.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
@Override
//itextPdf/AcroFields完成PDF資料渲染
public byte[] generatePdfByTemplate(byte[] pdfTemplate, Map<String, String> pdfParamMapping) {
Assert.notNull(pdfTemplate, "template is null");
if (pdfParamMapping == null || pdfParamMapping.isEmpty()) {
throw new IllegalArgumentException("pdfParamMapping can`t be empty");
}
PdfReader pdfReader = null;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
PdfStamper stamper = null;
try {
// 讀取pdf模板
pdfReader = new PdfReader(pdfTemplate);
stamper = new PdfStamper(pdfReader, baos);
//獲取所有表單欄位資料
AcroFields form = stamper.getAcroFields();
form.setGenerateAppearances(true);
// 設定
ArrayList<BaseFont> fontList = new ArrayList<>();
fontList.add(getMsyhBaseFont());
form.setSubstitutionFonts(fontList);
// 填充form
for (String formKey : form.getFields().keySet()) {
form.setField(formKey, pdfParamMapping.getOrDefault(formKey, StringUtils.EMPTY));
}
// 如果為false那麼生成的PDF檔案還能編輯,一定要設為true
stamper.setFormFlattening(true);
stamper.close();
return baos.toByteArray();
} catch (DocumentException | IOException e) {
LOGGER.error(e.getMessage(), e);
} finally {
if (stamper != null) {
try {
stamper.close();
} catch (DocumentException | IOException e) {
LOGGER.error(e.getMessage(), e);
}
}
if (pdfReader != null) {
pdfReader.close();
}
}
throw new SystemException("pdf generate failed");
}
/**
* 預設字型
*
* @return
*/
private BaseFont getDefaultBaseFont() throws IOException, DocumentException {
return BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
}
/**
* 微軟宋體字型
*
* @return
*/
//設定字型
private BaseFont getMsyhBaseFont() throws IOException, DocumentException {
try {
return BaseFont.createFont("/msyh.ttf", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
} catch (DocumentException | IOException e) {
LOGGER.error(e.getMessage(), e);
}
return getDefaultBaseFont();
}
}
- 資料庫的資料到pdf欄位的對映可以使用配置,建立資料欄位對映表,通過反射
可以將資料庫數 據物件轉為map,再通過定義的靜態map對映表,將資料map轉
換為pdf表單資料map
BeanUtils.bean2Map(bean);
/**
* JavaBean物件轉化成Map物件
* @param javaBean
* @return
*/
public static Map bean2Map(Object javaBean) {
Map map = new HashMap();
try {
// 獲取javaBean屬性
BeanInfo beanInfo = Introspector.getBeanInfo(javaBean.getClass());
PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
if (propertyDescriptors != null && propertyDescriptors.length > 0) {
String propertyName = null; // javaBean屬性名
Object propertyValue = null; // javaBean屬性值
for (PropertyDescriptor pd : propertyDescriptors) {
propertyName = pd.getName();
if (!propertyName.equals("class")) {
Method readMethod = pd.getReadMethod();
propertyValue = readMethod.invoke(javaBean, new Object[0]);
map.put(propertyName, propertyValue);
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
return map;
}
欄位對映配置
public class PdfMapping {
public final static Map<String, String> BASE_INFO_MAPPING = new HashMap() {
{
put("name", "partyA");
put("identity", "baseIdentity");
}
};
}