基于itext 7对PDF进行处理(包含图片填充)

本文介绍使用iText7处理PDF文件,重点讲解如何优雅地处理图片和文本填充,包括MultipartFile转File、PDF表单域处理、图片等比压缩及位置调整等关键技术点。

对itext的使用也是从itext 5开始,一点一点的拼凑出来的。做到后面发现业务上需要对图片进行处理,一直没有找到比较好的解决办法,最后入了itext 7的坑。
先说一说 itext 7的优点吧。代码量比较少,很简洁。对于图片的处理也比较优雅。哈哈哈哈哈哈哈哈说的太闷骚了
在这里插入图片描述
废话不多说,开干吧。刚吧嘚
首先pom导入itext 7大礼包:

<dependencies>
		<dependency>
			<groupId>com.itextpdf</groupId>
			<artifactId>itext7-core</artifactId>
			<version>7.1.8</version>
			<type>pom</type>
		</dependency>
</dependencies>

然后是PDF的主方法,参数是模板,response(返回文件给前端要用到),和String字符串格式的数据。

 /**
     * @param MultipartFile 设置好文本域的模板文件
     * @param str        填充的数据
     * @throws IOException
     */
    @PostMapping("/printPDF")
    public void interviewReportPDF(@RequestParam MultipartFile templateFile, HttpServletResponse response,@RequestParam String str) {
        Map<String, Object> entity = new HashMap<>();
        File file = null;
        String newPDFPath = null;
        File template = null;

        try {
            entity = objectToMap(str);//把对象转成map
            newPDFPath = "C:\\Users\\Administrator\\Desktop"+System.currentTimeMillis()+".pdf";//在本地生成文件,后续可以做到删除这些缓存文件
            template =MultipartFileToFile.multipartFileToFile(templateFile);
            file = replaceImageFieldPdf(template, newPDFPath, entity);
        } catch (Exception e) {
            e.printStackTrace();
        }

        //返回给前端
        response.reset();
        response.setHeader("ETag", String.valueOf(System.currentTimeMillis()));
        response.setDateHeader("Last-Modified", System.currentTimeMillis());
        response.setHeader("Access-Control-Allow-Origin", "*");
        FileInputStream in = null;
        OutputStream out = null;
        try {
            in = new FileInputStream(file);
            out = response.getOutputStream();
            long fileLen = in.available();
            response.setHeader("Content-Length", String.valueOf(fileLen));
            int len;
            byte[] bs = new byte[1024];
            while ((len = in.read(bs)) != -1) {
                out.write(bs, 0, len);
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            if (in != null) {
                try {
                    in.close();
                } catch (IOException e) {
                }
            }
        }
    }

主方法里面第一个调用的就是objectToMap(str);//把对象转成map,这里面主要是将Object转成Map,然后根据业务对数据做处理。可以根据自己的需求定制。

/**
     * 字段处理
     * 1.将Object对象里面的属性和值转化成Map对象
     * 2.时间转换,
     * 3.签名列表转换
     * 4.当事人信息转换
     *
     * @param obj
     * @return
     * @throws IllegalAccessException
     */
    public Map<String, Object> objectToMap(String str) throws IllegalAccessException {
        Map<String, Object> map = new HashMap<String, Object>();

        SimpleDateFormat formatter = new SimpleDateFormat("yyyy年MM月dd日 HH时mm分ss秒");

        map=(Map<String, Object>)JSONObject.parse(str);
        for(Map.Entry<String,Object> m:map.entrySet()){
            boolean time = m.getKey().startsWith("time");
            if(time){
                Long ll = Long.parseLong(m.getValue().toString());
                String s = DateUtils.timestarmpToStrSDF(ll, DateUtils.yyyy_MM_dd_wzsj);
                map.put(m.getKey(),s);
            }
        }

        //对签名列表做处理
        if (map.get("signList") != null) {
            List<Map<String,Object>> signList = (List<Map<String,Object>>) map.get("signList");
            for (int i = 0; i < signList.size(); i++) {
                Map<String, Object> stringObjectMap = signList.get(i);
                if(stringObjectMap.get("roleType")!=null){
                    map.put(stringObjectMap.get("roleType") + "Content", stringObjectMap.get("content"));
                    map.put("img_" + stringObjectMap.get("roleType") + "CasePeopleUrl",stringObjectMap.get("casePeopleUrl"));
                    if (stringObjectMap.get("timeSign") != null) {
                        Long ll = Long.parseLong(stringObjectMap.get("timeSign").toString());
                        String s = DateUtils.timestarmpToStrSDF(ll, DateUtils.yyyy_MM_dd_wzsj);
                        map.put(stringObjectMap.get("roleType") + "TimeSign",s);
                    }
                }
            }
        }

        //对当事人信息做处理
        if (map.get("caseEnterprise") != null) {
            Map<String,Object> caseEnterprise= (Map<String,Object>)map.get("caseEnterprise");
            for(Map.Entry<String,Object> m:caseEnterprise.entrySet()){
                map.put("caseEnterprise" +m.getKey().substring(0, 1).toUpperCase()+m.getKey().substring(1), m.getValue());
            }
        }

        return map;
    }

主方法里面接着是对前段传过来的文件做处理,我直接扒的一个工具类,将MultipartFile 转 File,以及删除本地生成的临时文件。

import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;

public class MultipartFileToFile {


    /**
     * MultipartFile 转 File
     *
     * @param file
     * @throws Exception
     */
    public static File multipartFileToFile(MultipartFile file) throws Exception {

        File toFile = null;
        if (file.equals("") || file.getSize() <= 0) {
            file = null;
        } else {
            InputStream ins = null;
            ins = file.getInputStream();
            toFile = new File(file.getOriginalFilename());
            inputStreamToFile(ins, toFile);
            ins.close();
        }
        return toFile;
    }

    //获取流文件
    private static void inputStreamToFile(InputStream ins, File file) {
        try {
            OutputStream os = new FileOutputStream(file);
            int bytesRead = 0;
            byte[] buffer = new byte[8192];
            while ((bytesRead = ins.read(buffer, 0, 8192)) != -1) {
                os.write(buffer, 0, bytesRead);
            }
            os.close();
            ins.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 删除本地临时文件
     * @param file
     */
    public static void delteTempFile(File file) {
        if (file != null) {
            File del = new File(file.toURI());
            del.delete();
        }

    }

}

接下来的才是PDF填充文本域和图片等处理。

   /**
     * 替换PDF图片表单域(文本)变量,1、获取表单域的大小;2、根据表单域的位置,确定图片的位置;3、如果图片的宽或者高大于表单域,则等比压缩图片。
     *
     * @param templatePdfPath 要替换的pdf全路径
     * @param params          替换参数
     * @param destPdfPath     替换后保存的PDF全路径
     * @throws FileNotFoundException
     * @throws IOException
     */
    public File replaceImageFieldPdf(File templatePdf, String destPdfPath, Map<String, Object> params) throws FileNotFoundException, IOException {
        PdfDocument pdf = new PdfDocument(new PdfReader(templatePdf), new PdfWriter(destPdfPath));

        if (params != null && !params.isEmpty()) {// 有参数才替换
            PdfAcroForm form = PdfAcroForm.getAcroForm(pdf, true);
            Map<String, PdfFormField> fields = form.getFormFields(); // 获取所有的表单域
            for (String param : params.keySet()) {
                PdfFormField formField = fields.get(param);
                if (formField != null && params.get(param) != null) {
                    System.out.println(formField);
                    String[] s = param.split("_");
                    if (s[0].equals("img")) {
                        replaceFieldImage(params, pdf, param, formField); // 替换图片
                    } else {
                        formField.setValue(params.get(param).toString(), true); // 替换值,generateAppearance为true是设置单选框复选框的值
                    }
                }
            }
            form.flattenFields();// 锁定表单,不让修改
        }
        pdf.close();

        return new File(destPdfPath);
    }

    /**
     * 替换域中的图片
     *
     * @param params
     * @param pdf
     * @param param
     * @param formField
     * @throws MalformedURLException
     */
    private void replaceFieldImage(Map<String, Object> params, PdfDocument pdf, String param, PdfFormField formField) throws MalformedURLException {
        String value = params.get(param).toString();
        byte[] imgDate = fileFacade.getFileDate(value);
        Rectangle rectangle = formField.getWidgets().get(0).getRectangle().toRectangle(); // 获取表单域的xy坐标
        PdfCanvas canvas = new PdfCanvas(pdf.getPage(1));
        ImageData image = ImageDataFactory.create(imgDate);
        float imageWidth = image.getWidth();
        float imageHeight = image.getHeight();
        float rectangleWidth = rectangle.getWidth();
        float rectangleHeight = rectangle.getHeight();

        float tempWidth = 0;
        float tempHeight = 0;

        int result = 1; // 压缩宽度
        if (imageWidth > rectangleWidth) {
            tempHeight = imageHeight * rectangleWidth / imageWidth;
            if (tempHeight > rectangleHeight) {
                tempHeight = rectangleHeight;
                result = 2; // 压缩高度
            } else {
                tempWidth = rectangleWidth;
                tempHeight = imageHeight * rectangleWidth / imageWidth;
            }
        } else {
            if (imageHeight > rectangleHeight) {
                tempHeight = rectangleHeight;
                result = 2;
            } else {
                result = 3;
            }
        }

        float y = 0;

        if (result == 1) { // 压缩宽度
            y = rectangleHeight - tempHeight;
        } else if (result == 3) { // 不压缩
            y = rectangleHeight - imageHeight;
        }

        // y/=2; // 如果想要图片在表单域的上下对齐,这个值除以2就行。同理可以计算x的偏移

        if (result == 1) {
            canvas.addImage(image, rectangle.getX(), rectangle.getY() + y, tempWidth, false);
        } else if (result == 2) {
            canvas.addImage(image, rectangle.getX(), rectangle.getY(), tempHeight, false, false);
        } else if (result == 3) {
            canvas.addImage(image, rectangle.getX(), rectangle.getY() + y, false);
        }
    }

作为大自然的搬运工,要感谢各位大神引路。这个方法可以生成本地文件同时返回给前端,不管是测试模板还是用于打印填充PDF都比较方便。希望各位多多指教!

评论 4
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值