1、模板制作

需求:java使用插件生成form表单与表格结合的pdf文件。本文采用的插件为itext。采用Adobe编辑模板的表单域域名。
   可以用word快速制作动态模板,将模板转为pdf文件并用adobe打开,点击adobe的更多工具,点击准备表单,打开表单域编辑。

一般在word制作的表格转换为pdf时,准备表单时表单域存在文本域。

注意:表单域中的字段名一定要跟代码中的对应。

2、maven依赖

      <dependency>
            <groupId>com.itextpdf</groupId>
            <artifactId>itext7-core</artifactId>
            <version>7.2.1</version>
            <type>pom</type>
        </dependency>
        <dependency>
            <groupId>com.itextpdf</groupId>
            <artifactId>itext-asian</artifactId>
            <version>5.2.0</version>
            <!--            3.0.1-->
        </dependency>
        <dependency>
            <groupId>com.itextpdf</groupId>
            <artifactId>itextpdf</artifactId>
            <version>5.5.13.2</version>
        </dependency>

3、代码实现

(1)form域填充

  这里指的是form域是静态模板,无需动态生成。此为获取表单域字段并相应填充的实现代码,其中有插入图片的需求,不需要图片可以省略insertImage这个方法,此段代码为参考,引用的原文在末尾有注。在form表单域的字段填充中,打印出的pdf字体粗细不统一,可以再逐一填充表单字段时做如下设定,以setFieldProperty设置字段属性。

 for (Map.Entry<String, String> entry : fieldMap.entrySet()) {
                if (entry.getValue() != null) {
                    form.setFieldProperty(entry.getKey(), "textfont", bf, null);
                    form.setFieldProperty(entry.getKey(), "textsize", 12f, null);
                    form.setField(entry.getKey(), entry.getValue(), true);
                }
      }
import com.itextpdf.text.*;
import com.itextpdf.text.Image;
import com.itextpdf.text.Rectangle;
import com.itextpdf.text.pdf.*;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.file.Files;
import java.util.*;

public class DevPdfFGenerator {

    public static boolean fillPdfTemplate( String template_path, String output_path, Map<String, String> fieldMap, Map<String, String> imageMap,int width, int height) {
        boolean success = true;
        OutputStream os = null;
        PdfStamper ps = null;
        PdfReader reader = null;
        AcroFields form = null;

        try {
            os = Files.newOutputStream(new File(output_path).toPath());
            reader = new PdfReader(template_path);
            ps = new PdfStamper(reader, os);
            form = ps.getAcroFields();

            // 添加中文字体支持(可选)
            BaseFont bf = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
            form.addSubstitutionFont(bf);

            // 填充表单字段
            for (Map.Entry<String, String> entry : fieldMap.entrySet()) {
                if (entry.getValue() != null) {
                    form.setField(entry.getKey(), entry.getValue(), true);
                }
            }

            // 插入图片
            for (Map.Entry<String, String> entry : imageMap.entrySet()) {
                if (entry.getValue() != null) {
                    insertImage(form, ps, entry.getKey(), entry.getValue());
                }
            }
        
        } catch (Exception e) {
            success = false;
            e.printStackTrace();  // 可以根据实际情况处理异常
        } finally {
            try {
                if (ps != null) ps.close();
                if (reader != null) reader.close();
                if (os != null) os.close();
            } catch (IOException | DocumentException e) {
                throw new RuntimeException(e);
            } finally {
                System.out.println("文件导出完成!");
            }
        }
        return success;
    }

    //插入图片的函数,fieldName为字段名,imageUrl为图片路径
    private static void insertImage(AcroFields form, PdfStamper ps, String fieldName, String imageUrl) throws IOException, DocumentException {
        if (imageUrl != null) {
            //获取fieldName所在的页码
            int pageNo = form.getFieldPositions(fieldName).get(0).page;
            Rectangle signRect = form.getFieldPositions(fieldName).get(0).position;
            float x = signRect.getLeft();
            float y = signRect.getBottom();
            Image image = Image.getInstance(imageUrl);
            // 获取操作的页面
            PdfContentByte overContent = ps.getOverContent(pageNo);
            // 根据域的大小缩放图片
            image.scaleToFit(signRect.getWidth(), signRect.getHeight());
            // 添加图片
            image.setAbsolutePosition(x, y);
            overContent.addImage(image);
            ps.setFormFlattening(true);
        }

    }

}

(2)动态列表的生成

这里需要特别注意的是:1、最开始没有特意用这段代码标识字段,中文字段并没有显示在表格中,itext可能默认不显示中文字段(有待查阅)

new PdfPCell(new Phrase("测试1", fontChinese1))

 2、表格列设置为9列,但要填充的列可能不足,若不予以填充,就无法显示内容,有两种解决方法,第一种就是默认给剩余列填充一个字符,第二种是将列合并,本文采用的是第一种方法,于列表末尾也有添加合并的字段。

注:参考的原文代码在文末有引用。

import com.itextpdf.text.*;
import com.itextpdf.text.pdf.*;
import com.jeesite.modules.sdms.entity.SdmsDeviceInspection;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.List;

