前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >在Spring Boot中使用iTextPDF创建动态PDF文档

在Spring Boot中使用iTextPDF创建动态PDF文档

作者头像
修己xj
发布2024-01-26 19:24:39
2480
发布2024-01-26 19:24:39
举报
文章被收录于专栏:修己xj修己xj

最近,我们的系统新增了一个客服模块,其中一个重要功能是能够以PDF格式导出客服与用户之间的聊天记录。这些聊天记录包含文字、图片和文件等多种内容。为了实现这一功能,我们首先使用了itextpdf 5.x版本制作了一个Demo。今天,我将与家人们分享一下这项进展。

itextpdf.jpg

iTextPDF 介绍

iTextPDF 是一个用于创建和操作 PDF(Portable Document Format)文档的流行的 Java 库。它提供了一套全面的功能,用于处理 PDF 文件,包括创建新文档、修改现有文档以及提取信息。以下是 iTextPDF 的一些关键方面的简要概述:

  • 文档创建:

iTextPDF 允许您从头开始创建新的 PDF 文档。

您可以向文档添加段落、表格、图像和其他元素。

  • 文本操作:

该库提供了格式化和处理文本的方法。

  • 页面布局:

您可以定义页面的布局,包括页面尺寸、边距等。

  • 字体和颜色:

iTextPDF 允许您选择字体和颜色,以定制文档的外观。

  • 表格:

通过 iTextPDF,您可以创建包含表格的文档,设置表格的列数、行数和单元格内容。

  • 图像处理:

您可以将图像插入到文档中,并设置图像的大小和位置。

  • 文档安全性:

iTextPDF 提供了对文档进行加密和数字签名的功能,以增强文档的安全性。

  • 文档解析:

除了创建文档,iTextPDF 还允许您解析现有的 PDF 文档,提取文本、图像等信息。

代码示例

我们此处使用的 iTextPDF 5.x的版本实现的

添加依赖

在pom文件中添加如下依赖

代码语言:javascript
复制
<dependency>
  <groupId>com.itextpdf</groupId>
  <artifactId>itextpdf</artifactId>
  <version>5.5.13.2</version> 
</dependency>

代码编写

service代码

代码语言:javascript
复制
import cn.xj.xjdoc.system.entity.Message;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.itextpdf.text.*;
import com.itextpdf.text.Font;
import com.itextpdf.text.Image;
import com.itextpdf.text.pdf.BaseFont;
import com.itextpdf.text.pdf.ColumnText;
import com.itextpdf.text.pdf.PdfAction;
import com.itextpdf.text.pdf.PdfWriter;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;

@Service
@Slf4j
public class PdfService {

