From 1a76b4aa22f8a7302fe06d9b1395f1d61f827750 Mon Sep 17 00:00:00 2001 From: david Date: Fri, 30 Jan 2026 14:12:05 +0100 Subject: [PATCH] . --- pyproject.toml | 4 +- scripts/build-docker.sh | 2 +- setup.cfg | 2 +- .../infrastructure/pdf/pyhanko_pdf_signer.py | 56 +++++++++++++++---- 4 files changed, 50 insertions(+), 14 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 5486a90..9d89934 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "factuges-document-signing-service" -version = "0.1.1" +version = "0.1.3" description = "FastAPI service for signing PDF documents using external secret managers" requires-python = ">=3.11" @@ -14,7 +14,7 @@ dependencies = [ "fastapi>=0.110", "uvicorn[standard]>=0.27", - "pyhanko>=0.25", + "pyhanko>=0.32", "cryptography>=42", # Infisical SDK for secret management diff --git a/scripts/build-docker.sh b/scripts/build-docker.sh index de7c75a..4fe2976 100755 --- a/scripts/build-docker.sh +++ b/scripts/build-docker.sh @@ -66,7 +66,7 @@ fi # --- Detectar nombre y versión de la API --- IMAGE_NAME=$(node -p "require('${PROJECT_DIR}/setup.cfg').name" 2>/dev/null || echo "factuges-document-signing-service") -IMAGE_VERSION=$(node -p "require('${PROJECT_DIR}/setup.cfg').version" 2>/dev/null || echo "0.1.2") +IMAGE_VERSION=$(node -p "require('${PROJECT_DIR}/setup.cfg').version" 2>/dev/null || echo "0.1.3") PORT="8000" # valor por defecto diff --git a/setup.cfg b/setup.cfg index 48de64a..351d655 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,7 +1,7 @@ [metadata] name = "factuges-document-signing-service" -version = "0.1.2" +version = "0.1.3" description = "FastAPI service for signing PDF documents using external secret managers" author = Rodax Software author_email = info@rodax-software.com diff --git a/src/signing_service/infrastructure/pdf/pyhanko_pdf_signer.py b/src/signing_service/infrastructure/pdf/pyhanko_pdf_signer.py index a30b07b..596db7d 100644 --- a/src/signing_service/infrastructure/pdf/pyhanko_pdf_signer.py +++ b/src/signing_service/infrastructure/pdf/pyhanko_pdf_signer.py @@ -1,11 +1,15 @@ import base64 -from csv import writer import io -from pydoc import doc +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 @@ -19,6 +23,9 @@ class PyHankoPDFSigner(PDFSignerPort): # 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, @@ -26,16 +33,45 @@ class PyHankoPDFSigner(PDFSignerPort): other_certs=[], ) - # 3️⃣ Preparar PDF - input_pdf = IncrementalPdfFileWriter(io.BytesIO(pdf_bytes)) + # 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 signers.async_sign_pdf( - input_pdf, - signers.PdfSignatureMetadata( - field_name="Signature1" - ), - signer=signer, + signed_pdf: io.BytesIO = await pdf_signer.async_sign_pdf( + writer, ) # 5️⃣ Obtener PDF firmado