当前位置: 代码网 > it编程>编程语言>Java > SpringBoot生成和操作PDF的代码详解

SpringBoot生成和操作PDF的代码详解

2025年01月14日 Java 我要评论
本文简介本文涉及pdf操作,如下:pdf模板制作基于pdf模板生成,并支持下载自定义中文字体完全基于代码生成,并保存到指定目录合并pdf,并保存到指定目录合并pdf,并支持下载基于pdf模板生成:适用

本文简介

本文涉及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>5.5.6</version>
</dependency>
<!-- 这个主要用来设置样式什么的 -->
<dependency>
    <groupid>com.itextpdf</groupid>
    <artifactid>itext-asian</artifactid>
    <version>5.2.0</version>
</dependency>

基于pdf模板生成,并下载

pdf模板制作

首先在word或者其他软件里面制作模板,挑选你熟悉的软件即可,前提是可生成pdf。

image-20220815134838696

将word文件转为pdf文件。

image-20220815135121685

使用adobe acrobat软件操作pdf,这里用的是这个软件,只要能实现这个功能,其他的软件也可~

选择表单编辑哈,我们要在对应的坑上添加表单占位

image-20220815135323496

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

对应的域名称要与程序的名称对应,方便后面数据填充,不然后面需要手动处理赋值。

image-20220815135431811

image-20220815135520626

创建个简单的模板吧,要注意填充的空间要充足,不然会出现数据展示不全呦~

效果如下:

image-20220815135622485

好了,到这里模板就生成好了,我们保存一下,然后放在我们的/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" rel="external nofollow"  rel="external nofollow"  rel="external nofollow"  rel="external nofollow"  rel="external nofollow"  rel="external nofollow" >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" rel="external nofollow"  rel="external nofollow"  rel="external nofollow"  rel="external nofollow"  rel="external nofollow"  rel="external nofollow" >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

导出的文件效果如下:

image-20220816165204811

完全基于代码生成,并保存

完全基于代码生成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" rel="external nofollow"  rel="external nofollow"  rel="external nofollow"  rel="external nofollow"  rel="external nofollow"  rel="external nofollow" >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文件

image-20220816170702824

我们打开看一下效果:

image-20220816170841371

合并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文件

image-20220816172150022

打开看一下效果:

image-20220816172236016

合并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会提示我们下载。

image-20220816172814426

完整代码

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" rel="external nofollow"  rel="external nofollow"  rel="external nofollow"  rel="external nofollow"  rel="external nofollow"  rel="external nofollow" >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" rel="external nofollow"  rel="external nofollow"  rel="external nofollow"  rel="external nofollow"  rel="external nofollow"  rel="external nofollow" >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" rel="external nofollow"  rel="external nofollow"  rel="external nofollow"  rel="external nofollow"  rel="external nofollow"  rel="external nofollow" >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();
        }
    }
}

以上就是springboot生成和操作pdf的代码详解的详细内容,更多关于springboot生成和操作pdf的资料请关注代码网其它相关文章!

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com