根据PDF模板生成具体内容PDF,模板pdf需要设置字段域

发布时间 2023-05-03 10:13:51作者: LilLazy
package com.zudp.common.core.utils.pdf;

import com.itextpdf.text.DocumentException;
import com.itextpdf.text.Image;
import com.itextpdf.text.Rectangle;
import com.itextpdf.text.pdf.*;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.entity.ContentType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.web.multipart.MultipartFile;

import java.io.*;
import java.util.*;


/**
 * 根据PDF模板生成具体内容PDF
 */
public class PDFUtils {
    private static final Logger log = LoggerFactory.getLogger(PDFUtils.class);

    /**
     * @param fields
     * @param data
     * @throws IOException
     * @throws DocumentException
     */
    private static void fillData(AcroFields fields, Map<String, String> data) throws IOException, DocumentException {
        List<String> keys = new ArrayList<String>();
        Map<String, AcroFields.Item> formFields = fields.getFields();
        for (String key : data.keySet()) {
            if (formFields.containsKey(key)) {
                String value = data.get(key);
                // 为字段赋值,注意字段名称是区分大小写的
                fields.setField(key, value);
                keys.add(key);
            }
        }
        Iterator<String> itemsKey = formFields.keySet().iterator();
        while (itemsKey.hasNext()) {
            String itemKey = itemsKey.next();
            if (!keys.contains(itemKey)) {
                fields.setField(itemKey, " ");
            }
        }
    }

    /**
     * @param templatePdfPath 模板pdf路径
     * @param generatePdf    生成pdf路径
     * @param imagePath       图片路径
     * @param fitWidth        图片宽
     * @param fitHeight       图片高
     * @param data            数据
     */
    public MultipartFile getPDFMultipartFile(String templatePdfPath, String generatePdf,String imageFiledName, String imagePath,
                              Float fitWidth, Float fitHeight,Map<String, String> data) {
        ByteArrayOutputStream bos = null;
        InputStream inputStream = null ;
        MultipartFile file = null ;
        try {
            PdfReader reader = new PdfReader(templatePdfPath);
            // 将要生成的目标PDF文件名称
            bos = new ByteArrayOutputStream();
            // 使用中文字体
            PdfStamper ps = new PdfStamper(reader, bos);
            BaseFont bf = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
            ArrayList<BaseFont> fontList = new ArrayList<BaseFont>();
            // 取出报表模板中的所有字段
            fontList.add(bf);
            AcroFields fields = ps.getAcroFields();
            fields.setSubstitutionFonts(fontList);
            //设置图片
            if (StringUtils.isNotBlank(imagePath)) {
                // 通过域名获取所在页和坐标,左下角为起点
                int pageno = fields.getFieldPositions(imageFiledName).get(0).page;
                Rectangle signrect = fields.getFieldPositions(imageFiledName).get(0).position;
                float x = signrect.getLeft();
                float y = signrect.getBottom();
                // 读图片
                Image image = Image.getInstance(imagePath);
                // 获取操作的页面
                PdfContentByte under = ps.getOverContent(pageno);
                // 这里控制图片的大小
                //image.scaleToFit(signrect.getWidth(), signrect.getHeight());
                image.scaleToFit(fitWidth, fitHeight);
                // 添加图片
                image.setAbsolutePosition(x, y);
                under.addImage(image);
                //移除图片的路径,要不然会把路径也设置上去
                data.remove(imageFiledName);
            }

            // 必须要调用这个,否则文档不会生成的  如果为false那么生成的PDF文件还能编辑,一定要设为true
            fillData(fields, data);
            ps.setFormFlattening(true);
            ps.close();

            byte[] testFile = bos.toByteArray();
            inputStream = new ByteArrayInputStream(testFile);
            try {
                file = new MockMultipartFile(generatePdf, generatePdf, ContentType.APPLICATION_OCTET_STREAM.toString(), inputStream);
            } catch (IOException e) {
                log.error(e.getMessage());
            }
        } catch (Exception e) {
            log.error(e.getMessage());
        } finally {
            if(inputStream != null ){
                try {
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (bos != null) {
                try {
                    bos.close();
                } catch (IOException e) {
                    log.error(e.getMessage());
                }
            }
        }
        return file;
    }
    /**
     * 生成到本地
     * @param templatePdfPath 模板pdf路径
     * @param generatePdfPath 生成pdf路径
     * @param data            数据
     */
    public String generatePDF(String templatePdfPath, String generatePdfPath, Map<String, String> data) {
        OutputStream fos = null;
        ByteArrayOutputStream bos = null;
        try {
            PdfReader reader = new PdfReader(templatePdfPath);
            // 将要生成的目标PDF文件名称
            bos = new ByteArrayOutputStream();
            // 使用中文字体
            PdfStamper ps = new PdfStamper(reader, bos);
            BaseFont bf = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
            ArrayList<BaseFont> fontList = new ArrayList<BaseFont>();
            // 取出报表模板中的所有字段
            fontList.add(bf);
            AcroFields fields = ps.getAcroFields();
            fields.setSubstitutionFonts(fontList);
            // 必须要调用这个,否则文档不会生成的  如果为false那么生成的PDF文件还能编辑,一定要设为true
            fillData(fields, data);
            ps.setFormFlattening(true);
            ps.close();
            fos = new FileOutputStream(generatePdfPath);
            fos.write(bos.toByteArray());
            fos.flush();
            return generatePdfPath;
        } catch (Exception e) {
            log.error(e.getMessage());
        } finally {
            if (fos != null) {
                try {
                    fos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (bos != null) {
                try {
                    bos.close();
                } catch (IOException e) {
                    log.error(e.getMessage());
                }
            }
        }
        return null;
    }

    /**
     * 获取生成文件的MultipartFile
     * @param templatePdfPath
     * @param generatePdf
     * @param data
     * @return
     */
    public MultipartFile getPDFMultipartFile(String templatePdfPath, String generatePdf, Map<String, String> data) {
        ByteArrayOutputStream bos = null;
        MultipartFile file = null ;
        InputStream inputStream = null ;
        try {
            PdfReader reader = new PdfReader(templatePdfPath);
            // 将要生成的目标PDF文件名称
            bos = new ByteArrayOutputStream();
            // 使用中文字体
            PdfStamper ps = new PdfStamper(reader, bos);
            String url = "/home/data/pdf_template/simfang.ttf";
            BaseFont bf = BaseFont.createFont(url, BaseFont.IDENTITY_H,BaseFont.NOT_EMBEDDED);
            // 可能存在的问题:adober的版本不同造成中文不能显示,或者其他原因导致字体失效。该方法不是很稳妥
            // BaseFont bf = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
            ArrayList<BaseFont> fontList = new ArrayList<BaseFont>();
            // 取出报表模板中的所有字段
            fontList.add(bf);
            AcroFields fields = ps.getAcroFields();
            fields.setSubstitutionFonts(fontList);
            // 必须要调用这个,否则文档不会生成的  如果为false那么生成的PDF文件还能编辑,一定要设为true
            fillData(fields, data);
            ps.setFormFlattening(true);
            ps.close();

            byte[] testFile = bos.toByteArray();
            inputStream = new ByteArrayInputStream(testFile);
            try {
                file = new MockMultipartFile(generatePdf, generatePdf, ContentType.APPLICATION_OCTET_STREAM.toString(), inputStream);
            } catch (IOException e) {
                log.error(e.getMessage());
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if(inputStream != null ){
                try {
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (bos != null) {
                try {
                    bos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return file;
    }
}