Java如何实现Pdf的导出?

在某些场景中,我们需要从数据库或其他来源获取的数据信息,动态的导出Pdf文件,如准考证的打印等。这时我们需要借助第三方依赖包—itextpdf 来实现此需求。

一、制作PDF模板

1、在Word内制作模板

因为PDF常用的软件不支持编辑,所以先用Word工具,如WPS或者Office新建一个空白Word文档,里面制作出自己想要的样式。

java导出pdf生成方框 java实现pdf导出_java

2、将Word转换成PDF形式

将设置好的Word文档转换成PDF形式,保存起来。

java导出pdf生成方框 java实现pdf导出_itext_02

3、编辑PDF准备表单

用Adobe Acrobat DC 软件打开保存好的PDF模板文件,点击右下角的更多工具按钮

java导出pdf生成方框 java实现pdf导出_java导出pdf生成方框_03

进入到此页面,点击“准备表单”按钮。

java导出pdf生成方框 java实现pdf导出_xpdf_04

接下来进行详细的配置数据源(注意,配置的数据源字段必须与Java中的实体类对象的字段名保持一致)

另外注意:在要显示图像的区域,点击鼠标右键,选择文本域,设定好图像的显示位置,并指定数据源字段

java导出pdf生成方框 java实现pdf导出_itext_05

配置完成之后保存,留作模板使用。

二、编写代码

在准备好PDF模板之后,即可以开始编写代码来实现PDF的导出

1、导入依赖

<dependency>
    <groupId>com.itextpdf</groupId>
    <artifactId>itextpdf</artifactId>
    <version>5.5.13</version>
</dependency>

2、实体类

import lombok.Data;

/**
 * @since 2021/7/11 15:33
 */
@Data
public class AdmissionCard {

    /**
     * 准考证号
     */
    private String no;
    /**
     * 考生姓名
     */
    private String name;
    /**
     * 考生性别
     */
    private String sex;
    /**
     * 身份证号
     */
    private String idCard;
    /**
     * 学习工作单位
     */
    private String school;
    /**
     * 报考单位
     */
    private String enterSchool;
    /**
     * 报考专业
     */
    private String major;
    /**
     * 报考点
     */
    private String enterName;
    /**
     * 考试地点
     */
    private String examAddress;
    /**
     * 考生头像
     */
    private String studentImg;
}

3、Service层代码的实现

/**
 * @since 2021/7/11 16:12
 */
public interface PdfCustomService {

    /**
     * 生成准考证PDF
     * @param admissionCard 准考证信息
     * @param response 响应
     */
    void generatorAdmissionCard(AdmissionCard admissionCard, HttpServletResponse response) throws UnsupportedEncodingException, FileNotFoundException;
}
import cn.ecut.file.pdf.entity.AdmissionCard;
import cn.ecut.file.pdf.service.PdfCustomService;
import com.itextpdf.text.Document;
import com.itextpdf.text.Image;
import com.itextpdf.text.Rectangle;
import com.itextpdf.text.pdf.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Map;

/**
 * @since 2021/7/11 16:12
 */
@Service
@Slf4j
public class PdfCustomServiceImpl implements PdfCustomService {