    public void export(HttpServletResponse response) throws IOException, DocumentException {

        List<Message> messageList = getMsgList();
        // 创建 PDF document
        Document document = new Document();
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        PdfWriter pdfWriter = PdfWriter.getInstance(document, baos);

        //获取系统字体,如果是中文,则需注意linux中不存在windows字体,中文乱码或者写不进去
        FontFactory.registerDirectories();

        Font chineseFont = FontFactory.getFont("SimSun", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);

        document.open();

        // 发送人字体
        Font senderFont = new Font(chineseFont.getBaseFont(), 16, Font.BOLD, BaseColor.ORANGE);
        // 发送时间字体
        Font senderTimeFont = new Font(chineseFont.getBaseFont(), 12, Font.UNDEFINED, BaseColor.BLUE);
        // 内容字体
        Font defaultFont = new Font(chineseFont.getBaseFont(), 12);

        // 将信息写入pdf中
        for (Message msg : messageList) {

            String senderText = String.format("%s  ", msg.getSendUser());
            String timeText = msg.getSendTime();
            String messageText = msg.getContent();
            Integer type = msg.getType();

            Paragraph paragraph = new Paragraph();

            ColumnText columnText = new ColumnText(pdfWriter.getDirectContent());
            columnText.addElement(paragraph);
            columnText.setSimpleColumn(20, document.bottom(), document.right() - 20, document.top(), 0, Element.ALIGN_LEFT);
            paragraph.add(new Chunk(senderText, senderFont));
            paragraph.add(new Chunk(timeText + "\n", senderTimeFont));

            float proportion = 1f;

            if(type == 1){
                //文字
                paragraph.add(new Chunk(messageText + "\n", defaultFont));
            }else if(type == 2){
                //图片
                // 创建Image对象
                Image image = Image.getInstance(new URL(msg.getContent()));

                //等比缩小图片
                if(image.getWidth() > 150){
                    proportion = image.getWidth()/150;
                    image.scaleToFit(image.getWidth()/proportion, image.getHeight()/proportion);
                }
                //加一空行
                paragraph.add(new Chunk(Chunk.NEWLINE));
                // 判断当前页内容是否已满
                if ((pdfWriter.getVerticalPosition(true) - image.getHeight()/proportion) < document.bottom()) {
                    document.newPage(); // Start a new page
                    columnText.setYLine(document.top());
                }
                paragraph.add(image);
            }else{
                //文件
                // 创建Chunk,设置文件连接,点击下载
                Chunk chunk = new Chunk(msg.getFileName(),defaultFont);
                chunk.setAction(new PdfAction(new URL(msg.getContent()).toExternalForm()));
                paragraph.add(chunk);
            }

            paragraph.setIndentationLeft(20f);
            paragraph.setIndentationRight(20f);
            paragraph.setSpacingAfter(10f);

            // 判断当前页内容是否已满
            if (ColumnText.hasMoreText(columnText.go())) {
                document.newPage();
            }
            document.add(paragraph);

        }
        document.close();

        //返回pdf
        response.setContentType("application/pdf");
        response.setHeader("Content-Disposition", "inline; filename=chat_export.pdf");
        response.getOutputStream().write(baos.toByteArray());
        response.getOutputStream().flush();
    }


