fix(pdf-generator): 使用自定义外观流嵌入签名图片
- 恢复使用 widget.setNormalAppearance() 方式 - 创建 XObject Form 作为外观流 - 签名图片按字段尺寸等比例缩放并居中 - 不依赖页面索引,直接设置到widget上 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
91d3e65289
commit
e9b2917561
|
|
@ -383,41 +383,48 @@ export class PdfGeneratorService {
|
||||||
const form = pdfDoc.getForm();
|
const form = pdfDoc.getForm();
|
||||||
const signatureButton = form.getButton(FORM_FIELDS.SIGNATURE);
|
const signatureButton = form.getButton(FORM_FIELDS.SIGNATURE);
|
||||||
|
|
||||||
// 获取按钮的 widget 和位置
|
// 获取按钮的 widget 和尺寸
|
||||||
const widgets = signatureButton.acroField.getWidgets();
|
const widgets = signatureButton.acroField.getWidgets();
|
||||||
if (widgets.length > 0) {
|
if (widgets.length > 0) {
|
||||||
const widget = widgets[0];
|
const widget = widgets[0];
|
||||||
const rect = widget.getRectangle();
|
const rect = widget.getRectangle();
|
||||||
|
const { width: fieldWidth, height: fieldHeight } = rect;
|
||||||
|
|
||||||
// 获取签名图片原始尺寸
|
// 计算图片缩放尺寸(保持宽高比,适应字段大小)
|
||||||
const imgDims = signatureImage.scale(1);
|
const imgDims = signatureImage.scale(1);
|
||||||
|
const scale = Math.min(fieldWidth / imgDims.width, fieldHeight / imgDims.height);
|
||||||
// 按字段尺寸等比例缩放签名图片
|
|
||||||
// 计算宽度和高度的缩放比例,取较小值以确保签名完全在字段内
|
|
||||||
const scaleX = rect.width / imgDims.width;
|
|
||||||
const scaleY = rect.height / imgDims.height;
|
|
||||||
const scale = Math.min(scaleX, scaleY);
|
|
||||||
|
|
||||||
const scaledWidth = imgDims.width * scale;
|
const scaledWidth = imgDims.width * scale;
|
||||||
const scaledHeight = imgDims.height * scale;
|
const scaledHeight = imgDims.height * scale;
|
||||||
|
|
||||||
// 在字段内居中放置签名
|
// 计算居中位置
|
||||||
const offsetX = (rect.width - scaledWidth) / 2;
|
const x = (fieldWidth - scaledWidth) / 2;
|
||||||
const offsetY = (rect.height - scaledHeight) / 2;
|
const y = (fieldHeight - scaledHeight) / 2;
|
||||||
|
|
||||||
// 获取按钮所在的页面
|
// 创建外观流内容
|
||||||
const pages = pdfDoc.getPages();
|
const appearanceStream = `q ${scaledWidth} 0 0 ${scaledHeight} ${x} ${y} cm /Img Do Q`;
|
||||||
const page = pages[5]; // 第6页(签名区域)
|
|
||||||
|
|
||||||
// 在按钮位置绘制缩放后的签名图片(居中)
|
// 创建 XObject Form 作为外观
|
||||||
page.drawImage(signatureImage, {
|
const context = pdfDoc.context;
|
||||||
x: rect.x + offsetX,
|
const imageXObjectName = 'Img';
|
||||||
y: rect.y + offsetY,
|
|
||||||
width: scaledWidth,
|
const Resources = context.obj({
|
||||||
height: scaledHeight,
|
XObject: { [imageXObjectName]: signatureImage.ref },
|
||||||
});
|
});
|
||||||
|
|
||||||
this.logger.log(`Signature embedded at (${rect.x + offsetX}, ${rect.y + offsetY}) with size ${scaledWidth}x${scaledHeight}, field size: ${rect.width}x${rect.height}`);
|
const appearanceStreamRef = context.register(
|
||||||
|
context.stream(appearanceStream, {
|
||||||
|
Type: 'XObject',
|
||||||
|
Subtype: 'Form',
|
||||||
|
FormType: 1,
|
||||||
|
BBox: [0, 0, fieldWidth, fieldHeight],
|
||||||
|
Resources,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
// 设置外观到 widget
|
||||||
|
widget.setNormalAppearance(appearanceStreamRef);
|
||||||
|
|
||||||
|
this.logger.log(`Signature embedded using custom appearance stream, field size: ${fieldWidth}x${fieldHeight}`);
|
||||||
} else {
|
} else {
|
||||||
throw new Error('No widgets found for signature button');
|
throw new Error('No widgets found for signature button');
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue