我的系统环境:win10、IDEA、jdk1.8
一、Adobe Acrobat DC的下载
百度链接:链接:https://pan.baidu.com/s/1RSV8D6kXDbWeV2owgw2Zyg
提取码:f8p8
可自取。
二、模板准备
1、在word中画好模板,另存为pdf格式
我生成的pdf模板如下图所示。
2、打开软件:Adobe Acrobat DC
页面如下图所示,选择【工具】–【准备表单】
将上述pdf文件放入这里
进入编辑表单页面,编辑字段(此字段名需和程序中字段名保持一致)。我的编辑如下图所示。
由于在【备注】这一字段中,我们的要求是文字带下划线的那种,且备注文字可能会有些长,因此我在这里设置成这样,我的思路如下:
1、在Adobe Acrobat DC中设置了bz-line1、bz-line2、bz-line3三个文本域,并分别设置其属性(双击该域即可,文字大小样式等都可以设置)。如下图所示:
2、程序中,读取数据(字符串类型),按固定长度对字符串进行截取并循环放入bz-line1、bz-line2、bz-line3中。后面在程序里我会展示我使用的笨方法。。。。3、其实。。如果没有要求带下划线的话,完全可以直接在【属性】中勾选上【多行】即可实现多文字的换行。
3、保存编辑好的模板
三、java实现
1、添加Maven依赖
<!-- PDF工具类 -->
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itextpdf</artifactId>
<version>5.5.13</version>
</dependency>
<!-- PDF中文支持 -->
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itext-asian</artifactId>
<version>5.2.0</version>
</dependency>
2、直接生成pdf至本地指定路径
(1)、pdfUtils类
public class PdfUtils {
private final static Logger log = LoggerFactory.getLogger(PdfUtils.class);
// 利用模板生成pdf,这将直接保存到指定路径
public static void pdfout(Map<String,Object> o,String templatePath,String newPDFPath) {
PdfReader reader;
FileOutputStream out=null;
ByteArrayOutputStream bos=null;
PdfStamper stamper;
try {
//系统字体
String prefixFont = "";
String os = System.getProperties().getProperty("os.name");
if (os.startsWith("win") || os.startsWith("Win")) {
prefixFont = "C:\\Windows\\Fonts" + File.separator;
} else {
prefixFont = "/usr/share/fonts/chinese" + File.separator;
}
//必须加“,0”或“,1”,否则会报错:com.itextpdf.text.DocumentException: Font 'C:\Windows\Fonts\simsun.ttc' with 'Identity-H' is not recognized.
BaseFont bf = BaseFont.createFont(prefixFont + "simsun.ttc,0" , BaseFont.IDENTITY_H, BaseFont.EMBEDDED);
out = new FileOutputStream(newPDFPath);// 输出流
reader = new PdfReader(templatePath);// 读取pdf模板
bos = new ByteArrayOutputStream();
stamper = new PdfStamper(reader, bos);
AcroFields form = stamper.getAcroFields();
//文字类的内容处理
Map<String,String> datemap = (Map<String,String>)o.get("datemap");
form.addSubstitutionFont(bf);
for(String key : datemap.keySet()){
//为了文字可以有下划线,并且换行,控制每行字数,当字数超过时,将剩余文字填充至下一备选域
if ("hzbz".equals(key)){
String hzbz = datemap.get(key);
String[] hzbzArray = stringToStringArray(hzbz,24);
for(int i = 0; i < hzbzArray.length; i++){
String fkey = "hzbz-line" + (i+1);
form.setField(fkey,hzbzArray[i]);
}
}else if("bz".equals(key)){
String bz = datemap.get(key);
String[] bzArray = stringToStringArray(bz,24);
for(int i = 0; i < bzArray.length; i++){
String fkey = "bz-line" + (i+1);
form.setField(fkey,bzArray[i]);
}
}else{
String value = datemap.get(key);
form.setField(key,value);
}
}
//图片类的内容处理
Map<String, Image> imgmap = (Map<String,Image>)o.get("imgmap");
for(String key : imgmap.keySet()) {
Image value = imgmap.get(key);
//String imgpath = value;
Image image = value;
int pageNo = form.getFieldPositions(key).get(0).page;
Rectangle signRect = form.getFieldPositions(key).get(0).position;
float x = signRect.getLeft();
float y = signRect.getBottom();
//根据路径读取图片
//Image image = Image.getInstance(imgpath);
//获取图片页面
PdfContentByte under = stamper.getOverContent(pageNo);
//图片大小自适应
image.scaleToFit(signRect.getWidth(), signRect.getHeight());
//添加图片
image.setAbsolutePosition(x, y);
under.addImage(image);
}
stamper.setFormFlattening(true);// 如果为false,生成的PDF文件可以编辑,如果为true,生成的PDF文件不可以编辑
stamper.close();
Document doc = new Document(PageSize.A4, 50, 40, 40, 50);
PdfCopy copy = new PdfCopy(doc, out);
doc.open();
//form.getTotalRevisions();
int pages= stamper.getReader().getNumberOfPages();
for(int i=1;i<=pages;i++){
PdfImportedPage importPage = copy.getImportedPage(new PdfReader(bos.toByteArray()), i);
copy.addPage(importPage);
}
doc.close();
} catch (IOException e) {
log.error("pdfout",e);
} catch (DocumentException e) {
log.error("pdfout",e);
}finally {
if(out!=null){
try{
out.close();
}catch(Exception e){
}
}
if(bos!=null){
try{
bos.close();
}catch(Exception e){
}
}
}
}
// 将字符串按照指定长度分割成字符串数组
public static String[] stringToStringArray(String src, int length) {
//检查参数是否合法
if (null == src || src.equals("")) {
return null;
}
if (length <= 0) {
return null;
}
int n = (src.length() + length - 1) / length; //获取整个字符串可以被切割成字符子串的个数
String[] split = new String[n];
for (int i = 0; i < n; i++) {
if (i < (n - 1)) {
split[i] = src.substring(i * length, (i + 1) * length);
} else {
split[i] = src.substring(i * length);
}
}
return split;
}
public static byte[] inputstream2Bytes(InputStream inStream) throws IOException{
byte[] in_b = null;
try{
ByteArrayOutputStream swapStream = new ByteArrayOutputStream();
byte[] buff = new byte[100]; //buff用于存放循环读取的临时数据
int rc = 0;
while ((rc = inStream.read(buff, 0, 100)) > 0) {
swapStream.write(buff, 0, rc);
}
in_b = swapStream.toByteArray(); //in_b为转换之后的结果
}catch(Exception e){
log.error("inputstream2Bytes",e);
}finally {
inStream.close();
}
return in_b;
}
/**
* @Description: 文件转流
*/
public static InputStream file2InputStream(File file) throws IOException{
return new FileInputStream(file);
}
(2)、controller层
@Controller
@RequestMapping("/pdf")
public class PdfController {
@PostMapping("/createPdf")
@ResponseBody
public DataObject CreatePdf() throws AppException {
//1、调用数据库,获取数据
//此处写读取数据的代码
//2、将数据存为key-value
//存文字信息
Map<String,String> map = new HashMap();
//存图像信息
Map<String,Image> mapI = new HashMap();
map.put("name","田XX");
map.put("sex","女");
map.put("grbh","123456789010");
map.put("sfzh","000000000000000000");
map.put("rylb","啊哦");
map.put("time1","2021-9-27");
map.put("ddyy1","啊哦");
map.put("time2","");
map.put("ddyy2","");
map.put("hzbz","有的人一生默默无闻,有的人一生轰轰烈烈,甚至千古流芳,为什么会这样?因为默默无闻的人只是满足于现状,而不去想怎么轰轰烈烈过一生,不要求自己,去做");
map.put("bz","有的人一生默默无闻,有的人一生轰轰烈烈,甚至千古流芳,为什么会这样?因为默默无闻的人只是满足于现状,而不去想怎么轰轰烈烈过一生,不要求自己,去做");
try{
//图片路径
File pic_file = new File("F:\\1555074510295049.jpg");
Image pic_image = Image.getInstance(inputstream2Bytes(file2InputStream(pic_file)));
mapI.put("pic",pic_image);
}catch(Exception e){
}
Map<String,Object> o=new HashMap();
o.put("datemap",map);
o.put("imgmap",mapI);
// 模板路径
String templatePath = "/META-INF/resources/static/template/template.pdf";
// 生成的新文件路径
String newPDFPath = "F:\\template_sc.pdf";
pdfout(o,templatePath,newPDFPath);
return DataObject.getInstance();
}
}
(3)前端入口
layui.sight.ajaxRequest({
type : 'post',
async : true,
url : layui.sight.compileUrl('${rc.contextPath}/pdf/CreatePdf'),
data : [],
callback : function(data) {
alert("成功!")
},
failedCallback : function(data) {}
});
3、导出pdf
(1)pdfUtils类
public class PdfUtils {
private final static Logger log = LoggerFactory.getLogger(PdfUtils.class);
/**
* 利用模板生成pdf导出
*/
public static void pdfExport(Map<String, Object> o, HttpServletResponse response) {
// 模板路径
String templatePath = "/META-INF/resources/static/template/template.pdf";
File file = new File(templatePath);
if (!file.exists()) {
try {
file.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
}
PdfReader reader;
ByteArrayOutputStream bos;
PdfStamper stamper;
OutputStream out = null;
try {
BaseFont bf = BaseFont.createFont("C:/Windows/Fonts/simfang.ttf", BaseFont.IDENTITY_H, BaseFont.EMBEDDED);
// 输出流
response.setContentType("application/pdf");
response.setHeader("Content-Disposition",
"attachment;fileName=" + URLEncoder.encode("template_scsc.pdf", "UTF-8"));
out = new BufferedOutputStream(response.getOutputStream());
// 读取pdf模板
reader = new PdfReader(templatePath);
bos = new ByteArrayOutputStream();
stamper = new PdfStamper(reader, bos);
AcroFields form = stamper.getAcroFields();
//文字类的内容处理
Map<String,String> datemap = (Map<String,String>)o.get("datemap");
form.addSubstitutionFont(bf);
for(String key : datemap.keySet()){
//为了文字可以有下划线,并且换行,控制每行字数,当字数超过时,将剩余文字填充至下一备选域
if ("hzbz".equals(key)){
String hzbz = datemap.get(key);
String[] hzbzArray = stringToStringArray(hzbz,24);
for(int i = 0; i < hzbzArray.length; i++){
String fkey = "hzbz-line" + (i+1);
form.setField(fkey,hzbzArray[i]);
}
}else if("bz".equals(key)){
String bz = datemap.get(key);
String[] bzArray = stringToStringArray(bz,24);
for(int i = 0; i < bzArray.length; i++){
String fkey = "bz-line" + (i+1);
form.setField(fkey,bzArray[i]);
}
}else{
String value = datemap.get(key);
form.setField(key,value);
}
}
//图片类的内容处理
Map<String, Image> imgmap = (Map<String,Image>)o.get("imgmap");
for(String key : imgmap.keySet()) {
Image value = imgmap.get(key);
//String imgpath = value;
Image image = value;
int pageNo = form.getFieldPositions(key).get(0).page;
Rectangle signRect = form.getFieldPositions(key).get(0).position;
float x = signRect.getLeft();
float y = signRect.getBottom();
//根据路径读取图片
//Image image = Image.getInstance(imgpath);
//获取图片页面
PdfContentByte under = stamper.getOverContent(pageNo);
//图片大小自适应
image.scaleToFit(signRect.getWidth(), signRect.getHeight());
//添加图片
image.setAbsolutePosition(x, y);
under.addImage(image);
}
stamper.setFormFlattening(true);
stamper.close();
Document doc = new Document();
PdfCopy copy = new PdfCopy(doc, out);
doc.open();
PdfImportedPage importPage = copy.getImportedPage(new PdfReader(bos.toByteArray()), 1);
copy.addPage(importPage);
doc.close();
} catch (IOException | DocumentException e) {
System.out.println(e);
} finally {
try {
assert out != null;
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
// 将字符串按照指定长度分割成字符串数组
public static String[] stringToStringArray(String src, int length) {
//检查参数是否合法
if (null == src || src.equals("")) {
return null;
}
if (length <= 0) {
return null;
}
int n = (src.length() + length - 1) / length; //获取整个字符串可以被切割成字符子串的个数
String[] split = new String[n];
for (int i = 0; i < n; i++) {
if (i < (n - 1)) {
split[i] = src.substring(i * length, (i + 1) * length);
} else {
split[i] = src.substring(i * length);
}
}
return split;
}
public static byte[] inputstream2Bytes(InputStream inStream) throws IOException{
byte[] in_b = null;
try{
ByteArrayOutputStream swapStream = new ByteArrayOutputStream();
byte[] buff = new byte[100]; //buff用于存放循环读取的临时数据
int rc = 0;
while ((rc = inStream.read(buff, 0, 100)) > 0) {
swapStream.write(buff, 0, rc);
}
in_b = swapStream.toByteArray(); //in_b为转换之后的结果
}catch(Exception e){
log.error("inputstream2Bytes",e);
}finally {
inStream.close();
}
return in_b;
}
/**
* @Description: 文件转流
*/
public static InputStream file2InputStream(File file) throws IOException{
return new FileInputStream(file);
}
(2)、controller层
@Controller
@RequestMapping("/pdf")
public class PdfController {
@RequestMapping("/exportPdf")
public void exportPdf(HttpServletRequest request, HttpServletResponse response) throws AppException {
//调用数据库,获取数据
//将数据存为key-value
//存文字信息
Map<String,String> map = new HashMap();
//存图像信息
Map<String,Image> mapI = new HashMap();
map.put("name","田XX");
map.put("sex","女");
map.put("grbh","12345678910");
map.put("sfzh","00000000000000000");
map.put("rylb","啊哦");
map.put("time1","2021-9-27");
map.put("ddyy1","啊哦");
map.put("time2","");
map.put("ddyy2","");
map.put("hzbz","有的人一生默默无闻,有的人一生轰轰烈烈,甚至千古流芳,为什么会这样?因为默默无闻的人只是满足于现状,而不去想怎么轰轰烈烈过一生,不要求自己,去做");
map.put("bz","有的人一生默默无闻,有的人一生轰轰烈烈,甚至千古流芳,为什么会这样?因为默默无闻的人只是满足于现状,而不去想怎么轰轰烈烈过一生,不要求自己,去做");
try{
//图片路径
File pic_file = new File("F:\\1555074510295049.jpg");
Image pic_image = Image.getInstance(inputstream2Bytes(file2InputStream(pic_file)));
mapI.put("pic",pic_image);
}catch(Exception e){
}
Map<String,Object> o=new HashMap();
o.put("datemap",map);
o.put("imgmap",mapI);
pdfExport(o,response);
}
}
(3)前端入口
function down(){
var url = layui.sight.compileUrl('${rc.contextPath}/pdf/exportPdf');
window.open(encodeURI(url));
}
四、最终效果
图片选的不太好。。
五、出现问题
将项目打包后,启动jar包时会报空指针的错误,具体错误描述我忘记复制了呜呜呜呜。
解决方案:
修改PdfUtils。修改后:
/**
* 利用模板生成pdf导出
*/
public static void pdfExport(Map<String, Object> o, HttpServletResponse response) throws IOException {
// 生成的新文件路径
String filePath = "D:\\test.text";
// 模板路径
Resource resource = new ClassPathResource("/META-INF/resources/static/template/template.pdf");
File inuModel = new File(filePath);
FileUtils.copyInputStreamToFile(resource.getInputStream(), inuModel);
PdfReader reader;
ByteArrayOutputStream bos;
PdfStamper stamper;
OutputStream out = null;
try {
BaseFont bf = BaseFont.createFont("C:/Windows/Fonts/simfang.ttf", BaseFont.IDENTITY_H, BaseFont.EMBEDDED);
// 输出流
response.setContentType("application/pdf");
response.setHeader("Content-Disposition",
"attachment;fileName=" + URLEncoder.encode("template_scsc.pdf", "UTF-8"));
out = new BufferedOutputStream(response.getOutputStream());
// 读取pdf模板
reader = new PdfReader(filePath);
bos = new ByteArrayOutputStream();
stamper = new PdfStamper(reader, bos);
AcroFields form = stamper.getAcroFields();
//文字类的内容处理
Map<String,String> datemap = (Map<String,String>)o.get("datemap");
form.addSubstitutionFont(bf);
for(String key : datemap.keySet()){
String value = datemap.get(key);
form.setField(key,value);
}
//图片类的内容处理
Map<String, Image> imgmap = (Map<String,Image>)o.get("imgmap");
for(String key : imgmap.keySet()) {
Image value = imgmap.get(key);
//String imgpath = value;
Image image = value;
int pageNo = form.getFieldPositions(key).get(0).page;
Rectangle signRect = form.getFieldPositions(key).get(0).position;
float x = signRect.getLeft();
float y = signRect.getBottom();
//根据路径读取图片
//Image image = Image.getInstance(imgpath);
//获取图片页面
PdfContentByte under = stamper.getOverContent(pageNo);
//图片大小自适应
image.scaleToFit(signRect.getWidth(), signRect.getHeight());
//添加图片
image.setAbsolutePosition(x, y);
under.addImage(image);
}
stamper.setFormFlattening(true);
stamper.close();
Document doc = new Document();
PdfCopy copy = new PdfCopy(doc, out);
doc.open();
PdfImportedPage importPage = copy.getImportedPage(new PdfReader(bos.toByteArray()), 1);
copy.addPage(importPage);
doc.close();
} catch (IOException | DocumentException e) {
System.out.println(e);
} finally {
try {
assert out != null;
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}