赞
踩
最近,我们的系统新增了一个客服模块,其中一个重要功能是能够以PDF格式导出客服与用户之间的聊天记录。这些聊天记录包含文字、图片和文件等多种内容。为了实现这一功能,我们首先使用了itextpdf 5.x版本制作了一个Demo。今天,我将与家人们分享一下这项进展。
iTextPDF 是一个用于创建和操作 PDF(Portable Document Format)文档的流行的 Java 库。它提供了一套全面的功能,用于处理 PDF 文件,包括创建新文档、修改现有文档以及提取信息。以下是 iTextPDF 的一些关键方面的简要概述:
iTextPDF 允许您从头开始创建新的 PDF 文档。
您可以向文档添加段落、表格、图像和其他元素。
该库提供了格式化和处理文本的方法。
您可以定义页面的布局,包括页面尺寸、边距等。
iTextPDF 允许您选择字体和颜色,以定制文档的外观。
通过 iTextPDF,您可以创建包含表格的文档,设置表格的列数、行数和单元格内容。
您可以将图像插入到文档中,并设置图像的大小和位置。
iTextPDF 提供了对文档进行加密和数字签名的功能,以增强文档的安全性。
除了创建文档,iTextPDF 还允许您解析现有的 PDF 文档,提取文本、图像等信息。
我们此处使用的 iTextPDF 5.x的版本实现的
在pom文件中添加如下依赖
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itextpdf</artifactId>
<version>5.5.13.2</version>
</dependency>
service代码
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代码
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); } }
代码到这就完了,我们可以启动服务,查看下展示的效果:
到这儿,如果不出意外的话肯定要出意外了,请继续阅读文章。
如果我们将服务部署到Linux服务器上,可能会遇到中文未正确写入或乱码的问题。这是由于Linux系统上的字体库与Windows系统不同。为了解决这个问题,我们可以在代码中直接将所需字体的ttf文件复制到项目目录下,并使用itextpdf加载这些字体。此前,我们成功为服务器添加了Windows字体库,因此我们可以直接从系统中获取字体。接下来,我们将介绍在Linux中添加Windows字体的操作步骤。
windows字体库的位置:C:\Users\Administrator\AppData\Local\Microsoft\Windows\Fonts
将windows的字体库Fonts 复制到目录 /usr/share/fonts
下,执行如下权限命令:
sudo chmod -R 777 Fonts
然后执行以下命令使字体生效
sudo fc-cache -fv
将windows的字体库Fonts下的文件 复制到目录 /usr/share/fonts
下,依次执行如下命令:
yum install -y mkfontscale
yum install -y fontconfig
cd /usr/share/fonts/
mkfontscale
mkfontdir
fc-cache
fc-list
Dockerfile
# 基于哪个镜像
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
Dockerfile
# 基于哪个镜像
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内容,包括图表、表格等,以满足项目的特定要求。希望这篇文章对你有所帮助!如果有任何问题或建议,请随时提出。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。