    @Override
    public void generatorAdmissionCard(AdmissionCard admissionCard, HttpServletResponse response) throws UnsupportedEncodingException, FileNotFoundException {
        // 模板名称
        String templateName = "准考证-模板.pdf";
        String path = "";
        // 获取操作系统名称,根据系统名称确定模板存放的路径
        String systemName = System.getProperty("os.name");
        if(systemName.toUpperCase().startsWith("WIN")){
            path = "D:/pdf/";
        }else {
            path = "/usr/local/pdf/";
        }
        // 生成导出PDF的文件名称
        String fileName = admissionCard.getName() + "-硕士准考证.pdf";
        fileName = URLEncoder.encode(fileName, "UTF-8");
        // 设置响应头
        response.setContentType("application/force-download");
        response.setHeader("Content-Disposition",
                "attachment;fileName=" + fileName);
        OutputStream out = null;
        ByteArrayOutputStream bos = null;
        PdfStamper stamper = null;
        PdfReader reader = null;
        try {
            // 保存到本地
            // out = new FileOutputStream(fileName);
            // 输出到浏览器端
            out = response.getOutputStream();
            // 读取PDF模板表单
            reader = new PdfReader(path + templateName);
            // 字节数组流,用来缓存文件流
            bos = new ByteArrayOutputStream();
            // 根据模板表单生成一个新的PDF
            stamper = new PdfStamper(reader, bos);
            // 获取新生成的PDF表单
            AcroFields form = stamper.getAcroFields();
            // 给表单生成中文字体,这里采用系统字体,不设置的话,中文显示会有问题
            BaseFont font = BaseFont.createFont("C:/WINDOWS/Fonts/SIMSUN.TTC,1", BaseFont.IDENTITY_H, BaseFont.EMBEDDED);
            form.addSubstitutionFont(font);
            // 装配数据
            Map<String, Object> data = new HashMap<>(15);
            data.put("no", admissionCard.getNo());
            data.put("name", admissionCard.getName());
            data.put("sex", admissionCard.getSex());
            data.put("idCard", admissionCard.getIdCard());
            data.put("school", admissionCard.getSchool());
            data.put("enterSchool", admissionCard.getEnterSchool());
            data.put("examAddress", admissionCard.getExamAddress());
            data.put("major", admissionCard.getMajor());
            data.put("enterName", admissionCard.getEnterName());
            data.put("studentImg", admissionCard.getStudentImg());
            // 遍历data,给pdf表单赋值
            for(String key : data.keySet()){
                // 图片要单独处理
                if("studentImg".equals(key)){
                    int pageNo = form.getFieldPositions(key).get(0).page;
                    Rectangle signRect = form.getFieldPositions(key).get(0).position;
                    float x = signRect.getLeft();
                    float y = signRect.getBottom();
                    String studentImage = data.get(key).toString();
                    //根据路径或Url读取图片
                    Image image = Image.getInstance(studentImage);
                    //获取图片页面
                    PdfContentByte under = stamper.getOverContent(pageNo);
                    //图片大小自适应
                    image.scaleToFit(signRect.getWidth(), signRect.getHeight());
                    //添加图片
                    image.setAbsolutePosition(x, y);
                    under.addImage(image);
                }
                // 设置普通文本数据
                else {
                    form.setField(key, data.get(key).toString());
                }
            }
            // 表明该PDF不可修改
            stamper.setFormFlattening(true);
            // 关闭资源
            stamper.close();
            // 将ByteArray字节数组中的流输出到out中(即输出到浏览器)
            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();
            log.info("*****************************PDF导出成功*********************************");
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            try {
                if (out != null) {
                    out.flush();
                    out.close();
                }
                if (reader != null) {
                    reader.close();
                }
            }catch (Exception e){
                e.printStackTrace();
            }
        }
    }
}

4、Controller层的实现

import cn.ecut.file.pdf.entity.AdmissionCard;
import cn.ecut.file.pdf.service.PdfCustomService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletResponse;
import java.io.FileNotFoundException;
import java.io.UnsupportedEncodingException;

/**
 * @since 2021/7/11 16:39
 */
@RestController
@Api(value = "PDF相关操作接口", tags = "PDF相关操作接口")
@RequestMapping("/pdf")
public class PdfController {

    @Autowired
    private PdfCustomService pdfCustomService;

    @ApiOperation(value = "导出PDF")
    @PostMapping("/admissioncard")
    public void generatorAdmissionCard(@RequestBody AdmissionCard admissionCard, HttpServletResponse response){
        try {
            pdfCustomService.generatorAdmissionCard(admissionCard, response);
        } catch (UnsupportedEncodingException | FileNotFoundException e) {
            e.printStackTrace();
        }
    }
}

三、测试效果

请求接口,动态传递数据从而导出不同数据的PDF文档

java导出pdf生成方框 java实现pdf导出_java_06

下载响应的PDF文档

java导出pdf生成方框 java实现pdf导出_java_07

结果正确,完美实现PDF的动态导出

java导出pdf生成方框 java实现pdf导出_java导出pdf生成方框_08