factuges-document-signing-s.../src/signing_service/infrastructure/pdf/pyhanko_pdf_signer.py

79 lines
2.4 KiB
Python
Raw Normal View History

2026-01-22 10:37:35 +00:00
import base64
import io
2026-01-30 13:12:05 +00:00
from datetime import datetime, timezone
2026-01-22 10:37:35 +00:00
2026-01-30 13:12:05 +00:00
from pyhanko import stamp
from pyhanko.sign import fields, signers
2026-01-22 10:37:35 +00:00
from pyhanko.sign import signers
2026-01-30 13:12:05 +00:00
from pyhanko.pdf_utils import text
2026-01-29 17:35:43 +00:00
from pyhanko.pdf_utils.incremental_writer import IncrementalPdfFileWriter
2026-01-22 10:37:35 +00:00
2026-01-30 13:12:05 +00:00
2026-01-22 10:37:35 +00:00
from signing_service.domain.ports.pdf_signer import PDFSignerPort
class PyHankoPDFSigner(PDFSignerPort):
2026-01-29 17:35:43 +00:00
async def sign(self, pdf_bytes: bytes, certificate: str, password: str,) -> bytes:
2026-01-22 10:37:35 +00:00
"""
2026-01-29 17:35:43 +00:00
pdf_bytes: original PDF byte
2026-01-22 10:37:35 +00:00
certificate: base64-encoded PKCS#12 (PFX)
"""
# 1⃣ Decodificar certificado
pfx_bytes = base64.b64decode(certificate)
2026-01-30 13:12:05 +00:00
# 3⃣ Preparar PDF en modo incremental
writer = IncrementalPdfFileWriter(io.BytesIO(pdf_bytes))
2026-01-22 10:37:35 +00:00
# 2⃣ Cargar clave y certificado
2026-01-29 17:35:43 +00:00
signer = signers.SimpleSigner.load_pkcs12_data(
pkcs12_bytes=pfx_bytes,
passphrase=password.encode(),
other_certs=[],
2026-01-22 10:37:35 +00:00
)
2026-01-30 13:12:05 +00:00
# 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,
)
2026-01-22 10:37:35 +00:00
2026-01-30 13:12:05 +00:00
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')
)
)
2026-01-22 10:37:35 +00:00
# 4⃣ Firmar
2026-01-30 13:12:05 +00:00
signed_pdf: io.BytesIO = await pdf_signer.async_sign_pdf(
writer,
2026-01-22 10:37:35 +00:00
)
2026-01-29 17:35:43 +00:00
# 5⃣ Obtener PDF firmado
return signed_pdf.getbuffer().tobytes()