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) 操作
-
表格初始化
PdfPTable table = new PdfPTable(3);:创建一个包含 3 列的表格。table.setWidthPercentage(100);:设置表格宽度占页面宽度的 100%。table.setTotalWidth(new float[]{30, 20, 50});:自定义列宽比例,这里三列的宽度比例分别为 30、20、50。
-
表格样式属性
table.setSpacingBefore(10f);:设置表格上方的间距为 10 个单位。table.setSpacingAfter(10f);:设置表格下方的间距为 10 个单位。table.setLockedWidth(true);:锁定列宽,防止列宽在后续操作中自动调整。table.getDefaultCell().setBorder(0);:设置表格默认单元格无边框。
二、单元格 (Cell) 操作
-
基础单元格
PdfPCell cell = new PdfPCell(new Paragraph("内容"));:创建一个包含文本内容的单元格。cell.setBackgroundColor(BaseColor.ORANGE);:设置单元格的背景颜色为橙色。cell.setBorderColor(BaseColor.BLUE);:设置单元格的边框颜色为蓝色。
-
高级属性
cell.setColspan(2);:使单元格横向合并 2 列。cell.setRowspan(2);:使单元格纵向合并 2 行。cell.setHorizontalAlignment(Element.ALIGN_CENTER);:设置单元格内容水平居中对齐。cell.setVerticalAlignment(Element.ALIGN_MIDDLE);:设置单元格内容垂直居中对齐。
三、文本与段落
-
段落样式
Paragraph p = new Paragraph("文本", FontFactory.getFont("SIMHEI", 12));:创建一个包含文本的段落,并设置中文字体为 “黑体”,字号为 12。p.setAlignment(Element.ALIGN_CENTER);:设置段落内容居中对齐。p.setSpacingBefore(5f);:设置段落前的间距为 5 个单位。
四、图形与图像
-
插入图片
Image img = Image.getInstance("logo.png");:从指定路径(这里是 “logo.png”)获取图片。PdfPCell imgCell = new PdfPCell(img, true);:创建一个包含图片的单元格,true表示图片将被缩放以适应单元格大小。
五、文档元数据
document.addTitle("标题");:设置文档的标题。document.addKeywords("关键词");:添加文档的关键词。document.addCreator("创建者");:设置文档的创建者信息。
相关文章:
Itext进行PDF的编辑开发
这周写了一周的需求,是制作一个PDF生成功能,其中用到了Itext来制作PDF的视觉效果。其中一些功能不是很懂,仅作记录,若要学习请仔细甄别正确与否。 开始之前,我还是想说,这傻福需求怎么想出来的,…...
Hibernate的组件映射
在实际的开发中,使用的是非常多的,还有几种比较特殊的关系映射: 组件映射继承映射 先看一下组件映射: 组件映射中, 组件也是一个类, 但是这个类它不独立称为一个实体, 也就是说, 数据库中没有一个表格单独的和它对应, 具体情况呢, 看演示:...
C++ 操作符重载Operator
C可以重载大多数操作符,如算术运算符号,-号。 位操作符<<,>> 下标符号[]等都可以重载。 重载的意思,是让这些符号,按你定义的行为来执行代码,但是这种自定义,是有限制的,必须有一…...
Docker 镜像、容器和 Docker Compose的区别
前言:Docker 的镜像、容器和 Docker Compose 是容器化技术的核心组件,以下是对它们的详细解析及使用场景说明。 1、Docker 镜像(Image) 定义: 镜像是只读模板,包含运行应用程序所需的代码、…...
Linux深度探索:进程管理与系统架构
1.冯诺依曼体系结构 我们常见的计算机,如笔记本。我们不常见的计算机,如服务器,大部分都遵守冯诺依曼体系。 截至目前,我们所认识的计算机,都是由⼀个个的硬件组件组成。 输入设备:键盘,鼠标…...
一段式端到端自动驾驶:VAD:Vectorized Scene Representation for Efficient Autonomous Driving
论文地址:https://github.com/hustvl/VAD 代码地址:https://arxiv.org/pdf/2303.12077 1. 摘要 自动驾驶需要对周围环境进行全面理解,以实现可靠的轨迹规划。以往的方法依赖于密集的栅格化场景表示(如:占据图、语义…...
17.整体代码讲解
从入门AI到手写Transformer-17.整体代码讲解 17.整体代码讲解代码 整理自视频 老袁不说话 。 17.整体代码讲解 代码 import collectionsimport math import torch from torch import nn import os import time import numpy as np from matplotlib import pyplot as plt fro…...
把dll模块注入到游戏进程的方法_基于文件修改的注入方式
1、概述 本文主要是介绍两种基于文件修改的注入方式,一种是“DLL劫持”,另一种是“修改导入表”。这两种注入方式都是利用操作系统加载PE时的特点来实现的,我们在实现这两种注入方式时只需专注于注入dll的实现,而不用花费额外的精力去关注注入器的实现。要想深入了解这两种…...
4月21日星期一今日早报简报微语报早读
4月21日星期一,农历三月廿四,早报#微语早读。 1、女子伸腿阻止列车关门等待同行人员,被深圳铁路警方行政拘留; 2、北理工再通报:开除宫某党籍,免去行政职务,解除聘用关系; 3、澳门…...
Spark(20)spark和Hadoop的区别
Apache Spark 和 Apache Hadoop 都是广泛使用的开源大数据处理框架,但它们在设计理念、架构、性能和适用场景等方面存在显著区别。以下是它们的主要区别: ### **1. 架构设计** - **Hadoop**: - **HDFS(Hadoop Distributed File…...
Kubeflow 快速入门实战(二) - Pipelines / Katib / KServer
承接前文博客 Kubeflow 快速入门实战(一) Kubeflow 快速入门实战(一) - 简介 / Notebooks-CSDN博客文章浏览阅读441次,点赞19次,收藏6次。本文主要介绍了 Kubeflow 的主要功能和能力,适用场景,基本用法。以及Notebook,…...
【JavaEE初阶】多线程重点知识以及常考的面试题-多线程进阶(一)
本篇博客给大家带来的是多线程中常见的所策略和CAS知识点. 🐎文章专栏: JavaEE初阶 🚀若有问题 评论区见 ❤ 欢迎大家点赞 评论 收藏 分享 如果你不知道分享给谁,那就分享给薯条. 你们的支持是我不断创作的动力 . 王子,公主请阅🚀 要开心要快…...
ECA 注意力机制:让你的卷积神经网络更上一层楼
ECA 注意力机制:让你的卷积神经网络更上一层楼 在深度学习领域,注意力机制已经成为提升模型性能的重要手段。从自注意力(Self-Attention)到各种变体,研究人员不断探索更高效、更有效的注意方法。今天我们要介绍一种轻…...
基于定时器查询模式的LED闪烁(STC89C52单片机)
#include <reg52.h> sbit LED P0^0; sbit ADDR0 P1^0; sbit ADDR1 P1^1; sbit ADDR2 P1^2; sbit ADDR3 P1^3; sbit ENLED P1^4; void main() { unsigned char cnt 0; //定义一个计数变量,记录T0溢出次数 ENLED 0; //使能U3,选择…...
SAP系统生产跟踪报表入库数异常
生产跟踪报表入库数异常 交库21820,入库43588是不可能的 原因排查: 报表的入库数取值,是取移动类型321 (即系检验合格后过账到非限制使用)的数. 查凭证,101过账2次21807,321过账了2次21794,然后用102退1次21794.就是说这批物料重复交库了. 解决: 方案一:开发增强设…...
Kubernetes控制平面组件:调度器Scheduler(一)
云原生学习路线导航页(持续更新中) kubernetes学习系列快捷链接 Kubernetes架构原则和对象设计(一)Kubernetes架构原则和对象设计(二)Kubernetes架构原则和对象设计(三)Kubernetes控…...
08-DevOps-向Harbor上传自定义镜像
harbor创建完成,往harbor镜像仓库中上传自定义的镜像,包括新建项目、docker配置镜像地址、镜像重命名、登录harbor、推送镜像这几个步骤,具体操作如下: harbor中新建项目 访问级别公开,代表任何人都可以拉取仓库中的镜…...
Vue v-for 循环DOM 指定dom个数展示一行
在Vue.js中,如果想根据v-for循环的结果来控制哪些元素应该在一行中展示,你可以通过计算属性或者方法来实现。这里使用CSS改变样式和js脚本两种方式做到这一点,根据你的具体需求选择适合的方法。 方法1:使用计算属性 如果你想要基…...
mysql控制单表数据存储及单实例表创建
1. 单表数据存储不要过大 主流建议 保守建议。100万以内保持最佳性能其他。不超过2000万 理论依据。 B树层级可能变多。从3增加到4。导致索引查询路径边长,增加IO开销 优化 加索引。对高频查询字段增加索引。避免全表扫描低频历史数据通过分区表或归档隔离。足够的…...
极验4滑块笔记:整理思路--填坑各种问题
最近在研究某验4逆向分析,以前没弄过这种,所以爬了很多坑,就是把分享给大家~ 1.这个gcaptcha4.js需要逆向,我的方法很笨就是将_ᕶᕴᕹᕶ()这个蝌蚪文打印处来,全局替换一下,然后Unicode这种代码࿰…...
LX3-初识是单片机
初识单片机 一 什么是单片机 单片机:单片微型计算机单片机的组成:CPU,RAM(内存),flash(硬盘),总线,时钟,外设…… 二 Coretex-M系列介绍 了解ARM公司与ST公司ARM内核系列: A 高性能应用,如手机,电脑…R 实时性强,如汽车电子,军工…M 超低功耗,如消费电子,家电,医疗器械 三…...
2025年渗透测试面试题总结-拷打题库10(题目+回答)
网络安全领域各种资源,学习文档,以及工具分享、前沿信息分享、POC、EXP分享。不定期分享各种好玩的项目及好用的工具,欢迎关注。 目录 2025年渗透测试面试题总结-拷打题库10 1. CSRF成因及防御措施 | 非Token防御 2. XSS Worm原理 3. Co…...
Linux系统下docker 安装 MySQL
踩坑解决: 1、docker安装mysql,不需要执行search 2、pull时,需要指定版本号 3、连接Navicat需要看阿里云端口号是否开启 在拉取镜像的时候,如果不使用代理服务器,docker search mysql不需要执行 本人在未使用代理服…...
配置 VS Code 使用 ESLint 格式化
1、在设置里面搜索Default Formatter,下拉框里选择eslint 2、并勾选Enables ESlint as a formatter 3、再在settings.json文件中添加配置代码,如下所示: 1) 、打开 VS Code 设置 快捷键:Ctrl ,(Mac: ⌘ ,…...
从代码实现理解Vision Permutator:WeightedPermuteMLP模型解析
从代码实现理解Vision Permutator:WeightedPermuteMLP模型解析 随着人工智能的快速发展,视觉识别任务变得越来越重要。最近提出的Vision Permutator架构为这一领域带来了新的思路,它通过可学习的排列操作重新定义了特征交互的方式。 今天我…...
Web开发:ABP框架10——使用数据库存储文件,完成文件的下载和上传
一、简要介绍 字节数组:字节数组是存储数据的字节序列,常用于二进制数据(如图片、音视频、文档等)的表示。 文件和字节的关系:文件是由字节构成,字节是文件内容的基本单位。 文件以字节形式存储在服务器数…...
SystemVerilog语法之内建数据类型
简介:SystemVerilog引进了一些新的数据类型,具有以下的优点:(1)双状态数据类型,更好的性能,更低的内存消耗;(2)队列、动态和关联数组,减少内存消耗…...
NestJS-Knife4j
文章目录 前言✅ 一、什么是 Knife4j?✅ 二、Knife4j 与 Swagger 对比✅ 三、NestJS-Knife4j 集成1. 安装依赖2. 配置 Swagger 与 Knife4j3. 启动应用并访问接口文档 ✅ 四、功能增强1. **接口分组**2. **请求/响应示例**3. **接口文档的美化** ✅ 五、总结 前言 N…...
【项目管理】成本类计算 笔记
项目管理-相关文档,希望互相学习,共同进步 风123456789~-CSDN博客 (一)知识总览 项目管理知识域 知识点: (项目管理概论、立项管理、十大知识域、配置与变更管理、绩效域) 对应&…...
手机投屏到电视方法
一、投屏软件 比如乐播投屏 二、视频软件 腾讯视频、爱奇艺 三、手机无线投屏功能 四、有线投屏 五、投屏器...
