本文简介

本文次要介绍了在SpringBoot我的项目下,通过代码和操作步骤,具体的介绍了如何操作PDF。心愿能够帮忙到筹备通过JAVA操作PDF的你。

我的项目框架用的SpringBoot,但在JAVA中代码都是通用的。

本文波及pdf操作,如下:

  • PDF模板制作
  • 基于PDF模板生成,并反对下载
  • 自定义中文字体
  • 齐全基于代码生成,并保留到指定目录
  • 合并PDF,并保留到指定目录
  • 合并PDF,并反对下载

基于PDF模板生成:实用于固定格局的PDF模板,基于内容进行填空,例如:合同信息生成、固定格局表格等等

齐全基于代码生成:实用于不固定的PDF,例如:动静表格、动静增加某块内容、不确定的内容大小等不确定的场景

PDF文件简介

PDF是可移植文档格局,是一种电子文件格式,具备许多其余电子文档格局无奈相比的长处。PDF文件格式能够将文字、字型、格局、色彩及独立于设施和分辨率的图形图像等封装在一个文件中。该格式文件还能够蕴含超文本链接、声音和动静影像等电子信息,反对专长文件,集成度和平安可靠性都较高。在零碎开发中通常用来生成比拟正式的报告或者合同类的电子文档。

代码实现PDF操作

首先须要引入咱们的依赖,这里通过maven治理依赖

在pom.xml文件增加以下依赖

<!--pdf操作--><!-- https://mvnrepository.com/artifact/com.itextpdf/itextpdf --><dependency>    <groupId>com.itextpdf</groupId>    <artifactId>itextpdf</artifactId>    <version>${itextpdf-version}</version></dependency>

基于PDF模板生成,并下载

PDF模板制作

  1. 首先在word或者其他软件外面制作模板,筛选你相熟的软件即可,前提是可生成pdf。

  1. 将word文件转为pdf文件。

  1. 应用Adobe Acrobat软件操作pdf,这里用的是这个软件,只有能实现这个性能,其余的软件也可~

    抉择表单编辑哈,咱们要在对应的坑上增加表单占位

  1. 在表单上增加文本域即可,所有的格局都用文本域即可,这里只是占坑。

    对应的域名称要与程序的名称对应,不便前面数据填充,不然前面须要手动解决赋值。

  1. 创立个简略的模板吧,要留神填充的空间要短缺,不然会呈现数据展现不全呦~

    成果如下:

好了,到这里模板就生成好了,咱们保留一下,而后放在咱们的/resources/templates目录下

PDF生成代码编写

util包下创立PdfUtil.java工具类,代码如下:

package com.maple.demo.util;import com.itextpdf.text.Document;import com.itextpdf.text.DocumentException;import com.itextpdf.text.Image;import com.itextpdf.text.Rectangle;import com.itextpdf.text.pdf.*;import javax.servlet.ServletOutputStream;import java.io.ByteArrayOutputStream;import java.io.IOException;import java.util.Map;import java.util.Objects;/** * @author 笑小枫 * @date 2022/8/15 * @see <a href="https://www.xiaoxiaofeng.com">https://www.xiaoxiaofeng.com</a> */public class PdfUtil {    private PdfUtil() {    }    /**     * 利用模板生成pdf     *     * @param data         写入的数据     * @param photoMap     图片信息     * @param out          自定义保留pdf的文件流     * @param templatePath pdf模板门路     */    public static void fillTemplate(Map<String, Object> data, Map<String, String> photoMap, ServletOutputStream out, String templatePath) {        PdfReader reader;        ByteArrayOutputStream bos;        PdfStamper stamper;        try {            // 读取pdf模板            reader = new PdfReader(templatePath);            bos = new ByteArrayOutputStream();            stamper = new PdfStamper(reader, bos);            AcroFields acroFields = stamper.getAcroFields();            // 赋值            for (String name : acroFields.getFields().keySet()) {                String value = data.get(name) != null ? data.get(name).toString() : null;                acroFields.setField(name, value);            }            // 图片赋值            for (Map.Entry<String, String> entry : photoMap.entrySet()) {                if (Objects.isNull(entry.getKey())) {                    continue;                }                String key = entry.getKey();                String url = entry.getValue();                // 依据地址读取须要放入pdf中的图片                Image image = Image.getInstance(url);                // 设置图片在哪一页                PdfContentByte overContent = stamper.getOverContent(acroFields.getFieldPositions(key).get(0).page);                // 获取模板中图片域的大小                Rectangle signRect = acroFields.getFieldPositions(key).get(0).position;                float x = signRect.getLeft();                float y = signRect.getBottom();                // 图片等比缩放                image.scaleAbsolute(signRect.getWidth(), signRect.getHeight());                // 图片地位                image.setAbsolutePosition(x, y);                // 在该页退出图片                overContent.addImage(image);            }            // 如果为false那么生成的PDF文件还能编辑,肯定要设为true            stamper.setFormFlattening(true);            stamper.close();            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();            bos.close();        } catch (IOException | DocumentException e) {            e.printStackTrace();        }    }}

controller包下创立TestPdfController.java类,并i代码如下:

package com.maple.demo.controller;import com.maple.demo.util.PdfUtil;import io.swagger.annotations.Api;import io.swagger.annotations.ApiOperation;import lombok.extern.slf4j.Slf4j;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;import javax.servlet.ServletOutputStream;import javax.servlet.http.HttpServletResponse;import java.io.IOException;import java.util.HashMap;import java.util.Map;/** * @author 笑小枫 * @date 2022/8/15 * @see <a href="https://www.xiaoxiaofeng.com">https://www.xiaoxiaofeng.com</a> */@Slf4j@RestController@RequestMapping("/example")@Api(tags = "实例演示-PDF操作接口")public class TestPdfController {    @ApiOperation(value = "依据PDF模板导出PDF")    @GetMapping("/exportPdf")    public void exportPdf(HttpServletResponse response) {        Map<String, Object> dataMap = new HashMap<>(16);        dataMap.put("nickName", "笑小枫");        dataMap.put("age", 18);        dataMap.put("sex", "男");        dataMap.put("csdnUrl", "https://zhangfz.blog.csdn.net/");        dataMap.put("siteUrl", "https://www.xiaoxiaofeng.com/");        dataMap.put("desc", "大家好,我是笑小枫。");        Map<String, String> photoMap = new HashMap<>(16);        photoMap.put("logo", "https://profile.csdnimg.cn/C/9/4/2_qq_34988304");        // 设置response参数,能够关上下载页面        response.reset();        response.setCharacterEncoding("UTF-8");        // 定义输入类型        response.setContentType("application/PDF;charset=utf-8");        // 设置名称        response.setHeader("Content-Disposition", "attachment; filename=" + "xiaoxiaofeng.pdf");        try {            ServletOutputStream out = response.getOutputStream();            // 模板门路记            PdfUtil.fillTemplate(dataMap, photoMap, out, "src/main/resources/templates/xiaoxiaofeng.pdf");        } catch (IOException e) {            e.printStackTrace();        }    }}

重启我的项目,在浏览器拜访:http://localhost:6666/example/exportPdf

导出的文件成果如下:

齐全基于代码生成,并保留

齐全基于代码生成PDF文件,这个就比拟定制化了,这里只讲常见的操作,大家能够用作参考:

自定义字体

PDF生成的时候,有没有遇到过失落中文字体的问题呢,唉~解决一下

先下载字体,这里用黑体演示哈,下载地址:https://www.zitijia.com/downloadpage?itemid=281258939050380345

下载完会失去一个Simhei.ttf文件,对咱们就是要它

先在util包下创立一个字体工具类吧,代码如下:

留神:代码中相干的绝对路径要替换成本人的
package com.maple.demo.util;import com.itextpdf.text.*;import com.itextpdf.text.pdf.BaseFont;import com.itextpdf.text.pdf.PdfPTable;import java.io.IOException;import java.util.List;/** * @author 笑小枫 * @date 2022/8/15 * @see <a href="https://www.xiaoxiaofeng.com">https://www.xiaoxiaofeng.com</a> */public class PdfFontUtil {    private PdfFontUtil() {    }    /**     * 根底配置,能够放相对路径,这里演示绝对路径,因为字体文件过大,这里不传到我的项目外面了,须要的本人下载     * 下载地址:https://www.zitijia.com/downloadpage?itemid=281258939050380345     */    public static final String FONT = "D:\\font/Simhei.ttf";    /**     * 根底款式     */    public static final Font TITLE_FONT = FontFactory.getFont(FONT, BaseFont.IDENTITY_H, 20, Font.BOLD);    public static final Font NODE_FONT = FontFactory.getFont(FONT, BaseFont.IDENTITY_H, 15, Font.BOLD);    public static final Font BLOCK_FONT = FontFactory.getFont(FONT, BaseFont.IDENTITY_H, 13, Font.BOLD, BaseColor.BLACK);    public static final Font INFO_FONT = FontFactory.getFont(FONT, BaseFont.IDENTITY_H, 12, Font.NORMAL, BaseColor.BLACK);    public static final Font CONTENT_FONT = FontFactory.getFont(FONT, BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);    /**     * 段落款式获取     */    public static Paragraph getParagraph(String content, Font font, Integer alignment) {        Paragraph paragraph = new Paragraph(content, font);        if (alignment != null && alignment >= 0) {            paragraph.setAlignment(alignment);        }        return paragraph;    }    /**     * 图片款式     */    public static Image getImage(String imgPath, float width, float height) throws IOException, BadElementException {        Image image = Image.getInstance(imgPath);        image.setAlignment(Image.MIDDLE);        if (width > 0 && height > 0) {            image.scaleAbsolute(width, height);        }        return image;    }    /**     * 表格生成     */    public static PdfPTable getPdfTable(int numColumns, float totalWidth) {        // 表格解决        PdfPTable table = new PdfPTable(numColumns);        // 设置表格宽度比例为%100        table.setWidthPercentage(100);        // 设置宽度:宽度均匀        table.setTotalWidth(totalWidth);        // 锁住宽度        table.setLockedWidth(true);        // 设置表格下面空白宽度        table.setSpacingBefore(10f);        // 设置表格上面空白宽度        table.setSpacingAfter(10f);        // 设置表格默认为无边框        table.getDefaultCell().setBorder(0);        table.setPaddingTop(50);        table.setSplitLate(false);        return table;    }    /**     * 表格内容带款式     */    public static void addTableCell(PdfPTable dataTable, Font font, List<String> cellList) {        for (String content : cellList) {            dataTable.addCell(getParagraph(content, font, -1));        }    }}

pdf生成工具类

持续在util包下的PdfUtil.java工具类里增加,因为本文波及到的操作比拟多,这里只贴相干代码,最初对立贴一个实现的文件

留神:代码中相干的绝对路径要替换成本人的
    /**     * 创立PDF,并保留到指定地位     *     * @param filePath 保留门路     */    public static void createPdfPage(String filePath) {        // FileOutputStream 须要敞开,开释资源        try (FileOutputStream outputStream = new FileOutputStream(filePath)) {            // 创立文档            Document document = new Document();            PdfWriter writer = PdfWriter.getInstance(document, outputStream);            document.open();            // 报告题目            document.add(PdfFontUtil.getParagraph("笑小枫的网站介绍", TITLE_FONT, 1));            document.add(PdfFontUtil.getParagraph("\n网站名称:笑小枫(www.xiaoxiaofeng.com)", INFO_FONT, -1));            document.add(PdfFontUtil.getParagraph("\n生成工夫:2022-07-02\n\n", INFO_FONT, -1));            // 报告内容            // 段落题目 + 报表图            document.add(PdfFontUtil.getParagraph("文章数据统计", NODE_FONT, -1));            document.add(PdfFontUtil.getParagraph("\n· 网站首页图\n\n", BLOCK_FONT, -1));            // 设置图片宽高            float documentWidth = document.getPageSize().getWidth() - document.leftMargin() - document.rightMargin();            float documentHeight = documentWidth / 580 * 320;            document.add(PdfFontUtil.getImage("D:\\xiaoxiaofeng.jpg", documentWidth - 80, documentHeight - 80));            // 数据表格            document.add(PdfFontUtil.getParagraph("\n· 数据详情\n\n", BLOCK_FONT, -1));            // 生成6列的表格            PdfPTable dataTable = PdfFontUtil.getPdfTable(6, 500);            // 设置表格            List<String> tableHeadList = tableHead();            List<List<String>> tableDataList = getTableData();            PdfFontUtil.addTableCell(dataTable, CONTENT_FONT, tableHeadList);            for (List<String> tableData : tableDataList) {                PdfFontUtil.addTableCell(dataTable, CONTENT_FONT, tableData);            }            document.add(dataTable);            document.add(PdfFontUtil.getParagraph("\n· 报表形容\n\n", BLOCK_FONT, -1));            document.add(PdfFontUtil.getParagraph("数据报告能够监控每天的推广状况," +                    "能够针对不同的数据体现进行剖析,以晋升推广成果。", CONTENT_FONT, -1));            document.newPage();            document.close();            writer.close();        } catch (Exception e) {            e.printStackTrace();        }    }    /**     * 模仿数据     */    private static List<String> tableHead() {        List<String> tableHeadList = new ArrayList<>();        tableHeadList.add("省份");        tableHeadList.add("城市");        tableHeadList.add("数量");        tableHeadList.add("百分比1");        tableHeadList.add("百分比2");        tableHeadList.add("百分比3");        return tableHeadList;    }    /**     * 模仿数据     */    private static List<List<String>> getTableData() {        List<List<String>> tableDataList = new ArrayList<>();        for (int i = 0; i < 3; i++) {            List<String> tableData = new ArrayList<>();            tableData.add("浙江" + i);            tableData.add("杭州" + i);            tableData.add("276" + i);            tableData.add("33.3%");            tableData.add("34.3%");            tableData.add("35.3%");            tableDataList.add(tableData);        }        return tableDataList;    }

controller包下的TestPdfController.java类中增加代码,因为本文波及到的操作比拟多,这里只贴相干代码,最初对立贴一个实现的文件,相干代码如下:

留神:代码中相干的绝对路径要替换成本人的
    @ApiOperation(value = "测试纯代码生成PDF到指定目录")    @GetMapping("/createPdfLocal")    public void create() {        PdfUtil.createPdfPage("D:\\xxf.pdf");    }

重启我的项目,在浏览器拜访:http://localhost:6666/example/createPdfLocal

能够在D:\\test目录下看到xxf.pdf文件

咱们关上看一下成果:

合并PDF,并保留

持续在util包下的PdfUtil.java工具类里增加,因为本文波及到的操作比拟多,这里只贴相干代码,最初对立贴一个实现的文件。

/**     * 合并pdf文件     *     * @param files   要合并文件数组(绝对路径如{ "D:\\test\\1.pdf", "D:\\test\\2.pdf" , "D:\\test\\3.pdf"})     * @param newFile 合并后寄存的目录D:\\test\\xxf-merge.pdf     * @return boolean 生胜利返回true, 否則返回false     */    public static boolean mergePdfFiles(String[] files, String newFile) {        boolean retValue = false;        Document document;        try (FileOutputStream fileOutputStream = new FileOutputStream(newFile)) {            document = new Document(new PdfReader(files[0]).getPageSize(1));            PdfCopy copy = new PdfCopy(document, fileOutputStream);            document.open();            for (String file : files) {                PdfReader reader = new PdfReader(file);                int n = reader.getNumberOfPages();                for (int j = 1; j <= n; j++) {                    document.newPage();                    PdfImportedPage page = copy.getImportedPage(reader, j);                    copy.addPage(page);                }            }            retValue = true;            document.close();        } catch (Exception e) {            e.printStackTrace();        }        return retValue;    }

controller包下的TestPdfController.java类中增加代码,因为本文波及到的操作比拟多,这里只贴相干代码,最初对立贴一个实现的文件,相干代码如下:

留神:代码中相干的绝对路径要替换成本人的
    @ApiOperation(value = "测试合并PDF到指定目录")    @GetMapping("/mergePdf")    public Boolean mergePdf() {        String[] files = {"D:\\test\\1.pdf", "D:\\test\\2.pdf"};        String newFile = "D:\\test\\xxf-merge.pdf";        return PdfUtil.mergePdfFiles(files, newFile);    }

咱们首先要筹备两个文件D:\\test\\1.pdf,D:\\test\\2.pdf,这里能够指定文件,也能够是生成的pdf文件。

如果是解决生成的文件,这里说下思维:程序创立一个长期目录,留神要惟一命名,而后将生成PDF文件保留到这个目录,而后从这个目录下拿到pdf进行解决,最初解决实现,保留到对应的目录下,删除这个长期目录和上面的文件。这里不做演示。《合并PDF,并下载》外面略有波及,然而解决的单个文件,略微革新即可

重启我的项目,在浏览器拜访:http://localhost:6666/example/mergePdf

能够在D:\\test目录下看到xxf-merge文件

关上看一下成果:

合并PDF,并下载

持续在util包下的PdfUtil.java工具类里增加,这里只贴将文件转为输入流,并删除文件的相干代码,合并相干代码见《合并PDF,并保留》的util类。

    /**     * 读取PDF,读取后删除PDF,实用于生成后须要导出PDF,创立临时文件     */    public static void readDeletePdf(String fileName, ServletOutputStream outputStream) {        File file = new File(fileName);        if (!file.exists()) {            System.out.println(fileName + "文件不存在");        }        try (InputStream in = new FileInputStream(fileName)) {            IOUtils.copy(in, outputStream);        } catch (Exception e) {            e.printStackTrace();        } finally {            try {                Files.delete(file.toPath());            } catch (IOException e) {                e.printStackTrace();            }        }    }

controller包下的TestPdfController.java类中增加代码,因为本文波及到的操作比拟多,这里只贴相干代码,最初对立贴一个实现的文件,相干代码如下:

留神:代码中相干的绝对路径要替换成本人的
@ApiOperation(value = "测试合并PDF后并导出")    @GetMapping("/exportMergePdf")    public void createPdf(HttpServletResponse response) {        // 设置response参数,能够关上下载页面        response.reset();        response.setCharacterEncoding("UTF-8");        // 定义输入类型        response.setContentType("application/PDF;charset=utf-8");        // 设置名称        response.setHeader("Content-Disposition", "attachment; filename=" + "xiaoxiaofeng.pdf");        try (ServletOutputStream out = response.getOutputStream()) {            String[] files = {"D:\\test\\1.pdf", "D:\\test\\2.pdf"};            // 生成为临时文件,转换为流后,再删除该文件            String newFile = "src\\main\\resources\\templates\\" + UUID.randomUUID() + ".pdf";            boolean isOk = PdfUtil.mergePdfFiles(files, newFile);            if (isOk) {                PdfUtil.readDeletePdf(newFile, out);            }        } catch (IOException e) {            e.printStackTrace();        }    }

重启我的项目,在浏览器拜访:http://localhost:6666/example/exportMergePdf会提醒咱们下载。

残缺代码

PdfUtil.java

package com.maple.demo.util;import com.itextpdf.text.Document;import com.itextpdf.text.DocumentException;import com.itextpdf.text.Image;import com.itextpdf.text.Rectangle;import com.itextpdf.text.pdf.*;import org.apache.commons.compress.utils.IOUtils;import javax.servlet.ServletOutputStream;import java.io.*;import java.nio.file.Files;import java.util.ArrayList;import java.util.List;import java.util.Map;import java.util.Objects;import static com.maple.demo.util.PdfFontUtil.*;/** * @author 笑小枫 * @date 2022/8/15 * @see <a href="https://www.xiaoxiaofeng.com">https://www.xiaoxiaofeng.com</a> */public class PdfUtil {    private PdfUtil() {    }    /**     * 利用模板生成pdf     *     * @param data         写入的数据     * @param photoMap     图片信息     * @param out          自定义保留pdf的文件流     * @param templatePath pdf模板门路     */    public static void fillTemplate(Map<String, Object> data, Map<String, String> photoMap, ServletOutputStream out, String templatePath) {        PdfReader reader;        ByteArrayOutputStream bos;        PdfStamper stamper;        try {            // 读取pdf模板            reader = new PdfReader(templatePath);            bos = new ByteArrayOutputStream();            stamper = new PdfStamper(reader, bos);            AcroFields acroFields = stamper.getAcroFields();            // 赋值            for (String name : acroFields.getFields().keySet()) {                String value = data.get(name) != null ? data.get(name).toString() : null;                acroFields.setField(name, value);            }            // 图片赋值            for (Map.Entry<String, String> entry : photoMap.entrySet()) {                if (Objects.isNull(entry.getKey())) {                    continue;                }                String key = entry.getKey();                String url = entry.getValue();                // 依据地址读取须要放入pdf中的图片                Image image = Image.getInstance(url);                // 设置图片在哪一页                PdfContentByte overContent = stamper.getOverContent(acroFields.getFieldPositions(key).get(0).page);                // 获取模板中图片域的大小                Rectangle signRect = acroFields.getFieldPositions(key).get(0).position;                float x = signRect.getLeft();                float y = signRect.getBottom();                // 图片等比缩放                image.scaleAbsolute(signRect.getWidth(), signRect.getHeight());                // 图片地位                image.setAbsolutePosition(x, y);                // 在该页退出图片                overContent.addImage(image);            }            // 如果为false那么生成的PDF文件还能编辑,肯定要设为true            stamper.setFormFlattening(true);            stamper.close();            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();            bos.close();        } catch (IOException | DocumentException e) {            e.printStackTrace();        }    }    /**     * 创立PDF,并保留到指定地位     *     * @param filePath 保留门路     */    public static void createPdfPage(String filePath) {        // FileOutputStream 须要敞开,开释资源        try (FileOutputStream outputStream = new FileOutputStream(filePath)) {            // 创立文档            Document document = new Document();            PdfWriter writer = PdfWriter.getInstance(document, outputStream);            document.open();            // 报告题目            document.add(PdfFontUtil.getParagraph("笑小枫的网站介绍", TITLE_FONT, 1));            document.add(PdfFontUtil.getParagraph("\n网站名称:笑小枫(www.xiaoxiaofeng.com)", INFO_FONT, -1));            document.add(PdfFontUtil.getParagraph("\n生成工夫:2022-07-02\n\n", INFO_FONT, -1));            // 报告内容            // 段落题目 + 报表图            document.add(PdfFontUtil.getParagraph("文章数据统计", NODE_FONT, -1));            document.add(PdfFontUtil.getParagraph("\n· 网站首页图\n\n", BLOCK_FONT, -1));            // 设置图片宽高            float documentWidth = document.getPageSize().getWidth() - document.leftMargin() - document.rightMargin();            float documentHeight = documentWidth / 580 * 320;            document.add(PdfFontUtil.getImage("D:\\xiaoxiaofeng.jpg", documentWidth - 80, documentHeight - 80));            // 数据表格            document.add(PdfFontUtil.getParagraph("\n· 数据详情\n\n", BLOCK_FONT, -1));            // 生成6列的表格            PdfPTable dataTable = PdfFontUtil.getPdfTable(6, 500);            // 设置表格            List<String> tableHeadList = tableHead();            List<List<String>> tableDataList = getTableData();            PdfFontUtil.addTableCell(dataTable, CONTENT_FONT, tableHeadList);            for (List<String> tableData : tableDataList) {                PdfFontUtil.addTableCell(dataTable, CONTENT_FONT, tableData);            }            document.add(dataTable);            document.add(PdfFontUtil.getParagraph("\n· 报表形容\n\n", BLOCK_FONT, -1));            document.add(PdfFontUtil.getParagraph("数据报告能够监控每天的推广状况," +                    "能够针对不同的数据体现进行剖析,以晋升推广成果。", CONTENT_FONT, -1));            document.newPage();            document.close();            writer.close();        } catch (Exception e) {            e.printStackTrace();        }    }    /**     * 模仿数据     */    private static List<String> tableHead() {        List<String> tableHeadList = new ArrayList<>();        tableHeadList.add("省份");        tableHeadList.add("城市");        tableHeadList.add("数量");        tableHeadList.add("百分比1");        tableHeadList.add("百分比2");        tableHeadList.add("百分比3");        return tableHeadList;    }    /**     * 模仿数据     */    private static List<List<String>> getTableData() {        List<List<String>> tableDataList = new ArrayList<>();        for (int i = 0; i < 3; i++) {            List<String> tableData = new ArrayList<>();            tableData.add("浙江" + i);            tableData.add("杭州" + i);            tableData.add("276" + i);            tableData.add("33.3%");            tableData.add("34.3%");            tableData.add("35.3%");            tableDataList.add(tableData);        }        return tableDataList;    }    /**     * 合并pdf文件     *     * @param files   要合并文件数组(绝对路径如{ "D:\\test\\1.pdf", "D:\\test\\2.pdf" , "D:\\test\\3.pdf"})     * @param newFile 合并后寄存的目录D:\\test\\xxf-merge.pdf     * @return boolean 生胜利返回true, 否則返回false     */    public static boolean mergePdfFiles(String[] files, String newFile) {        boolean retValue = false;        Document document;        try (FileOutputStream fileOutputStream = new FileOutputStream(newFile)) {            document = new Document(new PdfReader(files[0]).getPageSize(1));            PdfCopy copy = new PdfCopy(document, fileOutputStream);            document.open();            for (String file : files) {                PdfReader reader = new PdfReader(file);                int n = reader.getNumberOfPages();                for (int j = 1; j <= n; j++) {                    document.newPage();                    PdfImportedPage page = copy.getImportedPage(reader, j);                    copy.addPage(page);                }            }            retValue = true;            document.close();        } catch (Exception e) {            e.printStackTrace();        }        return retValue;    }    /**     * 读取PDF,读取后删除PDF,实用于生成后须要导出PDF,创立临时文件     */    public static void readDeletePdf(String fileName, ServletOutputStream outputStream) {        File file = new File(fileName);        if (!file.exists()) {            System.out.println(fileName + "文件不存在");        }        try (InputStream in = new FileInputStream(fileName)) {            IOUtils.copy(in, outputStream);        } catch (Exception e) {            e.printStackTrace();        } finally {            try {                Files.delete(file.toPath());            } catch (IOException e) {                e.printStackTrace();            }        }    }}

PdfFontUtil.java

package com.maple.demo.util;import com.itextpdf.text.*;import com.itextpdf.text.pdf.BaseFont;import com.itextpdf.text.pdf.PdfPTable;import java.io.IOException;import java.util.List;/** * @author 笑小枫 * @date 2022/8/15 * @see <a href="https://www.xiaoxiaofeng.com">https://www.xiaoxiaofeng.com</a> */public class PdfFontUtil {    private PdfFontUtil() {    }    /**     * 根底配置,能够放相对路径,这里演示绝对路径,因为字体文件过大,这里不传到我的项目外面了,须要的本人下载     * 下载地址:https://www.zitijia.com/downloadpage?itemid=281258939050380345     */    public static final String FONT = "D:\\font/Simhei.ttf";    /**     * 根底款式     */    public static final Font TITLE_FONT = FontFactory.getFont(FONT, BaseFont.IDENTITY_H, 20, Font.BOLD);    public static final Font NODE_FONT = FontFactory.getFont(FONT, BaseFont.IDENTITY_H, 15, Font.BOLD);    public static final Font BLOCK_FONT = FontFactory.getFont(FONT, BaseFont.IDENTITY_H, 13, Font.BOLD, BaseColor.BLACK);    public static final Font INFO_FONT = FontFactory.getFont(FONT, BaseFont.IDENTITY_H, 12, Font.NORMAL, BaseColor.BLACK);    public static final Font CONTENT_FONT = FontFactory.getFont(FONT, BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);    /**     * 段落款式获取     */    public static Paragraph getParagraph(String content, Font font, Integer alignment) {        Paragraph paragraph = new Paragraph(content, font);        if (alignment != null && alignment >= 0) {            paragraph.setAlignment(alignment);        }        return paragraph;    }    /**     * 图片款式     */    public static Image getImage(String imgPath, float width, float height) throws IOException, BadElementException {        Image image = Image.getInstance(imgPath);        image.setAlignment(Image.MIDDLE);        if (width > 0 && height > 0) {            image.scaleAbsolute(width, height);        }        return image;    }    /**     * 表格生成     */    public static PdfPTable getPdfTable(int numColumns, float totalWidth) {        // 表格解决        PdfPTable table = new PdfPTable(numColumns);        // 设置表格宽度比例为%100        table.setWidthPercentage(100);        // 设置宽度:宽度均匀        table.setTotalWidth(totalWidth);        // 锁住宽度        table.setLockedWidth(true);        // 设置表格下面空白宽度        table.setSpacingBefore(10f);        // 设置表格上面空白宽度        table.setSpacingAfter(10f);        // 设置表格默认为无边框        table.getDefaultCell().setBorder(0);        table.setPaddingTop(50);        table.setSplitLate(false);        return table;    }    /**     * 表格内容带款式     */    public static void addTableCell(PdfPTable dataTable, Font font, List<String> cellList) {        for (String content : cellList) {            dataTable.addCell(getParagraph(content, font, -1));        }    }}

TestPdfController.java

package com.maple.demo.controller;import com.maple.demo.util.PdfUtil;import io.swagger.annotations.Api;import io.swagger.annotations.ApiOperation;import lombok.extern.slf4j.Slf4j;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;import javax.servlet.ServletOutputStream;import javax.servlet.http.HttpServletResponse;import java.io.IOException;import java.util.HashMap;import java.util.Map;import java.util.UUID;/** * @author 笑小枫 * @date 2022/8/15 * @see <a href="https://www.xiaoxiaofeng.com">https://www.xiaoxiaofeng.com</a> */@Slf4j@RestController@RequestMapping("/example")@Api(tags = "实例演示-PDF操作接口")public class TestPdfController {    @ApiOperation(value = "依据PDF模板导出PDF")    @GetMapping("/exportPdf")    public void exportPdf(HttpServletResponse response) {        Map<String, Object> dataMap = new HashMap<>(16);        dataMap.put("nickName", "笑小枫");        dataMap.put("age", 18);        dataMap.put("sex", "男");        dataMap.put("csdnUrl", "https://zhangfz.blog.csdn.net/");        dataMap.put("siteUrl", "https://www.xiaoxiaofeng.com/");        dataMap.put("desc", "大家好,我是笑小枫。");        Map<String, String> photoMap = new HashMap<>(16);        photoMap.put("logo", "https://profile.csdnimg.cn/C/9/4/2_qq_34988304");        // 设置response参数,能够关上下载页面        response.reset();        response.setCharacterEncoding("UTF-8");        // 定义输入类型        response.setContentType("application/PDF;charset=utf-8");        // 设置名称        response.setHeader("Content-Disposition", "attachment; filename=" + "xiaoxiaofeng.pdf");        try {            ServletOutputStream out = response.getOutputStream();            // 模板门路记            PdfUtil.fillTemplate(dataMap, photoMap, out, "src/main/resources/templates/xiaoxiaofeng.pdf");        } catch (IOException e) {            e.printStackTrace();        }    }    @ApiOperation(value = "测试纯代码生成PDF到指定目录")    @GetMapping("/createPdfLocal")    public void create() {        PdfUtil.createPdfPage("D:\\test\\xxf.pdf");    }    @ApiOperation(value = "测试合并PDF到指定目录")    @GetMapping("/mergePdf")    public Boolean mergePdf() {        String[] files = {"D:\\test\\1.pdf", "D:\\test\\2.pdf"};        String newFile = "D:\\test\\xxf-merge.pdf";        return PdfUtil.mergePdfFiles(files, newFile);    }    @ApiOperation(value = "测试合并PDF后并导出")    @GetMapping("/exportMergePdf")    public void createPdf(HttpServletResponse response) {        // 设置response参数,能够关上下载页面        response.reset();        response.setCharacterEncoding("UTF-8");        // 定义输入类型        response.setContentType("application/PDF;charset=utf-8");        // 设置名称        response.setHeader("Content-Disposition", "attachment; filename=" + "xiaoxiaofeng.pdf");        try (ServletOutputStream out = response.getOutputStream()) {            String[] files = {"D:\\test\\1.pdf", "D:\\test\\2.pdf"};            // 生成为临时文件,转换为流后,再删除该文件            String newFile = "src\\main\\resources\\templates\\" + UUID.randomUUID() + ".pdf";            boolean isOk = PdfUtil.mergePdfFiles(files, newFile);            if (isOk) {                PdfUtil.readDeletePdf(newFile, out);            }        } catch (IOException e) {            e.printStackTrace();        }    }}

对于笑小枫

本章到这里完结了,喜爱的敌人关注一下我呦,大伙的反对,就是我保持写下去的能源。

微信公众号:笑小枫

笑小枫集体博客:https://www.xiaoxiaofeng.com

CSDN:https://zhangfz.blog.csdn.net

本文源码:https://github.com/hack-feng/maple-demo