在开发一些平台中会遇到将数据库中的数据渲染到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");
}
};
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。