itext通过模板导出pdf的研究

∥☆過路亽.° 提交于 2020-02-28 17:46:14

参考资料:

  1. iText+in+Action+2nd+Edition(Itext作者编写)
  2. ITEXT官网: https://itextpdf.com/en
  3. GitHub-Itext: https://github.com/itext

最终效果图:

package org.example;

import com.itextpdf.text.Document;
import com.itextpdf.text.Font;
import com.itextpdf.text.Phrase;
import com.itextpdf.text.Rectangle;
import com.itextpdf.text.pdf.*;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.util.ArrayList;
import java.util.HashMap;

public class App {
    // 模板路径
    static String SrcDir = "./src/test/resources/pdfs/";
    // 生成的新文件路径
    static String DestDir = "./target/sandbox/pdfs/";

    public static void main(String[] args) {
        HashMap<String, Object> classHashMap = new HashMap<>();
        ArrayList<HashMap<String, Object>> listData = new ArrayList<>();
        classHashMap.put("ClassName", "三班");
        classHashMap.put("ClassLeaderName", "王小明");
        classHashMap.put("Grade", "FirstGrade");
        classHashMap.put("BeiJing", "Yes");
        classHashMap.put("NanJing", "Yes");
        classHashMap.put("HeFei", "Yes");
        classHashMap.put("Introduce", "这是一个非常友好的班级\n我爱这个班级");
        classHashMap.put("ContactTel", "18075400516");
        classHashMap.put("ContactMail", "27972829@qq.com");
        classHashMap.put("List", listData);
        HashMap<String, Object> rowMap = new HashMap<>();
        rowMap.put("List_No", 1);
        rowMap.put("List_Name", "黄小明");
        rowMap.put("List_ID", "372925199019028939");
        rowMap.put("List_Addr", "上海市长宁区北新泾剑河路101弄1002室上海市长宁区北新泾剑河路101弄1002室");
        listData.add(rowMap);
        rowMap = new HashMap<>();
        rowMap.put("List_No", 2);
        rowMap.put("List_Name", "李小明");
        rowMap.put("List_ID", "372925199019028939");
        rowMap.put("List_Addr", "上海市普陀区延安西路100弄1001室");
        listData.add(rowMap);
        rowMap = new HashMap<>();
        rowMap.put("List_No", 3);
        rowMap.put("List_Name", "田明小");
        rowMap.put("List_ID", "372925199019038939");
        rowMap.put("List_Addr", "上海市长宁区北新泾剑河路100弄1001室上海市长宁区北新泾剑河路100弄1001室");
        listData.add(rowMap);
        rowMap = new HashMap<>();
        rowMap.put("List_No", 4);
        rowMap.put("List_Name", "黄明小");
        rowMap.put("List_ID", "372925199019028939");
        rowMap.put("List_Addr", "上海市长宁区北新泾剑河路101弄1002室");
        listData.add(rowMap);
        rowMap = new HashMap<>();
        rowMap.put("List_No", 5);
        rowMap.put("List_Name", "李小明");
        rowMap.put("List_ID", "372925199019028939");
        rowMap.put("List_Addr", "上海市普陀区延安西路100弄1001室");
        listData.add(rowMap);
        rowMap = new HashMap<>();
        rowMap.put("List_No", 6);
        rowMap.put("List_Name", "明田小");
        rowMap.put("List_ID", "372925199019038939");
        rowMap.put("List_Addr", "上海市长宁区北新泾剑河路100弄1001室上海市长宁区北新泾剑河路100弄1001室");
        listData.add(rowMap);

        String srcName = "ClassList.pdf";//"Class.pdf";
        try {
            File file = new File(SrcDir);
            if (!file.exists()) {
                file.mkdirs();
            }
            file = new File(DestDir);
            if (!file.exists()) {
                file.mkdirs();
            }

            new App().manipulatePdf(classHashMap, srcName);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /***
     * 单个HashMap生成一页PDF
     * @param hashMap
     * @param srcName
     * @throws Exception
     */
    protected void manipulatePdf(HashMap<String, Object> hashMap, String srcName) throws Exception {
        String Src = SrcDir + srcName;
        String Temp = DestDir + srcName.substring(0, srcName.indexOf(".")) + ".tmp";
        String Dest = DestDir + srcName.substring(0, srcName.indexOf(".")) + System.currentTimeMillis() + ".pdf";

        FileOutputStream fos = new FileOutputStream(Temp);
        PdfReader pReader = new PdfReader(Src);// 读取pdf模板
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        PdfStamper stamper = new PdfStamper(pReader, baos);
        AcroFields form = stamper.getAcroFields();//获取form域
        BaseFont bfChinese = BaseFont.createFont("STSongStd-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
        form.addSubstitutionFont(bfChinese);

        //base on hashmap's keys
        float tLeft = 0, tTop = 0, tWidth = 0, cHeight = 0;//表格的左上角、顶部坐标,表宽度、每行高度
        float[] floats = null;//每列列宽集合
        ArrayList<HashMap<String, Object>> listData = null;//列表数据
        String[] itemKeys = null;//列表中的key
        float[] itemLefts = null;//列表中每列的左边坐标
        for (String key : hashMap.keySet()) {
            if (key.contains("List")) {
                listData = (ArrayList<HashMap<String, Object>>) hashMap.get(key);//列表数据
                HashMap<String, Object> firstRow = listData.get(0);//头行--表格项
                itemKeys = new String[firstRow.keySet().size()];
                itemLefts = new float[firstRow.keySet().size()];

                //根据position,对items进行排序
                firstRow.keySet().toArray(itemKeys);//所有表格项
                for (int i = 0; i < itemKeys.length; i++) {
                    AcroFields.FieldPosition fPos = form.getFieldPositions(itemKeys[i]).get(0);
                    itemLefts[i] = fPos.position.getLeft();
                }
                //冒泡排序
                float max = 0;
                String temp;
                for (int i = 0; i < itemKeys.length; i++) {
                    for (int j = 0; j < itemKeys.length - 1 - i; j++) {
                        if (itemLefts[j] > itemLefts[j + 1]) {
                            max = itemLefts[j];
                            itemLefts[j] = itemLefts[j + 1];
                            itemLefts[j + 1] = max;

                            temp = itemKeys[j];
                            itemKeys[j] = itemKeys[j + 1];
                            itemKeys[j + 1] = temp;
                        }
                    }
                }

                //列表数据处理:根据首行filed,计算出每列列宽集合
                floats = new float[itemKeys.length];
                for (int i = 0; i < itemKeys.length; i++) {
                    AcroFields.FieldPosition fPos = form.getFieldPositions(itemKeys[i]).get(0);
                    if (i == 0) {//首列的左边坐标 = 表格左边坐标
                        tLeft = fPos.position.getLeft();
                        tTop = fPos.position.getBottom();
                    }
                    floats[i] = (float) (fPos.position.getWidth() + 0.7 * 2);//border+padding=0.7
                    tWidth += fPos.position.getWidth() + 0.7 * 2;
                }
            } else {
                form.setField(key, hashMap.get(key) == null ? "" : hashMap.get(key).toString());
            }
        }
        stamper.setFormFlattening(true);
        stamper.close();
        fos.write(baos.toByteArray());
        fos.flush();
        fos.close();
        baos.close();

        /***
         * read stream from temp file and write list to destination pdf
         */

        FileOutputStream outputStream = new FileOutputStream(Dest);
        PdfReader reader = new PdfReader(Temp);
        Rectangle pageSize = reader.getPageSize(1);
        Document document = new Document(pageSize);
        PdfWriter writer = PdfWriter.getInstance(document, outputStream);
        document.open();
        PdfContentByte cbUnder = writer.getDirectContentUnder();
        PdfImportedPage formPage;
        //将模板中的form数据,读取过来,支持多页。
        for (int i = 0; i < reader.getNumberOfPages(); i++) {
            if (i != 0) {
                document.newPage();
            }
            formPage = writer.getImportedPage(reader, i + 1);
            cbUnder.addTemplate(formPage, 0, 0);
        }

        if (listData != null && listData.size() > 0) {
            //BaseFont
            Font fontChinese = new Font(bfChinese, 10.5F, Font.NORMAL);// 五号
            PdfPTable table = table = new PdfPTable(floats);
            table.setTotalWidth(tWidth);
            PdfPCell cell = new PdfPCell();
            float bWidth = cell.getBorderWidth();
            int rowNum = 0;//初始row号为0
            float pageHeight = document.getPageSize().getHeight();
            float formHeight = pageHeight - tTop;
            float tTopMargin = 10, tBottomMargin = 10, rowHeight = 0;//每页上下留出10px的margin
            float tHeight = formHeight + tBottomMargin;//初始表高:Form高+底边框高,之后每新建一页,先把顶部和底部边框计算进来
            int preRowPage = (int) (tHeight / pageHeight) + 1;                          //上一个行所属页,从最后form所在页算起
            int curRowPage = 0;
            HashMap<Integer, Integer> pageMap = new HashMap<>();//<K,V>=<pageNum,endPageNo>
            for (HashMap<String, Object> rowMap : listData) {
                //handle row-cells
                for (String K : itemKeys) {
                    cell = new PdfPCell();
                    cell.addElement(new Phrase(rowMap.get(K).toString(), fontChinese));
                    table.addCell(cell);
                }

                //计算哪些行处于分页处
                rowHeight = table.getRowHeight(rowNum);
                //preRowPage = (int) (tHeight / pageHeight) + 1;                          //上一个行所属页
                curRowPage = (int) ((tHeight + rowHeight + bWidth) / pageHeight) + 1;   //如果添加当前行后,所属的页
                if (preRowPage < curRowPage) {                                          //当前行处于分页处,为下一页的第一行
                    pageMap.put(preRowPage, rowNum);                                    //rowNum:从0开始计算,rowNum:下一页的第一行码
                    tHeight += tTopMargin + tBottomMargin + bWidth * 2;                 //换页处的行,添加每页的上、下边距、头部第一个边框
                    preRowPage = curRowPage;
                }
                tHeight += rowHeight + bWidth * 2;

                rowNum++;//下一行的行码:从0开始
            }

            //分页处理:
            int pageCount = (int) (tHeight / pageHeight) + 1;
            for (int pageNo = 1; pageNo <= pageCount; pageNo++) {
                if (pageNo == 1) {//首页中,table要偏移
                    table.writeSelectedRows(0, pageMap.get(pageNo) == null ? -1 : pageMap.get(pageNo),
                            tLeft, tTop, cbUnder);
                } else {
                    document.newPage();
                    table.writeSelectedRows(pageMap.get(pageNo - 1), pageMap.get(pageNo) == null ? -1 : pageMap.get(pageNo),
                            tLeft, pageHeight - tTopMargin, cbUnder);
                }
            }
        }

        document.close();
        reader.close();

        //将临时文件删除掉
        File temp = new File(Temp);
        if (temp.exists()) {
            if (temp.isFile()) {
                temp.delete();
            }
        }
    }
}
 

pom.xml
 

<dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.11</version>
            <scope>test</scope>
        </dependency>

        <!-- add all iText 7 modules -->
        <dependency>
            <groupId>com.itextpdf</groupId>
            <artifactId>itext7-core</artifactId>
            <version>${itext.version}</version>
            <type>pom</type>
        </dependency>

        <dependency>
            <groupId>com.itextpdf</groupId>
            <artifactId>forms</artifactId>
            <version>7.1.9</version>
        </dependency>


        <dependency>
            <groupId>com.itextpdf</groupId>
            <artifactId>itextpdf</artifactId>
            <version>5.5.0</version>
            <!--5.5.9对于checkbox的形状显示会出问题-->
        </dependency>

        <dependency>
            <groupId>com.itextpdf</groupId>
            <artifactId>itext-asian</artifactId>
            <version>5.2.0</version>
        </dependency>
    </dependencies>

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!