    /**
     * 获取数据的方法
     * @return
     */
    public List<Message>  getMsgList(){
        String  jsonArrayString ="[{\"type\": 1, \"content\": \"嘿,听说最近有点八卦,你有什么料吗?\", \"fileName\": null, \"sendTime\": \"2024-01-25 21:46:12\", \"sendUser\": \"大雄\"}, {\"type\": 1, \"content\": \"当然啦!你知道吗,最近有个超级智能机器人在公司里开始工作了,听说能做我们的工作,让人有点担心啊。\", \"fileName\": null, \"sendTime\": \"2024-01-25 21:47:17\", \"sendUser\": \"修己\"}, {\"type\": 1, \"content\": \" 哇,真的吗?那它是怎么工作的?会不会抢我们的饭碗?\", \"fileName\": null, \"sendTime\": \"2024-01-25 21:48:22\", \"sendUser\": \"大雄\"}, {\"type\": 1, \"content\": \" 别慌,听说它只是一个AI助手,能够处理一些重复性的任务,让我们有更多时间专注于创造性的工作。\", \"fileName\": null, \"sendTime\": \"2024-01-25 21:49:12\", \"sendUser\": \"修己\"}, {\"type\": 1, \"content\": \" 哦,原来如此。不过,你觉得这个机器人有没有潜在的危险性啊?\", \"fileName\": null, \"sendTime\": \"2024-01-25 21:50:18\", \"sendUser\": \"大雄\"}, {\"type\": 1, \"content\": \"哈哈,我也有点担心,但听说它的设计是为了帮助我们,而不是取代我们。还有,它可不会参与任何八卦。\", \"fileName\": null, \"sendTime\": \"2024-01-25 21:51:42\", \"sendUser\": \"修己\"}, {\"type\": 1, \"content\": \" 哈哈,说到八卦,你有听说最近公司里有什么有趣的事情吗?\", \"fileName\": null, \"sendTime\": \"2024-01-25 21:52:12\", \"sendUser\": \"大雄\"}, {\"type\": 1, \"content\": \"对呀,听说某某老板最近秘密约会了某某同事,整个公司都在传。你觉得是真的吗?照片都爆出来了,给你发下\", \"fileName\": null, \"sendTime\": \"2024-01-25 21:53:12\", \"sendUser\": \"修己\"}, {\"type\": 2, \"content\": \"http://chevereto.xiuji.mynatapp.cc/images/2023/06/27/z_3b2fa0f1285d8071229631addebf3087.png\", \"fileName\": null, \"sendTime\": \"2024-01-25 21:54:12\", \"sendUser\": \"修己\"}, {\"type\": 2, \"content\": \"http://chevereto.xiuji.mynatapp.cc/images/2023/06/23/_20230623212425.png\", \"fileName\": null, \"sendTime\": \"2024-01-25 21:55:12\", \"sendUser\": \"修己\"}, {\"type\": 2, \"content\": \"http://chevereto.xiuji.mynatapp.cc/images/2023/06/27/z_73bc1ab1a92d68b15e9e1babc3d15afb.png\", \"fileName\": null, \"sendTime\": \"2024-01-25 21:56:12\", \"sendUser\": \"修己\"}, {\"type\": 2, \"content\": \"http://chevereto.xiuji.mynatapp.cc/images/2023/06/23/_20230623212445.png\", \"fileName\": null, \"sendTime\": \"2024-01-25 21:57:12\", \"sendUser\": \"修己\"}, {\"type\": 1, \"content\": \"哇,这可真是八卦大爆炸啊!我倒是没听说,但如果是真的,那可真是太有趣了。\", \"fileName\": null, \"sendTime\": \"2024-01-25 21:58:12\", \"sendUser\": \"大雄\"}, {\"type\": 1, \"content\": \"是啊,公司里的八卦总是让人忍不住想知道更多。有时候我觉得,我们也是一群活在八卦世界里的AI助手。\", \"fileName\": null, \"sendTime\": \"2024-01-25 21:59:12\", \"sendUser\": \"修己\"}, {\"type\": 1, \"content\": \"哈哈,说得对!我们也需要一些八卦来调剂一下生活。不过,我还是觉得那个超级智能机器人有点神秘呢。\", \"fileName\": null, \"sendTime\": \"2024-01-25 22:02:36\", \"sendUser\": \"大雄\"}, {\"type\": 1, \"content\": \"是啊,或许我们也能通过它得知更多关于公司内幕的事情。要不要试试向它搭讪?\", \"fileName\": null, \"sendTime\": \"2024-01-25 22:05:36\", \"sendUser\": \"修己\"}, {\"type\": 1, \"content\": \" 哈哈,说不定它能告诉我们更多关于那对神秘约会的内幕。不过,我们还是小心点为好,免得被当成八卦机器人。\", \"fileName\": null, \"sendTime\": \"2024-01-25 22:08:36\", \"sendUser\": \"大雄\"}, {\"type\": 1, \"content\": \"顺便给你在发个我最近吃的瓜的pdf\", \"fileName\": null, \"sendTime\": \"2024-01-25 22:12:36\", \"sendUser\": \"修己\"}, {\"type\": 4, \"content\": \"http://chevereto.xiuji.mynatapp.cc/images/2023/06/23/西安外国语大学会计ACCA2001班丁玉婕出轨情况说明.pdf\", \"fileName\": \"西安外国语大学会计ACCA2001班丁玉婕出轨情况说明.pdf\", \"sendTime\": \"2024-01-25 22:18:36\", \"sendUser\": \"修己\"}]";
        // 使用Fastjson将JsonArray字符串解析为JSONArray对象
        JSONArray jsonArray = JSONArray.parseArray(jsonArrayString);
        List<Message> messageList = new ArrayList<>();
        // 遍历JsonArray,将每个JSON对象转换为Message对象并添加到List中
        for (Object jsonMessage : jsonArray) {
            JSONObject jsonObject = (JSONObject) jsonMessage;
            Message message = jsonObject.toJavaObject(Message.class);
            messageList.add(message);
        }
        return messageList;
    }
}

