使用iText填充PDF表单域时的行间距控制方案(含换行场景)及Java代码参考
使用iText填充PDF表单域时的行间距控制方案(含换行场景)及Java代码参考
刚巧之前做项目踩过这个坑,来给你唠唠怎么用iText搞定PDF表单域填充时的行间距问题,尤其是遇到换行的场景~
默认情况下,用iText直接给多行表单域填值,行间距是跟着PDF表单域本身的默认样式走的,要是需求里需要调整行高(比如要更宽松的行间距),或者内容里带了手动换行符(\n),直接填的话可能排版会乱。这时候咱们得自定义文本的绘制逻辑,不能用默认的填充方法。
核心实现思路
- 先获取目标表单域的位置、尺寸信息
- 创建
PdfAppearance对象,这相当于咱们的“画布”,用来手动绘制带指定行间距的文本 - 自定义行高(比如设置为字号的1.5倍,这个可以按需调整)
- 处理文本:不管是手动换行的
\n还是需要自动换行的长文本,拆分成一行一行的内容 - 把拆分后的文本逐行绘制到
PdfAppearance上,每一行的Y坐标按行高递减(因为PDF的坐标系是左下角为原点) - 最后把这个自定义的外观赋值给表单域,替代默认样式
完整Java代码参考
package com.example.demo2.demos.test; import com.lowagie.text.DocumentException; import com.lowagie.text.Font; import com.lowagie.text.pdf.BaseFont; import com.lowagie.text.pdf.PdfAppearance; import com.lowagie.text.pdf.PdfContentByte; import com.lowagie.text.pdf.PdfFormField; import com.lowagie.text.pdf.PdfReader; import com.lowagie.text.pdf.PdfStamper; import com.lowagie.text.pdf.AcroFields; import java.io.*; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; public class PdfFormFiller { // 自定义填充表单域,支持行间距控制 public static void fillFormWithCustomLineSpacing(InputStream inputStream, String outputPdfPath, Map<String, String> fieldValues, float lineSpacingMultiplier) throws IOException, DocumentException { PdfReader reader = new PdfReader(inputStream); PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(outputPdfPath)); AcroFields form = stamper.getAcroFields(); // 加载基础字体(这里用系统自带的宋体,也可以用项目内的自定义字体文件) BaseFont baseFont = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED); Font font = new Font(baseFont, 12); // 设置字号为12 float fontSize = font.getSize(); float lineHeight = fontSize * lineSpacingMultiplier; // 计算行高,比如1.5倍行间距 for (Map.Entry<String, String> entry : fieldValues.entrySet()) { String fieldName = entry.getKey(); String content = entry.getValue(); if (content == null || content.isEmpty()) continue; // 获取表单域的矩形区域 AcroFields.Item item = form.getFieldItem(fieldName); if (item == null) continue; PdfFormField field = item.getWidget(0); float[] rect = field.getRectangle().getRectangle(); float x = rect[0]; float y = rect[1]; float width = rect[2] - rect[0]; float height = rect[3] - rect[1]; // 创建自定义外观 PdfContentByte cb = stamper.getOverContent(1); // 假设表单在第一页,多页场景需循环处理页码 PdfAppearance appearance = cb.createAppearance(width, height); appearance.setFontAndSize(baseFont, fontSize); // 处理文本换行:拆分手动换行和自动换行 List<String> lines = splitTextIntoLines(content, baseFont, fontSize, width); // 从表单域的顶部开始绘制文本(Y坐标从height开始往下减) float currentY = height - fontSize; // 顶部留一点边距 for (String line : lines) { appearance.beginText(); appearance.setTextMatrix(2, currentY); // X坐标留2px边距 appearance.showText(line); appearance.endText(); currentY -= lineHeight; // 行间距控制 // 内容超出表单域高度时可添加截断逻辑,按需调整 if (currentY < 0) break; } // 把自定义外观赋值给表单域 form.setFieldAppearance(fieldName, "N", appearance); } stamper.setFormFlattening(false); // 设为true则表单域变为普通文本,无法再编辑 stamper.close(); reader.close(); } // 辅助方法:拆分文本为适配宽度的行,支持手动换行(\n)和自动换行 private static List<String> splitTextIntoLines(String content, BaseFont baseFont, float fontSize, float maxWidth) { List<String> lines = new ArrayList<>(); String[] manualLines = content.split("\n"); for (String manualLine : manualLines) { if (baseFont.getWidthPoint(manualLine, fontSize) <= maxWidth - 4) { // 减4是预留左右边距 lines.add(manualLine); continue; } // 自动换行:逐字符拆分,直到一行宽度接近最大值 StringBuilder currentLine = new StringBuilder(); float currentWidth = 0; for (char c : manualLine.toCharArray()) { char[] singleChar = {c}; float charWidth = baseFont.getWidthPoint(singleChar, fontSize); if (currentWidth + charWidth > maxWidth - 4) { lines.add(currentLine.toString().trim()); currentLine = new StringBuilder(); currentWidth = 0; } currentLine.append(c); currentWidth += charWidth; } if (currentLine.length() > 0) { lines.add(currentLine.toString().trim()); } } return lines; } // 测试方法 public static void main(String[] args) { try { Map<String, String> fieldValues = new HashMap<>(); fieldValues.put("multiLineField", "这是第一行手动换行文\n这是需要自动换行的超长文本,用来测试当内容超过表单域宽度时,是否能自动换行并保持设置的行间距效果"); fillFormWithCustomLineSpacing( new FileInputStream("input.pdf"), "output.pdf", fieldValues, 1.5f // 行间距倍数,可按需调整为1.2、1.8等 ); } catch (Exception e) { e.printStackTrace(); } } }
几个实用注意点
- 字体适配:如果需要支持中文,一定要加载支持中文的字体,比如代码里的宋体,也可以用项目内的自定义字体文件(示例:
BaseFont.createFont("fonts/simsun.ttf", BaseFont.IDENTITY_H, BaseFont.EMBEDDED)) - 行间距灵活调整:
lineSpacingMultiplier参数可以按需修改,比如1.2倍是紧凑行高,1.8倍是宽松行高 - 多页表单适配:代码默认处理第一页的表单域,多页场景需要遍历每一页的表单域信息
- 表单扁平化:如果需要把填充后的表单域转为不可编辑的普通文本,将
stamper.setFormFlattening(true)即可
内容来源于stack exchange
相关产品推荐
相关产品推荐