public class GetDynamicTable {

//    @Test
    public static void getFinalPdf(List<SdmsDeviceInspection> tableList,String template_path,String finalPath) {
        PdfReader reader = null;// 读取pdf模板
        Document document = null;
        PdfWriter writer = null;
        FileOutputStream outputStream = null;
        FileOutputStream fos = null;
        try {
            outputStream = new FileOutputStream(finalPath);
            reader = new PdfReader(template_path);
            Rectangle pageSize = reader.getPageSize(1);
            document = new Document(pageSize);
            writer = PdfWriter.getInstance(document, outputStream);


            document.open();
            PdfContentByte cbUnder = writer.getDirectContentUnder();
            PdfImportedPage pageTemplate = writer.getImportedPage(reader, 1);
            cbUnder.addTemplate(pageTemplate, 0, 0);
            //新创建一页来存放后面生成的表格
            document.newPage();


            //页眉和table之间的间距
            Paragraph blank = new Paragraph(" ");
            document.add(blank);

            //填充动态集合数据填充
            Paragraph paragraph = generatePdfATATable(tableList);
            document.add(paragraph);

        } catch (IOException e) {
            e.printStackTrace();
        } catch (DocumentException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            document.close();
            reader.close();
        }

    }

    /**
     * 生成pdf表格
     *
     * @return
     * @see
     */
    private static Paragraph generatePdfATATable(List<SdmsDeviceInspection> tableList) throws Exception {
//        宋体
        BaseFont bfChinese = BaseFont.createFont("STSongStd-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
        //浅色不加粗
        Font fontChinese1 = new Font(bfChinese, 11F, Font.NORMAL);// 五号
        //黑色加粗
        Font fontChinese = new Font(bfChinese, 11F, Font.BOLD);// 五号
        Paragraph ret = new Paragraph("设备控制记录表(附页)", fontChinese1);
//        居中
        ret.setAlignment(Element.ALIGN_CENTER);
        PdfPTable tableBox = new PdfPTable(9);
        tableBox.setWidths(new float[]{0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f});// 每个单元格占多宽
        //设置总宽度
        tableBox.addCell(new PdfPCell(new Phrase("测试1", fontChinese1)));
        PdfPCell nameValue = new PdfPCell(new Phrase("测试2", fontChinese1));
        nameValue.setColspan(2);
        tableBox.addCell(nameValue);
        tableBox.addCell(new Phrase("测试3", fontChinese1));
        PdfPCell ecepdiNoValue = new PdfPCell(new Phrase("测试4", fontChinese1));
        ecepdiNoValue.setColspan(2);
        tableBox.addCell(ecepdiNoValue);
        tableBox.addCell(new Phrase("测试5", fontChinese1));
        PdfPCell prjName = new PdfPCell(new Phrase("测试6", fontChinese1));
        prjName.setColspan(2);
        tableBox.addCell(prjName);
        for (int i = 0; i < 3; i++) {
            tableBox.addCell(new Phrase("日期", fontChinese1));
            tableBox.addCell(new Phrase("状态", fontChinese1));
            tableBox.addCell(new Phrase("使用人", fontChinese1));
        }

        tableBox.setWidthPercentage(95f);
//         创建表格格式及内容 
        for (SdmsDeviceInspection ata : tableList) {
            tableBox.addCell(new Phrase(ata.getPhase(), fontChinese1));
            tableBox.addCell(new Phrase(ata.getName(), fontChinese1));
            tableBox.addCell(new Phrase(ata.getEcepdiNo(), fontChinese1));
        }
       if (tableList.size() % 3!= 0) {
            int index = 9 - tableList.size()%3 * 3;
            while (index > 0) {
                tableBox.addCell(new PdfPCell(new Phrase("", fontChinese1))); // 空单元格
                index--;
            }
        }
        PdfPCell lastText = new PdfPCell(new Phrase("注释", fontChinese1));
        lastText.setColspan(9);
        tableBox.addCell(lastText);
        ret.add(tableBox);
        return ret;
    }


    /**
     * 设置页眉信息;填充logo
     */
    private static class Header extends PdfPageEventHelper {
        public static PdfPTable header;

        public Header(PdfPTable header) {
            Header.header = header;
        }

        @Override
        public void onEndPage(PdfWriter writer, Document document) {
            // 把页眉表格定位
            header.writeSelectedRows(0, -1, 30, 806, writer.getDirectContent());
        }
}

 方法调用

 注意:这里的表单生成的模板文件为原始模板,而动态列表需要采用表单生成的pdf文件。(后续会会再优化,大家有什么优化的方法可以一起讨论 欢迎指正)

 public String getPdf( SdmsDeviceReturn sdmsDeviceReturn ) throws FileNotFoundException {
        String output_path = "/D:/###/text.pdf";
        String template_path ="/static/common/测试模板-表格.pdf";
        //表单中的文本数据map
        Map<String, String> form = new HashMap<>();
        //表单中图片map
        Map<String, String> image = new HashMap<>();
      
        //表单内容
        form.put("fill5","1");
       
//图片
        image.put("imgUser", "D:/###/测试图片.jpg");
        image.put("imgCustodian", "D:/###/测试图片.jpg");
        image.put("imgApply", "D:/###/测试图片.jpg");

        DevPdfFGenerator.fillPdfTemplate(template_path, output_path, form, image, 300, 300);
//列表内容
          List<SdmsDeviceInspection> tableList = new ArrayList<>();
          SdmsDeviceInspection inspection = new SdmsDeviceInspection();
          inspection.setEcepdiNo("ceshi");
          inspection.setName("测试名称");
          tableList.add(inspection);
        String templatePath="/D:/###/text.pdf";
        String finalPath="/D:/###/text2.pdf";
        // 表格数据渲染
        GetDynamicTable.getFinalPdf(tableList,templatePath,finalPath);
        return "pdf生成成功";
    }

4、最终呈现

  下图为form表单呈现效果


下图为动态生成列表呈现效果。


form引用:JAVA操作Itext向pdf模板中填充数据,图片,复选框!_java填充pdf-CSDN博客Java使用itext按模板生成PDF文件_51CTO博客_java生成pdf文件 动态表格引用:java操作pdf模板并添加动态集合列表数据_java 动态表格 pdf-CSDN博客

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