controller代码

代码语言:javascript
复制
import cn.xj.xjdoc.system.service.PdfService;
import com.itextpdf.text.DocumentException;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.io.IOException;

@RestController
@Slf4j
public class PdfController {

    @Resource
    private PdfService pdfService;
    
    @GetMapping("/export")
    public void export(HttpServletResponse response) throws DocumentException, IOException {
        pdfService.export(response);
    }
}

代码到这就完了,我们可以启动服务,查看下展示的效果:

_20240126070325.jpg

到这儿,如果不出意外的话肯定要出意外了,请继续阅读文章。

Linux上解决中文没写入或者乱码问题

如果我们将服务部署到Linux服务器上,可能会遇到中文未正确写入或乱码的问题。这是由于Linux系统上的字体库与Windows系统不同。为了解决这个问题,我们可以在代码中直接将所需字体的ttf文件复制到项目目录下,并使用itextpdf加载这些字体。此前,我们成功为服务器添加了Windows字体库,因此我们可以直接从系统中获取字体。接下来,我们将介绍在Linux中添加Windows字体的操作步骤。

windows字体库的位置:C:\Users\Administrator\AppData\Local\Microsoft\Windows\Fonts

Linux 中添加windows字体库

  • ubuntu

将windows的字体库Fonts 复制到目录 /usr/share/fonts 下,执行如下权限命令:

代码语言:javascript
复制
sudo chmod -R 777 Fonts

然后执行以下命令使字体生效

代码语言:javascript
复制
sudo fc-cache -fv
  • centos

将windows的字体库Fonts下的文件 复制到目录 /usr/share/fonts 下,依次执行如下命令:

代码语言:javascript
复制
yum install -y mkfontscale

yum install -y fontconfig

cd /usr/share/fonts/

mkfontscale

mkfontdir

fc-cache

fc-list

构建具有windows字体库的docker镜像

  • ubuntu

Dockerfile

代码语言:javascript
复制
# 基于哪个镜像
FROM ubuntu:20.10

# 维护者
MAINTAINER xj

# 拷贝文件到容器
ADD Fonts /usr/share/fonts/chinese_font/
RUN chmod -R 755 /usr/share/fonts/chinese_font
RUN fc-cache -fv
  • centos

Dockerfile

代码语言:javascript
复制
# 基于哪个镜像
FROM centos:centos7.1.1503
# 维护者
MAINTAINER xj
ADD Fonts/* /usr/share/fonts/
RUN yum install -y mkfontscale
RUN yum install -y fontconfig
RUN cd /usr/share/fonts/
RUN mkfontscale
RUN mkfontdir
RUN fc-cache
RUN fc-list

总结

这个例子演示了如何使用Spring Boot和iTextPDF创建动态的、个性化的PDF文档。你可以根据实际需求扩展生成的PDF内容,包括图表、表格等,以满足项目的特定要求。希望这篇文章对你有所帮助!如果有任何问题或建议,请随时提出。

本文参与?腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2024-01-26,如有侵权请联系?cloudcommunity@tencent.com 删除

本文分享自 修己xj 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与?腾讯云自媒体分享计划? ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • iTextPDF 介绍
  • 代码示例
    • 添加依赖
      • 代码编写
      • Linux上解决中文没写入或者乱码问题
        • Linux 中添加windows字体库
          • 构建具有windows字体库的docker镜像
          • 总结
          相关产品与服务
          腾讯云小微
          腾讯云小微,是一套腾讯云的智能服务系统,也是一个智能服务开放平台,接入小微的硬件可以快速具备听觉和视觉感知能力,帮助智能硬件厂商实现语音人机互动和音视频服务能力。
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
          http://www.vxiaotou.com