factuges-document-signing-s.../src/signing_service/infrastructure/pdf/pyhanko_pdf_signer.py
2026-01-30 14:12:05 +01:00

79 lines
2.4 KiB
Python
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import base64
import io
from datetime import datetime, timezone
from pyhanko import stamp
from pyhanko.sign import fields, signers
from pyhanko.sign import signers
from pyhanko.pdf_utils import text
from pyhanko.pdf_utils.incremental_writer import IncrementalPdfFileWriter
from signing_service.domain.ports.pdf_signer import PDFSignerPort
class PyHankoPDFSigner(PDFSignerPort):
async def sign(self, pdf_bytes: bytes, certificate: str, password: str,) -> bytes:
"""
pdf_bytes: original PDF byte
certificate: base64-encoded PKCS#12 (PFX)
"""
# 1⃣ Decodificar certificado
pfx_bytes = base64.b64decode(certificate)
# 3⃣ Preparar PDF en modo incremental
writer = IncrementalPdfFileWriter(io.BytesIO(pdf_bytes))
# 2⃣ Cargar clave y certificado
signer = signers.SimpleSigner.load_pkcs12_data(
pkcs12_bytes=pfx_bytes,
passphrase=password.encode(),
other_certs=[],
)
# 3⃣ Extract certificate info (from signer)
cert = signer.signing_cert
subject = cert.subject.native.get('common_name', 'Desconocido')
# 5⃣ Define signature field position
fields.append_signature_field(
writer,
sig_field_spec=fields.SigFieldSpec(
sig_field_name="Signature1",
on_page=-1,
box=(380, 100, 560, 160), # x, y, x+width, y+height
)
)
# 4⃣ Signature metadata (panel de firmas)
meta = signers.PdfSignatureMetadata(
field_name="Signature1",
name=subject,
)
pdf_signer = signers.PdfSigner(
meta, signer=signer, stamp_style=stamp.TextStampStyle(
stamp_text='Firmado digitalmente por: %(signer)s\nFecha: %(ts)s',
text_box_style=text.TextBoxStyle(
border_width=0,
font_size=12
#font=opentype.GlyphAccumulatorFactory('path to font police.ttf'),
),
#background=images.PdfImage('path to img.jpg')
)
)
# 4⃣ Firmar
signed_pdf: io.BytesIO = await pdf_signer.async_sign_pdf(
writer,
)
# 5⃣ Obtener PDF firmado
return signed_pdf.getbuffer().tobytes()