news 2026/9/29 8:48:32

Java后端必看:itext操作PDF全攻略,水印签章文本替换一次讲透

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Java后端必看:itext操作PDF全攻略,水印签章文本替换一次讲透

简介:这是一份面向Java开发者的PDF处理工具源码包,基于iText库实现文档创建、电子签章、斜字水印、文本替换等高频操作,能解决实际项目中的PDF生成与编辑难题。包内共32个文件,含7个Java源文件与9个class文件,便于阅读与调试;5个jar包覆盖iText核心库及BouncyCastle安全扩展,另有p12数字证书用于签章验签、png/jpg图片作水印素材,并附项目配置与readme说明,整体约8.77MB,目录结构清晰便于查找。已有1571人学习下载,适合具备Java基础、想快速上手PDF处理的开发者参考。借助示例工程可掌握PdfWriter创建文档、PdfStamper设置签名、PdfFormXObject绘制斜字水印及AcroFields替换文本等关键API,同时理解大文件处理与版本兼容性的注意事项,为正式项目提供可复用的代码模板。

1. itext操作PDF文档:从创建到签章水印,这份资源到底能解决什么

itext操作PDF文档,第一次碰的人往往会被坐标系和内容流整懵。我当初接手合同批处理项目,要在几百个PDF上加斜字水印、替换甲方名称、再盖电子公章,试了好几个库,最后还是落到itext上。这份资源把创建PDF、斜字水印、文本替换、电子签章四个场景拆成可以直接跑的Java示例,每段带注释和参数说明,适合刚接触itext的Java后端,也适合要把PDF批处理功能塞进现有系统的同学。看完你能分清哪些操作是itext的本职,哪些是边缘试探,省掉翻文档和反复试错的时间。这里说的签章是图片印章,不是数字证书签名的复杂玩法,覆盖最常见的企业盖章需求。

2. itext创建PDF文档:搭建最小项目与表格布局

这一章解决“从零生成PDF并排布内容”的问题。很多人上来就写Document,却不知道页面大小、边距、字体选型会影响后续所有操作。先讲选型理由,再给最小可运行代码,最后用表格说明参数怎么调。

2.1 为什么选itext:三个理由和一个限制

在Java生态里做PDF生成,itext几乎是默认选项。第一,它直接暴露PDF底层对象,PdfContentByte、PdfStamper都能拿到手,做起定制操作来比PDFBox直接拼内容流要顺手;第二,它内置了中文字体方案,通过itext-asian包配合BaseFont能输出中文,不依赖服务器系统字体;第三,它适合无界面服务端环境,很多报表导出、合同生成都是它在撑。限制也明显:itext分AGPL协议和商业授权,内部工具用AGPL没问题,但要分清楚是itext5还是itext7,两者包名完全不同,代码不能混用。

我优先用itext5的com.lowagie.text包,因为老项目沉淀多,网上示例九成都是这个,遇到问题容易搜到。itext7改成了com.itextpdf.kernel等新包,API更现代,但迁移成本高,除非全新项目且有人接力,否则我不主动选。这份资源里的代码全部基于itext5,pom里引入依赖就能跑。

2.2 创建PDF的最小可运行代码

先看pom依赖:

<dependency> <groupId>com.itextpdf</groupId> <artifactId>itextpdf</artifactId> <version>5.5.13.3</version> </dependency> <dependency> <groupId>com.itextpdf</groupId> <artifactId>itext-asian</artifactId> <version>5.2.0</version> </dependency>

itextpdf是主体,itext-asian提供中文字体。我一般还会带上org.slf4j:slf4j-simple,因为itext5的日志走自己的logger,没有绑定会在控制台输出一堆警告,不影响运行但看着烦。

然后是创建文档的完整代码:

import com.lowagie.text.Document; import com.lowagie.text.PageSize; import com.lowagie.text.Paragraph; import com.lowagie.text.Font; import com.lowagie.text.pdf.BaseFont; import com.lowagie.text.pdf.PdfWriter; import java.io.FileOutputStream; public class CreateDemo { public static void main(String[] args) throws Exception { // 1. 创建Document对象,A4页面,左右边距54磅约1.9cm,上下36磅约1.27cm Document document = new Document(PageSize.A4, 54, 54, 36, 36); // 2. 绑定PdfWriter,输出到文件 PdfWriter.getInstance(document, new FileOutputStream("create_demo.pdf")); // 3. 打开文档 document.open(); // 4. 注册中文字体:STSong-Light + UniGB-UCS2-H BaseFont bf = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED); Font font = new Font(bf, 12, Font.NORMAL); // 5. 添加段落 document.add(new Paragraph("你好,itext创建PDF成功。", font)); document.add(new Paragraph("第二行,验证分段效果。", font)); // 6. 关闭 document.close(); } }

逻辑说明:Document是画布,PdfWriter负责把画布写入文件。构造参数顺序是页面宽、高、左、右、上、下边距,单位是磅(point),1磅=1/72英寸。BaseFont.createFont的第三入参NOT_EMBEDDED表示不嵌入字体,生成的文件体积小,但换机器打开可能显示异常;要可移植就改成BaseFont.EMBEDDED。

参数说明:A4宽度595磅,高度842磅。如果你用毫米录入,记得换算公式:磅 = 毫米 * 72 / 25.4。这个换算在签章定位时会反复用。STSong-Light是Adobe的中文字体名,配合UniGB-UCS2-H编码能处理简体中文;繁体场景把编码换成UniCNS-UCS2-H,不然会有缺字问题。

如果你直接跑这段代码,可能会遇到NoClassDefFoundError: org/slf4j/LoggerFactory,那就是缺slf4j。在pom里加slf4j-simple依赖即可。这个依赖问题新手几乎必遇,属于itext5的经典玄学。

2.3 表格与段落:让内容不再是白纸黑字

实际业务里,创建PDF基本都要带表格。PdfPTable是itext5里最常用的表格类,给它添加单元格、设置列宽比例,比用绝对坐标排版省心得多。下面是一个带表头、合并单元格的示例:

import com.lowagie.text.pdf.PdfPTable; import com.lowagie.text.pdf.PdfPCell; import com.lowagie.text.Element; // 在document.open()之后创建表格,5列 PdfPTable table = new PdfPTable(5); table.setWidthPercentage(100); // 表格占页面宽度 table.setSpacingBefore(10); // 表格前间距 table.setSpacingAfter(10); // 表格后间距 // 设置列宽比例 table.setWidths(new float[]{1, 2, 2, 2, 1}); // 表头单元格 PdfPCell header = new PdfPCell(new Paragraph("序号", font)); header.setBackgroundColor(BaseColor.LIGHT_GRAY); header.setHorizontalAlignment(Element.ALIGN_CENTER); table.addCell(header); table.addCell(new PdfPCell(new Paragraph("名称", font))); // 合并单元格 PdfPCell merged = new PdfPCell(new Paragraph("跨列合并", font)); merged.setColspan(3); merged.setHorizontalAlignment(Element.ALIGN_CENTER); table.addCell(merged); // 普通数据行 table.addCell("1"); table.addCell("合同编号"); table.addCell("2025-001"); table.addCell("甲方"); table.addCell("已盖章"); document.add(table);

逻辑说明:PdfPTable列数在构造时确定,之后addCell按行填充。setWidths接收各列宽度比例,数组长度必须等于列数,否则抛异常。setColspan合并跨列的单元格。背景色BaseColor是itext自己的RGB类,别混用java.awt.Color,虽然有些情况能跑,但那是兼容性玄学。

参数说明:setWidthPercentage(100)让表格自适应页宽。如果同时调用setTotalWidth指定固定宽度,百分比会失效,二者只能选一个。单元格默认垂直居中,水平靠左,要居中必须显式setHorizontalAlignment。

很多人创建PDF后会遇到“文字没显示全”的问题,因为Paragraph默认行距是字号的1.5倍,段落很长时自动换行不会自动分页,需要手动document.newPage()。itext的分页策略是:当前Y坐标小于下边距时自动新页,但表格不会自动拆分表头。如果表格要跨页且每页重复表头,就给表头行调用setHeaderRows(1),合同、发票这类多页文档尤其常用。

3. 斜字水印实现:旋转、透明度与多行多列铺满页面

水印是PDF操作里最常见的需求,斜字水印尤其多用于合同和内部资料。这一章先讲原理,再给多行多列的实现代码,最后说参数怎么调。

3.1 斜字水印的原理:PdfContentByte与图形状态

PDF的内容不是图层,而是一串绘制指令。itext通过PdfContentByte往页面内容流里追加指令。水印可以放在页面内容之上(over)或之下(under),对应PdfWriter的getOverContent(pageNum)和getUnderContent(pageNum)。斜字效果的关键是修改文本矩阵,通过setTextMatrix的旋转矩阵实现。

先用最原始的方式写一行文字,不旋转:

PdfContentByte cb = writer.getDirectContentUnder(); // 底层内容 cb.saveState(); cb.beginText(); cb.setFontAndSize(bf, 40); cb.setTextMatrix(30, 400); // 平移到坐标(30,400) cb.showText("内部资料"); cb.endText(); cb.restoreState();

这里bf是已创建的BaseFont。运行后能看到一行水平文字。注意beginText和endText之间的所有操作构成一个文本对象,saveState/restoreState保护上下文,避免影响后续绘制。

旋转是setTextMatrix的六个参数:(a, b, c, d, e, f)。其中a,b,c,d决定旋转和缩放,e,f是平移坐标。要让文字逆时针旋转角度alpha,四个值分别是cos(alpha), sin(alpha), -sin(alpha), cos(alpha)。旋转中心不是文字的左上角,而是(e,f)这个基点,所以多行水印循环时,每个基点的位置都要算好。

3.2 多行多列斜字水印的代码实现

常见做法是把页面切成网格,在每个格子里画同一行斜字。页面尺寸和边距要拿准,否则水印会贴边或被截断。下面是完整代码,适合对已有PDF批量加斜字水印:

import com.lowagie.text.pdf.PdfContentByte; import com.lowagie.text.pdf.BaseFont; import com.lowagie.text.pdf.PdfStamper; import com.lowagie.text.pdf.PdfReader; import com.lowagie.text.pdf.PdfGState; public static void addWatermark(PdfReader reader, String outPath, String text, int fontSize, float angleDeg, float opacity) throws Exception { int pageCount = reader.getNumberOfPages(); // 取第一页尺寸,假设所有页大小一致,不一致时需要逐页取 com.lowagie.text.Rectangle pageSize = reader.getPageSize(1); float pageW = pageSize.getWidth(); float pageH = pageSize.getHeight(); PdfStamper stamper = new PdfStamper(reader, new java.io.FileOutputStream(outPath)); BaseFont bf = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED); float alpha = (float) Math.toRadians(angleDeg); float cosA = (float) Math.cos(alpha); float sinA = (float) Math.sin(alpha); // 网格间距,按字号比例给,可调 float stepX = fontSize * 6f; float stepY = fontSize * 4f; for (int page = 1; page <= pageCount; page++) { PdfContentByte over = stamper.getOverContent(page); over.saveState(); if (opacity < 1f) { PdfGState gs = new PdfGState(); gs.setFillOpacity(opacity); gs.setStrokeOpacity(opacity); over.setGState(gs); } over.beginText(); over.setFontAndSize(bf, fontSize); // 网格循环 for (float y = stepY; y < pageH; y += stepY) { for (float x = stepX; x < pageW; x += stepX) { over.setTextMatrix(cosA, sinA, -sinA, cosA, x, y); over.showText(text); } } over.endText(); over.restoreState(); } stamper.close(); reader.close(); }

逻辑说明:PdfStamper用于修改已有PDF,getOverContent取顶层内容,水印会压在正文上。循环从stepY和stepX开始,是为了给页面边缘留出半个间距,避免文字写出页外。setTextMatrix前四个参数决定旋转,最后两个是基点坐标。每页画完restoreState,避免页面间状态污染。

参数说明:stepX和stepY控制水印密度,数值越小越密集。fontSize * 6f和fontSize * 4f是我测试下来视觉比较均匀的默认值。如果水印要防扫描复印,把stepX降到fontSize * 3f即可,但页面会显得很满。透明度用PdfGState设置,注意setFillOpacity和setStrokeOpacity两个都要设,不然打印时描边不透明,水印会显得生硬。

3.3 水印参数调优:字号、旋转角度、透明度

实际项目中,这几个参数是最常改的:

参数常见值说明
angleDeg30~45太直像牢房,太斜阅读困难;合同类一般30
opacity0.3~0.5太淡扫描不见,太浓影响阅读
fontSize40~60大字用于警告,小字用于编号
stepX字号*6横向间距,要密就*4
stepY字号*4纵向间距,要疏就*5

角度不要低于15度,容易和正文方向混淆;超过60度时旋转包围盒变大,文字容易跑到页面外。透明度低于0.2时,复印基本扫不出来,等于没加。

另一个要注意的点:如果原PDF是扫描件(内容都是图片),over水印会浮在图片上,打印没问题;但如果是已经电子签章的PDF,over水印可能压住签章区域,这时改用getUnderContent,或者避开签章坐标。我的实践是,文本替换、水印、签章的操作顺序固定为“先替换文本,再加水印,最后签章”,这样每一步都不干扰前一步的内容层。

4. 文本替换实操:模板填充与内容修正的两种路径

文本替换是itext里最容易被高估的功能。PDF没有Word那种“查找替换”底层支持,这一章给出两条实际可用的路径:表单域填充和内容区覆盖重写,并说清边界。

4.1 文本替换的三种思路及其边界

思路一:用表单域。如果PDF是用Acrobat做的带表单的情况,PdfStamper可以直接设字段值,正规且稳定。思路二:用PdfCleanUp把目标区域内容擦掉,再在同一位置写入新文本,模拟替换。思路三:直接解析内容流里的Tj和TJ操作符重写字符串,对字体嵌入和编码要求极高,非必要不用。

思路二是通用性最强的,也是这份资源推荐的做法。前提是你知道要替换的文字在页面上大概的位置和区域,比如合同首页的“甲方名称:张三”这一段。通常先用LocationTextExtractionStrategy提取文本定位,但准确率受字体影响,不是100%可靠。更稳的方法是在生成PDF时就在已知坐标的模板里留好占位符,比如“甲方名称:[甲方]”,后面替换时直接擦掉占位区域写入真实值。

如果用表单域模板,填充代码最稳:

PdfReader reader = new PdfReader("template.pdf"); PdfStamper stamper = new PdfStamper(reader, new FileOutputStream("filled.pdf")); AcroFields fields = stamper.getAcroFields(); fields.setField("contractName", "采购合同"); fields.setField("partyA", "某科技有限公司"); stamper.setFormFlattening(true); // 扁平化,输出后没有可编辑域 stamper.close(); reader.close();

这个方式的前提是你有带表单域的模板。没有的话,用下面的区域覆盖方案。

4.2 基于PdfCleanUp的字符替换代码

PdfCleanUpProcessor从itext5.5.5开始可用,它能把指定矩形区域的内容擦除。然后通过PdfStamper写入新文本。下面是代码:

import com.itextpdf.text.pdf.PdfReader; import com.itextpdf.text.pdf.PdfStamper; import com.itextpdf.text.pdf.parser.PdfCleanUpProcessor; import com.itextpdf.text.pdf.parser.PdfCleanUpLocation; import com.itextpdf.text.Rectangle; import com.itextpdf.text.pdf.BaseFont; import com.itextpdf.text.pdf.PdfContentByte; import java.util.ArrayList; import java.util.List; public static void replaceText(PdfReader reader, String outPath, String newText, float x, float y, float width, float height, BaseFont bf, int fontSize) throws Exception { // 先擦除指定区域 List<PdfCleanUpLocation> cleanUpLocations = new ArrayList<>(); cleanUpLocations.add(new PdfCleanUpLocation(1, new Rectangle(x, y, x + width, y + height), BaseColor.WHITE)); PdfCleanUpProcessor cleaner = new PdfCleanUpProcessor(cleanUpLocations, reader); cleaner.cleanUp(); // 再写入新内容 PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(outPath)); PdfContentByte over = stamper.getOverContent(1); over.saveState(); over.setColorFill(BaseColor.BLACK); over.beginText(); over.setFontAndSize(bf, fontSize); over.setTextMatrix(x, y); over.showText(newText); over.endText(); over.restoreState(); stamper.close(); }

逻辑说明:PdfCleanUpLocation接收页码、矩形和填充颜色,默认用白色矩形把区域内所有可视内容遮住。然后PdfStamper把新文本写到同一位置。这里的Rectangle左下角是(x,y),宽高必须足够覆盖旧文字,否则旧文字的边缘残留,视觉上像翻车。如果页面本身有底色,把BaseColor.WHITE改成和底色一致,避免白斑。

参数说明:cleanUpLocations可以传多个区域,批量替换不同位置时很方便。新文本颜色用setColorFill设置。中文字体必须用支持中文的BaseFont,不要用默认英文,否则新文字变方块。

4.3 替换无效时怎么办:字体嵌入与内容流解析

如果用了上面的代码后新文字错位或者旧文字没擦干净,查三件事:第一,擦除区域坐标是否基于PDF页面原点;有些PDF有/Rotate旋转属性,坐标要相应变换,否则区域会偏移。第二,旧文字如果是嵌入字体且颜色是CMYK模式,PdfCleanUp对CMYK支持不好,可能擦不掉。第三,检查单位,Rectangle用的是磅,如果从UI拿的是毫米,记得乘72/25.4。

如果只是简单合同模板,我建议放弃“原位替换”,改为“整体重新生成”。维护一个纯文本模板,用坐标填充,用itext直接生成最终PDF,比在旧PDF上修修补补稳定得多。文本替换只适合少量定制场景,不适合大批量处理不同格式的源PDF。要定位旧文字位置,可以用PdfReaderContentParser配合LocationTextExtractionStrategy,但提取出的字符串有时乱序,字间距被拆开,我基本只用来做粗略定位,然后手动微调几磅。

5. itext操作PDF避坑与排查:五个高频问题

这一章专门记录踩坑经验,每条按现象、原因、解决三个部分展开,都是我在真实项目中遇过的问题。

5.1 问题一:生成的PDF中文乱码或显示成空白

现象:用itext创建文档,中文部分要么乱码,要么整段消失,英文正常。

原因:没有注册支持中文的BaseFont,或者用了系统字体路径但服务器上没有该字体。itext默认字体是Helvetica,不支持中文字符,所以中文全部丢。

解决:统一使用BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED),并确保pom里有itext-asian依赖。如果是自定义字体文件,比如simhei.ttf,把ttf放到classpath,用createFont("/font/simhei.ttf", BaseFont.IDENTITY_H, BaseFont.EMBEDDED)。这里IDENTITY_H编码能覆盖所有Unicode字符。注意不要用Helvetica去画中文,否则怎么写都是方块。

5.2 问题二:签章位置偏移,盖到别的地方去了

现象:用图片签章,明明坐标是预期值,打印出来印章却跑到页面上方或右侧。

原因:PDF坐标系原点在左下角,而大多数图像处理工具原点在左上角;此外,如果页面有CropBox偏移或旋转属性,坐标基准也会变。

解决:先读取页面实际大小,用reader.getPageSizeWithRotation(pageNum)获取页面矩形,以getLeft()和getBottom()作为原点。如果从左上角换算,公式是:pdfX = left + x,pdfY = top - y - height,其中height是印章图片高度(磅)。盖章代码里建议用PdfImage和absolutePosition,避免受裁剪框影响。签章坐标涉及毫米换算,我在封装工具类时已经把毫米转磅的逻辑写进方法了,业务层直接传毫米就行。

5.3 问题三:水印把正文盖得看不清

现象:水印加完后,正文被半透明色块影响,文字阅读困难。

原因:使用getOverContent且透明度设置不当,或者水印颜色太深,比如用了纯黑。

解决:换成getUnderContent画水印,让水印在正文下方;或者把透明度降到0.3以下。另外,水印颜色用浅灰如BaseColor(200,200,200),不要用纯黑。如果是扫描件,getUnderContent会把水印压在扫描图片下面,等于看不见,这时只能降低getOverContent的透明度。

5.4 问题四:替换文本后新内容变成方块

现象:执行文本替换后,新写入的中文在阅读器里显示为一串方块。

原因:新写入用的BaseFont不是中文字体,或者用了setFontAndSize时传入了Helvetica。还有可能是PdfStamper.close()之前被GC回收了字体对象。

解决:检查bf是否来自STSong-Light或自定义中文字体,不要复用创建PDF时那个默认英文font。在PdfStamper中写入时,确保BaseFont变量作用域大于stamper.close()的调用点,不要在局部方法里创建后立刻被回收。如果还不放心,用BaseFont.EMBEDDED嵌入字体,能规避大部分显示问题。

5.5 问题五:itext版本差异导致API找不到

现象:代码在别人电脑上跑不起来,报NoSuchMethodError或ClassNotFoundException。

原因:项目里同时存在itext5和itext7的依赖,或者被传递依赖覆盖了版本。比如用了com.itextpdf.text.Document但另一处引入了com.itextpdf.kernel.pdf.PdfDocument,包名完全不兼容。

解决:在pom的<dependencyManagement>中锁定itext5版本为5.5.13.3,并排除传递依赖里可能带来的其他itext版本。如果必须用itext7,就别参考itext5的写法,API对象模型完全不同。我的习惯是新建项目时确认一下pom树,mvn dependency:tree | grep itext,看一眼有没有重复,有就排除。

6. 进阶玩法:把PDF批处理封装成工具,顺便聊聊签章防篡改

最后一章落到效率提升上。当你把创建、水印、替换、签章都跑通后,会发现重复代码很多。我习惯封装一个PdfTool类,提供链式调用,批量处理几十个文件就是循环。

6.1 封装一个PDF工具类:批量加水印/替换/签章

下面片段演示怎么把水印和签章串起来:

public class PdfTool { private String srcPath; private String destPath; private String watermarkText; private List<SignatureInfo> signatures = new ArrayList<>(); public PdfTool(String srcPath) { this.srcPath = srcPath; } public PdfTool withWatermark(String text) { this.watermarkText = text; return this; } public PdfTool addSignature(byte[] imgBytes, float x, float y, float width, float height) { signatures.add(new SignatureInfo(imgBytes, x, y, width, height)); return this; } public void execute() throws Exception { PdfReader reader = new PdfReader(srcPath); PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(destPath)); BaseFont bf = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED); for (int page = 1; page <= reader.getNumberOfPages(); page++) { if (watermarkText != null) { PdfContentByte over = stamper.getOverContent(page); over.beginText(); over.setFontAndSize(bf, 42); over.setTextMatrix(0.866f, 0.5f, -0.5f, 0.866f, 100, 100); over.showText(watermarkText); over.endText(); } for (SignatureInfo sig : signatures) { addImageToPage(stamper, page, sig, bf); } } stamper.close(); reader.close(); } }

逻辑说明:PdfTool把水印和签章叠加到每一页。addImageToPage内部调getOverContent(page).addImage,图片以矩形区域放置。withWatermark只画一行水印,足够演示;要铺满页面可以复用第3章的网格循环。

参数说明:addSignature传入图片字节数组和位置尺寸。位置单位是磅,如果界面上收的是毫米,在传入时换算。setTextMatrix里的0.866f和0.5f是30度旋转的cos和sin值,写死了30度。

6.2 验证结果:用itext读取刚生成的PDF

批处理跑完,不要只看文件大小,要读回来验证。我用下面的代码检查页数和首页文本:

PdfReader reader = new PdfReader("output.pdf"); int pages = reader.getNumberOfPages(); String content = PdfTextExtractor.getTextFromPage(reader, 1); System.out.println("页数=" + pages + ", 首页文本=" + content); reader.close();

如果content里能搜到水印文本,说明水印画上去了;搜不到,检查是不是写在扫描件里。文本提取只能拿到文本对象,扫描件是图像,搜不到很正常。

6.3 一个教训:签章之后的PDF不能再直接改内容

最后必须分享一个血泪教训。有些流程是先盖章再改字段,这在技术上非常危险。数字签章(哪怕是图片签章)在PDF阅读器里会被视为内容完整性的一部分,一旦签章后被修改,阅读器会提示文档被篡改。我用图片签章时发现,只要改了页面任何内容,Adobe Reader就会显示“签章有效性未知”或红叉。

从那以后,我每次处理带签章的PDF,都强制把“签章”放在所有修改流程的最后一步。如果后续确实要改内容,就用原文件重新生成PDF,再重新签章,不要直接在已盖章的PDF上动手。这条经验替我省了很多返工。希望帮到你。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/29 8:48:24

小白/程序员轻松入门CrewAI,玩转大模型多智能体协作

CrewAI是一个开源的Python多智能体编排框架&#xff0c;旨在解决如何组织多个AI智能体协作完成复杂任务的问题。它通过Crew&#xff08;团队&#xff09;实现自治协作&#xff0c;用多个Agent自主决策和动态委派任务&#xff1b;通过Flow&#xff08;流水线&#xff09;实现精确…

作者头像 李华
网站建设 2026/9/29 8:47:20

FDE(前线部署工程师):大模型落地必备技能,小白也能收藏学习!

本文深入解析了FDE&#xff08;前线部署工程师&#xff09;的起源、为何现在火热、日常职责、能力要求以及普通人的学习路径。FDE是负责将AI技术落地到企业实际应用中的关键角色&#xff0c;需要具备工程能力、AI能力、数据集成能力和业务理解能力。文章强调&#xff0c;学习FD…

作者头像 李华
网站建设 2026/9/29 8:46:11

基于LSTM网络进行微博情绪识别项目

一、文件分工train_eval_test.py文件用来构建词表、训练、验证、测试。load_dataset.py文件用来读取CSV&#xff0c;把文本转成词表ID&#xff0c;划分训练集、验证集、测试集&#xff0c;并做batch迭代。model.py文件用来定义TextRNN模型&#xff1a;Embedding BiLSTM Linea…

作者头像 李华
网站建设 2026/9/29 8:46:06

Anubis科研绘图工具:基于XML配置的确定性矢量图生成方案

Anubis 是一款面向生物信息学与科研绘图场景的轻量级可视化工具&#xff0c;其核心定位并非替代 Matplotlib 或 ggplot2&#xff0c;而是解决一类特定痛点&#xff1a;当研究者手头已有结构清晰的原始数据&#xff08;如多组实验的统计值、时间序列的均值标准差、多条件下的基因…

作者头像 李华
网站建设 2026/9/29 8:45:52

从荷兰护理培训到国内高校采购:人体干燥设备的跨区域场景验证

在养老护理和适老化改造领域&#xff0c;一个品类的成熟往往需要经历三个阶段的验证&#xff1a;场景需求的真实检验、供应链的分工协作&#xff0c;以及行业标准的规范化。人体干燥设备&#xff08;干身机/浴后吹干器&#xff09;正在经历这一过程。 本文从荷兰护理培训场景切…

作者头像 李华