基于PDFBox实现签名文本与背景图的矩形适配需求问询
PDFBox 自定义数字签名外观的实现方案
PDFBox 并没有提供直接满足你所有需求的一站式标准API,但可以通过组合它的核心工具类来实现这些功能,无需完全从零编写底层计算逻辑:
1. 签名文本适配矩形+自动换行+字号自适应
利用 PDFont 的文本宽度计算能力,搭配动态调整逻辑即可实现:
- 先设定初始字号,通过
PDFont.getStringWidth()计算文本宽度,逐步缩小字号直到文本总宽度(拆分后每行宽度)适配矩形可用宽度; - 文本换行可通过循环累加字符计算宽度,当超过矩形宽度时拆分字符串;
- 用
PDPageContentStream的文本绘制方法,将拆分后的文本逐行绘制在矩形内,可实现居中或对齐效果。
核心逻辑示例:
// 获取签名字段及对应矩形 PDField signatureField = ...; PDRectangle fieldRect = signatureField.getWidget().getRectangle(); float padding = 10; // 留边距避免文本贴边 float maxTextWidth = fieldRect.getWidth() - padding * 2; float maxTextHeight = fieldRect.getHeight() - padding * 2; PDFont font = PDType1Font.HELVETICA; float fontSize = 20; // 初始字号 String signatureText = "你的签名信息,比如姓名+日期"; List<String> fittedLines = new ArrayList<>(); // 动态调整字号,直到文本总行高适配矩形 while (true) { fittedLines = splitTextToFit(signatureText, font, fontSize, maxTextWidth); float lineHeight = fontSize * 1.2f; // 行间距 float totalTextHeight = fittedLines.size() * lineHeight; if (totalTextHeight <= maxTextHeight || fontSize <= 8) { // 设置最小字号下限 break; } fontSize -= 1; } // 绘制文本(居中对齐) PDPageContentStream contentStream = new PDPageContentStream(doc, page, AppendMode.APPEND, true, true); contentStream.beginText(); contentStream.setFont(font, fontSize); // 计算文本起始坐标(水平+垂直居中) float longestLineWidth = getLongestLineWidth(fittedLines, font, fontSize); float startX = fieldRect.getLowerLeftX() + padding + (maxTextWidth - longestLineWidth) / 2; float startY = fieldRect.getUpperRightY() - padding - fontSize; contentStream.newLineAtOffset(startX, startY); for (String line : fittedLines) { contentStream.showText(line); contentStream.newLineAtOffset(0, -fontSize * 1.2f); } contentStream.endText(); contentStream.close(); // 辅助方法:拆分文本为适配宽度的行 private List<String> splitTextToFit(String text, PDFont font, float fontSize, float maxWidth) throws IOException { List<String> lines = new ArrayList<>(); StringBuilder currentLine = new StringBuilder(); String[] words = text.split(" "); for (String word : words) { float testWidth = font.getStringWidth(currentLine + " " + word) * fontSize / 1000; if (testWidth <= maxWidth || currentLine.isEmpty()) { if (!currentLine.isEmpty()) currentLine.append(" "); currentLine.append(word); } else { lines.add(currentLine.toString()); currentLine = new StringBuilder(word); } } lines.add(currentLine.toString()); return lines; } // 辅助方法:获取最长行的宽度 private float getLongestLineWidth(List<String> lines, PDFont font, float fontSize) throws IOException { float maxWidth = 0; for (String line : lines) { float width = font.getStringWidth(line) * fontSize / 1000; if (width > maxWidth) maxWidth = width; } return maxWidth; }
2. 背景图按原比例适配矩形并居中
通过PDImageXObject处理图片,结合比例计算实现自适应居中:
- 计算图片原宽高比与矩形宽高比,取较小的缩放比例避免拉伸;
- 根据缩放比例计算图片新尺寸,再计算居中绘制的坐标;
- 用
drawImage()方法绘制背景图(注意要先绘制背景再绘制文本,避免被覆盖)。
核心逻辑示例:
// 加载背景图 PDImageXObject backgroundImg = PDImageXObject.createFromFile("signature-bg.png", doc); float imgOriginalWidth = backgroundImg.getWidth(); float imgOriginalHeight = backgroundImg.getHeight(); float rectWidth = fieldRect.getWidth(); float rectHeight = fieldRect.getHeight(); // 计算缩放比例(保持原比例) float scaleX = rectWidth / imgOriginalWidth; float scaleY = rectHeight / imgOriginalHeight; float scale = Math.min(scaleX, scaleY); float scaledImgWidth = imgOriginalWidth * scale; float scaledImgHeight = imgOriginalHeight * scale; // 计算居中绘制坐标 float drawX = fieldRect.getLowerLeftX() + (rectWidth - scaledImgWidth) / 2; float drawY = fieldRect.getLowerLeftY() + (rectHeight - scaledImgHeight) / 2; // 绘制背景图(要在文本绘制前执行) contentStream.drawImage(backgroundImg, drawX, drawY, scaledImgWidth, scaledImgHeight);
总结
这些实现都是基于PDFBox官方提供的核心API完成,虽然没有现成的封装方法,但复用PDFont、PDPageContentStream、PDImageXObject等类的能力,比完全自定义计算更稳定,也符合PDFBox的设计逻辑。
内容的提问来源于stack exchange,提问作者Himanshu Jindal
相关产品推荐
相关产品推荐

