bash
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itextpdf</artifactId>
<version>5.5.13.3</version>
</dependency>
java
/**
* 合同签订测试
* @param request
* @param response
* @return
*/
@RequestMapping(value = "/contract/contractSignTest", method = RequestMethod.POST)
void contractSignTest(HttpServletRequest request, HttpServletResponse response, MultipartFile file,Long contractId);
java
private String downloadAndSignPdf(String pdfUrl, String imageUrl,MultipartFile file) throws Exception {
// 下载PDF和图片
byte[] pdfBytes = downloadFromUrl(pdfUrl);
byte[] imageBytes;
if (file != null && !file.isEmpty()) {
imageBytes = file.getBytes();
}else {
imageBytes = downloadFromUrl(imageUrl);
}
try {
// 使用iText将图片添加到PDF
ByteArrayOutputStream pdfOutput = new ByteArrayOutputStream();
com.itextpdf.text.pdf.PdfReader reader = new com.itextpdf.text.pdf.PdfReader(pdfBytes);
com.itextpdf.text.pdf.PdfStamper stamper = new com.itextpdf.text.pdf.PdfStamper(reader, pdfOutput);
// 获取最后一页
int lastPage = reader.getNumberOfPages();
com.itextpdf.text.Rectangle pageSize = reader.getPageSize(lastPage);
// 创建图片对象并设置位置(右下角)
com.itextpdf.text.Image image = com.itextpdf.text.Image.getInstance(imageBytes);
image.setAbsolutePosition(pageSize.getWidth() * 0.3f, pageSize.getHeight() * 0.3f);
image.scaleToFit(pageSize.getWidth() * 0.2f, pageSize.getHeight() * 0.1f);
// 将图片添加到PDF
stamper.getOverContent(lastPage).addImage(image);
// ========== 添加平铺水印开始 ==========
com.itextpdf.text.pdf.PdfGState gs = new com.itextpdf.text.pdf.PdfGState();
gs.setFillOpacity(0.25f);//透明度
for (int i = 1; i <= reader.getNumberOfPages(); i++) {
PdfContentByte canvas = stamper.getOverContent(i);
canvas.setGState(gs);
com.itextpdf.text.Rectangle page_size = reader.getPageSize(i);
// 动态计算水印参数
String watermarkText = "测试pdf水印";
int fontSize = 60; // 基础字号
int padding = 40; // 边距
// 先用临时Graphics2D计算文字实际宽度
java.awt.image.BufferedImage tempImg = new java.awt.image.BufferedImage(1, 1, java.awt.image.BufferedImage.TYPE_INT_ARGB);
java.awt.Graphics2D tempG2d = tempImg.createGraphics();
tempG2d.setFont(new java.awt.Font("宋体", java.awt.Font.BOLD, fontSize));
java.awt.FontMetrics fm = tempG2d.getFontMetrics();
int textWidth = fm.stringWidth(watermarkText);
int textHeight = fm.getHeight();
tempG2d.dispose();
// 根据文字尺寸计算图片大小(考虑旋转后需要的空间)
double angleRad = Math.toRadians(30);
double cos = Math.abs(Math.cos(angleRad));
double sin = Math.abs(Math.sin(angleRad));
int imgWidth = (int)(textWidth * cos + textHeight * sin) + padding * 2;
int imgHeight = (int)(textWidth * sin + textHeight * cos) + padding * 2;
// 创建实际水印图片
java.awt.image.BufferedImage watermarkImg = new java.awt.image.BufferedImage(
imgWidth, imgHeight, java.awt.image.BufferedImage.TYPE_INT_ARGB);
java.awt.Graphics2D g2d = watermarkImg.createGraphics();
g2d.setRenderingHint(java.awt.RenderingHints.KEY_ANTIALIASING,
java.awt.RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setRenderingHint(java.awt.RenderingHints.KEY_TEXT_ANTIALIASING,
java.awt.RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
g2d.setFont(new java.awt.Font("宋体", java.awt.Font.BOLD, fontSize));
g2d.setColor(new java.awt.Color(180, 180, 180, 150));
// 旋转并绘制
g2d.rotate(Math.toRadians(30), imgWidth / 2.0, imgHeight / 2.0);
g2d.drawString(watermarkText, (imgWidth - textWidth) / 2, (imgHeight + textHeight) / 2 - fm.getDescent());
g2d.dispose();
com.itextpdf.text.Image watermarkImage = com.itextpdf.text.Image.getInstance(
watermarkImg, null);
// 计算平铺数量(根据页面大小)
int cols = Math.max(2, (int)(page_size.getWidth() / imgWidth) - 1);
int rows = Math.max(2, (int)(page_size.getHeight() / imgHeight) - 1);
for (int row = 0; row < rows; row++) {
for (int col = 0; col < cols; col++) {
float x = page_size.getWidth() * (col + 1) / (cols + 1) - imgWidth / 2;
float y = page_size.getHeight() * (row + 1) / (rows + 1) - imgHeight / 2;
watermarkImage.setAbsolutePosition(x, y);
canvas.addImage(watermarkImage);
}
}
}
// ========== 添加平铺水印结束 ==========
stamper.close();
reader.close();
byte[] signedPdfBytes = pdfOutput.toByteArray();
Result<UploadResult> upload = storageService.upload(
"contract_signed_" + System.currentTimeMillis() + ".pdf",
signedPdfBytes,
null,
null
);//上传到文件服务器
String path = upload.getData().getPath();
System.out.println(domainUrl+path);
return path;
} catch (Exception e) {
log.warn("图片处理失败,返回原PDF: " + e.getMessage());
throw new Exception(e.getMessage());
}
}
private byte[] downloadFromUrl(String urlString) throws Exception {
URL url = new URL(urlString);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET");
connection.setConnectTimeout(10000);
connection.setReadTimeout(10000);
try (InputStream inputStream = connection.getInputStream();
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream()) {
byte[] buffer = new byte[4096];
int bytesRead;
while ((bytesRead = inputStream.read(buffer)) != -1) {
byteArrayOutputStream.write(buffer, 0, bytesRead);
}
connection.disconnect();
return byteArrayOutputStream.toByteArray();
}
}

前端需自行上传签字的图片文件