Itext进行PDF的编辑开发

这周写了一周的需求,是制作一个PDF生成功能,其中用到了Itext来制作PDF的视觉效果。其中一些功能不是很懂,仅作记录,若要学习请仔细甄别正确与否。

开始之前,我还是想说,这傻福需求怎么想出来的,让人手撸PDF,哥们一两页PDF写了快1k行代码,代码量大并且感觉极难维护。这次遇见的难点主要有:

1:对于分页的处理。

这个问题真的困扰了很久,现在也无法良好解决。比如在遇见我写一个动态表格的时候,发生了分页,但我一旦分页我要先添加一些别的表头信息再继续我的分页实现。这个时候我首先想到了用事件监听之类的,在发生分页时自动插入某些函数。

注册事件监听器:
        pdfDoc.addEventHandler(PdfDocumentEvent.START_PAGE,new PageNumberEventHandler(font, boldFont));

具体实现(GPT实现)

 private static class PageNumberEventHandler implements IEventHandler {private final PdfFont font;private final PdfFont boldFont;public PageNumberEventHandler(PdfFont font, PdfFont boldFont) {this.font = font;this.boldFont = boldFont;}@Overridepublic void handleEvent(Event event) {PdfDocumentEvent docEvent = (PdfDocumentEvent) event;PdfDocument pdfDoc = docEvent.getDocument();PdfPage page = docEvent.getPage();int pageNumber = pdfDoc.getPageNumber(page);int totalPages = pdfDoc.getNumberOfPages();// 更新静态变量InvoiceGenerator.currentPage = pageNumber;InvoiceGenerator.totalPages = totalPages;Rectangle pageSize = page.getPageSize();PdfCanvas pdfCanvas = new PdfCanvas(page.newContentStreamBefore(),page.getResources(), pdfDoc);// 定义绘制区域(关键修正)Rectangle rootArea = new Rectangle(36, 36,pageSize.getWidth() - 72,pageSize.getHeight() - 72);// 创建Canvas对象Canvas canvas = new Canvas(pdfCanvas, rootArea);// 如果是续页(第2页及以后),添加续篇表头if (pageNumber > 1) {try {addContinuationHeader(canvas, font, boldFont, pageNumber);} catch (IOException e) {throw new RuntimeException(e);}}// 添加页码(右上角)Paragraph pageInfo = new Paragraph().add(String.format(PAGE_X_OF_Y_TEXT, pageNumber, totalPages)).setFont(font).setFontSize(8).setFixedPosition(pageSize.getRight() - 36,pageSize.getTop() - 20,100).setTextAlignment(TextAlignment.RIGHT);canvas.add(pageInfo);// 添加修订信息(左下角)Paragraph revInfo = new Paragraph(REV_INFO).setFont(font).setFontSize(7).setFixedPosition(36, 20, 100);canvas.add(revInfo);canvas.close();pdfCanvas.release();}

我后面发现,handler实现出来要写在一个canvas里面,而我开始是把所有内容绘制在一个document里面的,这就导致我把canvas放在document里面的时候document里面的内容无法正确识别出我加入的canvas,然后形成了内容的叠加。遂放弃了用监听器。

手动计算分页

比如计算一页最多可以放多少数据,然后放了这么多条我就新建一页

              document.add(new AreaBreak(AreaBreakType.NEXT_PAGE));// 强制分页

然后继续在这一页里面写。这样的缺点就是我要计算出准确的能放多少数据,如果数据的变化太多这个方法就不行了。其实后面就遇见了,因为后面插入的每条数据的宽度不一样。然后我之前想的是。把宽度计算出来,结果我第一页的宽度计算和后面的不一样,缕了半小时思路还是有问题。

实际场景是:

插入items里面的每一行数据,按道理来说每一行setbond是(10),但是里面的详情字段可能会超长,然后导致换行,我这里就把这个item当作了两行,这样确实可以做到准确分页,但是会出现的问题是,如果分页刚好发生在换行这个item,那么我这个item就应该到下面去,我设定的是第一页只存12行,存过了就分页。但这样如果我第4个item的时候前面已经有10行了,那么我第4行就要超过了,所以第4行就应该放到下一个去。然后当时就是设置了一个总行数totalNum和一个真实到了哪一个item的数量currentNum。第一页的逻辑就是。总行数totalNum就是每次calculateLineBreaks(commercialInvoiceDto.getItems().get(i).getDescription()
                    , font
                    , 8
                    , 340)得到的行数,这个函数就是计算我这个详情实际占多少行。

if(i+calculateLineBreaks(commercialInvoiceDto.getItems().get(i).getDescription(), font, 8, 340)<12)

那么在第二页的逻辑的时候,我就传一个currentNum去代表这一页开始遍历的item的index。这一页可以存40行数据,那么我每遍历一个currentNum进去,我的totalNum就加一次calculateLineBreaks,然后如果下一次+calculateLineBreaks会超过40我就提前分页。这样我感觉从逻辑上是能行通的。结果后面告诉我不想要这种换行的效果,要采用字体缩放来解决。

字体缩放

结果这才是最痛苦的,本来打算周五可以摸摸鱼看看八股,在GPT和百度的挣扎下,最开始老板告诉我有一个自适应缩放的,找了半天没找到。我就打算采用百度上通过计算我这个字段,计算出他会占多少行,然后按照比例缩小字体大小。结果他的计算方式是大致计算,不能准确的识别出里面的空格,就导致缩放的有可能会不够小。然后我采用一个处理溢出操作的方法,结果和注释所说一样,我的自适应缩放只能缩放到7f大小就不能更小了。

        Paragraph para = new Paragraph(Des).setFont(font).setFontSize(fontSize).setWidth(maxWidth);para.setProperty(Property.OVERFLOW_X, OverflowPropertyValue.FIT);//只能自适应缩放到字体大小大于7f的,看见有什么MIN_FONT_SIZE字段,但我用的7.2.5没有,我看了好久源码也没找到。
2:布局问题
边框重合

在生成过程中,我发现有的线框会变得很粗,这是因为两个外边框的重合会引起变粗的效果。因此可以通过setBorder来操控,比如说和上面的方框重合了,就把上面的setBorderBottom设置为(NO_Border)或者把下面的setBorderTop。

同时注意,外边框和内部文字的边框,如果只想呈现一个框框里面只有文字没有分割线的效果就只在new Table的时候设置边框,如果文字中间需要分割就在Cell里面设置。

对齐问题

一行有多少内容可以存放是由开始设置的分割布局来决定的,因此如果有两行内容可以放在一个table里面,但是注意存放的数量。

3:常见概念

(这是在下周一写的了,哥们明明功能都写完了,结果组长告诉我周五说我现在用的包不能过甲方审核,heartbreak了。然后后面尝试用openPdf做,我感觉太麻烦了,刚好老板说不用做了,但是都写这么多了,再说点我这次 遇见的常用的一些内容吧)

一、表格 (Table) 操作
  1. 表格初始化
    • PdfPTable table = new PdfPTable(3);:创建一个包含 3 列的表格。
    • table.setWidthPercentage(100);:设置表格宽度占页面宽度的 100%。
    • table.setTotalWidth(new float[]{30, 20, 50});:自定义列宽比例,这里三列的宽度比例分别为 30、20、50。
  2. 表格样式属性
    • table.setSpacingBefore(10f);:设置表格上方的间距为 10 个单位。
    • table.setSpacingAfter(10f);:设置表格下方的间距为 10 个单位。
    • table.setLockedWidth(true);:锁定列宽,防止列宽在后续操作中自动调整。
    • table.getDefaultCell().setBorder(0);:设置表格默认单元格无边框。
二、单元格 (Cell) 操作
  1. 基础单元格
    • PdfPCell cell = new PdfPCell(new Paragraph("内容"));:创建一个包含文本内容的单元格。
    • cell.setBackgroundColor(BaseColor.ORANGE);:设置单元格的背景颜色为橙色。
    • cell.setBorderColor(BaseColor.BLUE);:设置单元格的边框颜色为蓝色。
  2. 高级属性
    • cell.setColspan(2);:使单元格横向合并 2 列。
    • cell.setRowspan(2);:使单元格纵向合并 2 行。
    • cell.setHorizontalAlignment(Element.ALIGN_CENTER);:设置单元格内容水平居中对齐。
    • cell.setVerticalAlignment(Element.ALIGN_MIDDLE);:设置单元格内容垂直居中对齐。
三、文本与段落
  1. 段落样式
    • Paragraph p = new Paragraph("文本", FontFactory.getFont("SIMHEI", 12));:创建一个包含文本的段落,并设置中文字体为 “黑体”,字号为 12。
    • p.setAlignment(Element.ALIGN_CENTER);:设置段落内容居中对齐。
    • p.setSpacingBefore(5f);:设置段落前的间距为 5 个单位。
四、图形与图像
  1. 插入图片
    • Image img = Image.getInstance("logo.png");:从指定路径(这里是 “logo.png”)获取图片。
    • PdfPCell imgCell = new PdfPCell(img, true);:创建一个包含图片的单元格,true表示图片将被缩放以适应单元格大小。
五、文档元数据
  1. document.addTitle("标题");:设置文档的标题。
  2. document.addKeywords("关键词");:添加文档的关键词。
  3. document.addCreator("创建者");:设置文档的创建者信息。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/77738.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

android编译使用共享缓存

注意 服务器端与客户端系统的版本号需为Ubuntu20.04ccache版本不能低于4.4执行用户需要为sudo权限服务器端nfs目录权限必须为nobody:nogroup 一、服务端配置&#xff1a; 在服务器192.168.60.142上配置 NFS 共享 1.安装 NFS 服务器&#xff1a; 1 sudo apt-get install nfs…

工作中sql总结

sql总结 场景1分组后失败的成功数据带入场景2完全性质的一对一匹配场景3虚拟户的特殊匹配场景4多对多匹配场景5一对一匹配场景6 一对多匹配 场景1分组后失败的成功数据带入 现有一批交易表的数据&#xff0c;根据户名&#xff0c;日期&#xff0c;金额分组&#xff0c;存在TRA…

QML FontDialog:使用FontDialog实现字体选择功能

目录 引言相关阅读FontDialog基本介绍字体属性 实例演示项目结构代码实现Main.qmlmain.cpp 代码解析运行效果 总结 引言 在桌面应用程序开发中&#xff0c;字体选择是一个常见的需求。Qt Quick提供了FontDialog组件来实现这一功能。本文将介绍如何在Qt Quick应用程序中使用Fon…

MCP(3):在CherryStudio中使用MCPServer

上一文章讲述了如何新建一个MCP Server&#xff0c;并在MCP Inspector完成测试。本文讲述如何在CherryStudio中进行测试。 Cherry Studio 是一款由 CherryHQ 开发的多模型支持的 AI 桌面助手&#xff0c;兼容 Windows、Linux 和 macOS 系统&#xff0c;旨在为用户提供更便捷、…

面试题-链表(2)

1.合并两个有序链表&#xff1a; 21. 合并两个有序链表 - 力扣&#xff08;LeetCode&#xff09; public ListNode mergeTwoLists(ListNode headA, ListNode headB){ListNode newheadnew ListNode(-1);ListNode curnewhead;while(headA!null&&headB!null){if(headA.va…

微软Entra新安全功能引发大规模账户锁定事件

误报触发大规模锁定 多家机构的Windows管理员报告称&#xff0c;微软Entra ID新推出的"MACE"&#xff08;泄露凭证检测应用&#xff09;功能在部署过程中产生大量误报&#xff0c;导致用户账户被大规模锁定。这些警报和锁定始于昨夜&#xff0c;部分管理员认为属于误…

【MATLAB第117期】#源码分享 | 基于MATLAB的SSM状态空间模型多元时间序列预测方法(多输入单输出)

【MATLAB第117期】#源码分享 | 基于MATLAB的SSM状态空间模型多元时间序列预测方法&#xff08;多输入单输出&#xff09; 引言 本文使用状态空间模型实现失业率递归预测&#xff0c;状态空间模型&#xff08;State Space Model, SSM&#xff09;是一种用于描述动态系统行为的…

谷歌浏览器搜索后的页面总是覆盖当前页面

最近将搜索引擎换为谷歌后&#xff0c;发现&#xff0c;每次搜索完的结果页面总是覆盖当前页面&#xff0c;非常不方便&#xff0c;在浏览器设置中又找不到类似设置的选项&#xff0c;然后终于在一个博主“如何设置使谷歌浏览器打开链接自动跳转到新标签页而不是覆盖当前页面?…

记录学习的第三十天

今天终于又开始写博客了。 还是滑动窗口问题&#xff0c;这段时间不出意外都是这了 上面的思路是我自己做的&#xff0c;但是不知道为什么不行&#xff0c;有没有大佬能指点一下我。 接下来这道题是进阶的。不过我之前的基础都做的很艰难&#xff0c;道阻且长啊。

QTextDocument 入门

一、QTextDocument QTextDocument 是 Qt 中用于处理富文本文档的核心类&#xff0c;支持文本格式、图片、表格等复杂内容。 1. QTextDocument 入门 1.1 基本概念 QTextDocument 是 Qt 中用于处理富文本内容的核心类&#xff0c;它提供了&#xff1a; 结构化文本存储&#x…

WebRTC服务器Coturn服务器相关测试工具

1、概述 在安装开源的webrtc服务器coturn服务器后&#xff0c;会附带安装coturn的相关工具&#xff0c;主要有以下几种工具 2、turnadmin工具 说明&#xff1a;服务器命令行工具&#xff0c;提供添加用户、添加管理员、生成TURN密钥等功能&#xff0c;turnadmin -h查看详细用…

如何创建Vue3工程

1.首先下载环境 &#xff08;默认下好了VS code&#xff09; Node.js: Node.js 中文网 — 下载 Node.js 选择要下载的版本 检查环境&#xff1a; 在命令行中输入 node ,检查版本号 2.创建工程 1.找到自己要创建工程的文件目录&#xff0c;右键打开打开终端 在终端输入创…

基于大模型的肛裂手术全流程预测与治疗方案研究报告

目录 一、引言 1.1 研究背景与意义 1.2 研究目标与创新点 1.3 研究方法与技术路线 二、肛裂概述与大模型技术原理 2.1 肛裂的医学定义与分类 2.2 肛裂的发病机制与临床症状 2.3 大模型技术简介 三、大模型在肛裂术前预测的应用 3.1 术前风险因素分析与数据收集 3.2 …

【趣味小游戏】--扫雷游戏

目录 一.test.c部分 二.game.h部分 三.game.c部分 前言:前面学习了数组和函数等c语言相关知识&#xff0c;这篇文章我们将通过这些知识分为三个文件来完成扫雷游戏&#xff1b; 1.test.c //文件中写游戏的测试逻辑 2.game.c //文件中写游戏中函数的实现等 3.game.h. //文件中写…

【微服务】SpringBoot制作Docker镜像接入SkyWalking详解

目录 一、前言 二、SkyWalking介绍 2.1 SkyWalking是什么 2.2 SkyWalking核心功能 2.3 SkyWalking整体架构 2.4 SkyWalking主要工作流程 三、前置准备 3.1 搭建SkyWalking服务 3.1.1 下载安装包 3.1.2 上传服务器目录 2.1.3 数据库持久化配置说明 3.1.4 启动skywalk…

从零开始构建微博爬虫与数据分析系统

从零开始构建微博爬虫与数据分析系统 引言 社交媒体平台蕴含着海量的信息和数据&#xff0c;通过对这些数据的收集和分析&#xff0c;我们可以挖掘出有价值的见解。本文将详细介绍如何构建一个完整的微博爬虫和数据分析系统&#xff0c;从数据爬取、清洗、到多维度分析与可视…

深入探索RAG:用LlamaIndex为大语言模型扩展知识,实现智能检索增强生成

大型语言模型&#xff08;LLM&#xff09;&#xff0c;如ChatGPT和Llama&#xff0c;在回答问题方面表现出色&#xff0c;但它们的知识仅限于训练时所获取的信息。它们无法访问私有数据&#xff0c;也无法在训练截止日期之后学习新知识。那么&#xff0c;核心问题就是……我们如…

【延迟双删】简单解析

使用场景&#xff1a;【高并发】情况下的做【更新操作】 什么是延迟双删 首次删除&#xff1a;当需要更新某个数据项时&#xff0c;首先删除缓存中的该项。 更新数据库&#xff1a;接着&#xff0c;更新数据库中的该项。 短暂延迟&#xff1a;然后等待一段很短的时间&#xff…

解决Windows安全中心显示空白页面

1、电脑重装系统后&#xff0c;发现原本一些软件打不开了&#xff0c;电脑莫名认为有病毒&#xff0c;自动删除插件。附图。 2、第一反应是电脑防火墙的原因&#xff0c;默认威胁防护识别到了病毒软件&#xff0c;自动删除。在开始屏幕搜Windows安全中心&#xff0c;打开之后发…

【回眸】error: failed to compile `xxxxxx`重装rust环境

在ubuntu上安装软件报错 error: failed to compile cxx2flow v0.6.2 Caused by: package cargo-platform v0.1.9 cannot be built because it requires rustc 1.78 or newer, while the currently active rustc version is 1.75.0 Try re-running cargo install with --lo…