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. 最后统一扁平化所有表单字段