From 083c0fd5402132453961b611fb42a1455f400c43 Mon Sep 17 00:00:00 2001 From: hailin Date: Sat, 3 Jan 2026 23:28:18 -0800 Subject: [PATCH] fix(planting): draw signature directly on page instead of using form field MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The PDF signature field is only 92x51 points, which causes signatures to appear too small or invisible. Changed to use drawImage() directly on the page at the field's position with a larger size (150x80 max). 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- .../pdf/pdf-generator.service.ts | 91 +++++++++---------- 1 file changed, 43 insertions(+), 48 deletions(-) diff --git a/backend/services/planting-service/src/infrastructure/pdf/pdf-generator.service.ts b/backend/services/planting-service/src/infrastructure/pdf/pdf-generator.service.ts index 40ec832a..e1b7f952 100644 --- a/backend/services/planting-service/src/infrastructure/pdf/pdf-generator.service.ts +++ b/backend/services/planting-service/src/infrastructure/pdf/pdf-generator.service.ts @@ -389,64 +389,59 @@ export class PdfGeneratorService { this.logger.log('Using form fields mode'); await this.fillFormFields(pdfDoc, data, customFont); - // 5. 如果有签名,嵌入签名图片到 signature 按钮字段 + // 5. 如果有签名,嵌入签名图片 + // 直接使用坐标方式在页面上绘制签名,不受表单字段尺寸限制 if (signature) { const signatureImage = await pdfDoc.embedPng(signature.signatureImagePng); + // 获取签名字段位置作为参考 + let signatureX = 380; // 默认 x 坐标 + let signatureY = 100; // 默认 y 坐标 + try { const form = pdfDoc.getForm(); const signatureButton = form.getButton(FORM_FIELDS.SIGNATURE); - - // 获取按钮的 widget 和尺寸 const widgets = signatureButton.acroField.getWidgets(); if (widgets.length > 0) { - const widget = widgets[0]; - const rect = widget.getRectangle(); - const { width: fieldWidth, height: fieldHeight } = rect; - - // 计算图片缩放尺寸(保持宽高比,适应字段大小) - const imgDims = signatureImage.scale(1); - const scale = Math.min(fieldWidth / imgDims.width, fieldHeight / imgDims.height); - const scaledWidth = imgDims.width * scale; - const scaledHeight = imgDims.height * scale; - - // 计算居中位置 - const x = (fieldWidth - scaledWidth) / 2; - const y = (fieldHeight - scaledHeight) / 2; - - // 创建外观流内容 - const appearanceStream = `q ${scaledWidth} 0 0 ${scaledHeight} ${x} ${y} cm /Img Do Q`; - - // 创建 XObject Form 作为外观 - const context = pdfDoc.context; - const imageXObjectName = 'Img'; - - const Resources = context.obj({ - XObject: { [imageXObjectName]: signatureImage.ref }, - }); - - 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 { - throw new Error('No widgets found for signature button'); + const rect = widgets[0].getRectangle(); + // 使用字段左下角作为签名位置参考 + signatureX = rect.x; + signatureY = rect.y; + this.logger.log(`Signature field position: x=${rect.x}, y=${rect.y}, field size: ${rect.width}x${rect.height}`); } - } catch (error) { - // 如果没有签名按钮字段,回退到坐标方式 - this.logger.log(`Signature button field error: ${error.message}, using coordinates`); - this.embedSignatureByCoordinates(pdfDoc, signatureImage); + } catch { + this.logger.log('No signature button field found, using default coordinates'); } + + // 直接在页面上绘制签名图片 + const pages = pdfDoc.getPages(); + const lastPage = pages[pages.length - 1]; + + // 计算签名图片的尺寸(保持宽高比,目标尺寸更大) + const maxWidth = 150; + const maxHeight = 80; + const { width, height } = signatureImage.scale(1); + let scaledWidth = width; + let scaledHeight = height; + + if (width > maxWidth) { + scaledWidth = maxWidth; + scaledHeight = (height * maxWidth) / width; + } + if (scaledHeight > maxHeight) { + scaledHeight = maxHeight; + scaledWidth = (width * maxHeight) / height; + } + + // 在签名位置绘制图片 + lastPage.drawImage(signatureImage, { + x: signatureX, + y: signatureY, + width: scaledWidth, + height: scaledHeight, + }); + + this.logger.log(`Signature drawn at (${signatureX}, ${signatureY}), size: ${scaledWidth}x${scaledHeight}`); } // 6. 最后统一扁平化所有表单字段