💡 Full working example available on GitHub:
python-linux-container-pdf-signing
Introdução
O script funciona localmente. Você o containeriza em python:3.11-slim e ele falha ao executar import groupdocs.signature. Você corrige isso, e ele falha novamente na primeira assinatura. Nenhum dos erros menciona o que realmente está faltando.
A assinatura em contêiner com Python é um fluxo de trabalho do GroupDocs.Signature que requer duas camadas de provisionamento em vez de uma: as bibliotecas de tempo de execução do .NET nas quais a ligação foi construída e as fontes que toda assinatura de texto precisa para ser renderizada. Este tutorial constrói ambas, depois o script que resolve uma família de fontes em tempo de execução em vez de codificar uma fixa, de modo que o mesmo código funcione no contêiner e na máquina onde foi escrito.
Why Both Layers Matter
GroupDocs.Signature for Python é uma ligação .NET, portanto libicu e uma biblioteca compatível com OpenSSL 1.1 precisam existir antes que qualquer importação seja bem‑sucedida. Essa é a camada um, e está bem documentada em Running in Docker.
A razão pela qual as duas camadas são confundidas é que ambas falham em momentos próximos à importação e nenhum erro nomeia sua causa. Um libssl1.1 ausente gera um erro de carregamento sobre um objeto compartilhado; uma fonte ausente gera um erro de assinatura encapsulado em uma exceção proxy. Nenhum diz “sua imagem base é muito pequena”, que é o que ambas realmente significam.
A camada dois são as fontes, e é a que surpreende as pessoas. python:3.11-slim não contém nenhum arquivo de fonte. GroupDocs.Signature não substitui uma família ausente – nomear uma que não está instalada gera exceção, e nada é escrito – e limpar a fonte não é uma solução alternativa, porque a biblioteca então solicita sua própria fonte padrão e falha de forma idêntica. Em uma imagem sem fontes, uma assinatura de texto é simplesmente impossível.
Pré‑requisitos
Python 3.11 (a roda abaixo tem limite inferior em CPython 3.14) e groupdocs-signature-net==26.1. Docker se você quiser ver ambas as falhas de propósito, o que leva cerca de dez minutos.
Instalação
pip install groupdocs-signature-net==26.1
Etapa 1 – Construir a camada .NET
libssl1.1 não está no Bookworm, então ele vem de um snapshot Debian fixado:
ENV SNAPSHOT_DATE=20220328T000000Z
RUN echo "deb [trusted=yes] http://snapshot.debian.org/archive/debian/${SNAPSHOT_DATE} bullseye main" \
> /etc/apt/sources.list.d/debian-archive.list \
&& apt-get -o Acquire::Check-Valid-Until=false update \
&& apt-get install -y --no-install-recommends \
libicu67 \
libssl1.1 \
&& apt-get clean && rm -rf /var/lib/apt/lists/*
Pontos principais:
- Esta camada apenas faz a importação funcionar; não diz nada sobre fontes.
- Fixar a data do snapshot mantém a construção reproduzível quando o arquivo muda.
Etapa 2 – Construir a camada de fontes
Quatro pacotes, mantidos como camada própria para que possam ser comentados e reproduzir a falha:
RUN apt-get update && apt-get install -y --no-install-recommends \
fontconfig \
fonts-dejavu-core \
fonts-liberation \
fonts-noto-cjk \
&& fc-cache -f \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
fontconfig é o resolvedor e fornece fc-list. fonts-dejavu-core cobre o mínimo latino, grego e cirílico. fonts-liberation cobre documentos que referenciam Arial ou Times New Roman pelo nome. fonts-noto-cjk cobre chinês, japonês e coreano.
Etapa 3 – Perguntar à biblioteca qual família ela pode usar
Escanear /usr/share/fonts por nome de arquivo parece equivalente e não é: fonts-noto-cjk instala NotoSansCJK-Regular.ttc, cujo nome de família é Noto Sans CJK JP. A resposta portátil é uma sondagem – uma assinatura real em um arquivo temporário – com a falha convertida em um valor:
with signature.Signature(source_path) as sign:
options = TextSignOptions()
options.text = "probe"
options.left = 10
options.top = 10
options.width = 60
options.height = 20
font = SignatureFont()
font.family_name = family_name
font.size = 10.0
options.font = font
sign.sign(scratch, [options])
return None
Observe font.size = 10.0. A ligação mapeia o tamanho para um float .NET e rejeita um int com a mensagem numeric argument expected, got 'int'. Como isso acontece dentro da sondagem, toda família candidata falha e a saída parece exatamente como uma imagem sem fontes. Eu adicionei três pacotes de fontes a uma imagem que já os continha antes de perceber o literal.
A resolução então se torna um loop:
for candidate in candidates:
if try_family(source_path, candidate) is None:
return candidate
return None
Etapa 4 – Assinar o que foi resolvido, verificar o que foi assinado
A família latina é obrigatória, a CJK opcional:
with signature.Signature(source_path) as sign:
options = [build_text_options(LATIN_TEXT, latin_family, 50)]
if cjk_family:
options.append(build_text_options(CJK_TEXT, cjk_family, 120))
result = sign.sign(output_path, options)
return len(result.succeeded)
Depois verifique, porque CJK renderizado como caixas vazias não gera exceção:
options = TextVerifyOptions()
options.text = expected_text
options.match_type = gsd.TextMatchType.CONTAINS
options.all_pages = True
result = sign.verify(options)
CONTAINS é deliberado: em modo de avaliação a biblioteca adiciona texto de teste à página, e uma correspondência exata reportaria um documento perfeitamente bom como falho.
E os documentos que dizem que Python tem suporte Linux limitado?
A página Running in Docker lista pacotes Python prontos para Linux e deixa o Signature de fora. No groupdocs-signature-net==26.1 este exemplo assinou e verificou dentro de python:3.11-slim, incluindo CJK, com ambas as camadas instaladas. Considere a lista desatualizada em vez de um bloqueio, e confirme com sua própria versão antes de comprometer um deployment.
Aplicações no Mundo Real
Um serviço de faturamento que carimba uma linha de aprovação em PDFs gerados precisa exatamente disso: a camada .NET, uma fonte latina e uma verificação de resolução na inicialização. A verificação é o que transforma um deployment ruim em um contêiner que se recusa a iniciar, ao invés de uma fila de faturas que falham silenciosamente uma a uma. Um portal de documentos que aceita nomes de clientes em qualquer escrita precisa também do pacote CJK, além da etapa de verificação, porque é a única coisa que impede que uma caixa renderizada seja aceita como nome assinado.
Onde a verificação de resolução deve ficar
Coloque-a onde quer que seja executada uma vez por processo: uma chamada ao nível de módulo, um handler de lifespan do FastAPI, um AppConfig.ready do Django, ou as primeiras linhas do main de um worker. Dois valores são retornados, a família latina e a família CJK, e ambos devem aparecer no log de inicialização ao lado da contagem de fontes.
Essa colocação faz mais do que economizar tempo de sondagem. Ela move a falha do tratamento de requisição – problema de um cliente e stack trace que ninguém lê – para a inicialização, onde se torna um deployment que não subiu e alguém já está observando. Um contêiner que sai com “no usable font family, install fonts-dejavu-core” não precisa de depuração alguma.
Solução de Problemas – Problemas Comuns
import groupdocs.signature falha
A camada .NET está ausente ou o repositório de snapshot estava inacessível durante a build. Esta é a camada um, e não tem nada a ver com fontes. Verifique o log da build para a etapa apt antes de tocar em qualquer código de assinatura, pois uma falha ao buscar o snapshot não impede a imagem de ser construída.
Toda fonte candidata falha, mas fc-list mostra fontes
Verifique se font.size está como int antes de adicionar mais pacotes.
A assinatura aparece, mas o texto CJK são caixas
fonts-noto-cjk está ausente. A assinatura foi escrita com uma família que não tem glifos para esses pontos de código, por isso a etapa de verificação existe: ela falha exatamente nesse caso, onde a assinatura relatou sucesso.
O que as duas imagens realmente imprimem
Execute ambas e leia as quatro primeiras linhas. A imagem sem fontes relata font files on disk: 0, ambas as linhas de resolução como (none), o erro deliberado de fonte ausente e sai com código 3 mostrando a correção mínima. A imagem provisionada relata contagem de fontes diferente de zero, DejaVu Sans para Latin e Noto Sans CJK JP para CJK, duas assinaturas aplicadas e ambos os textos verificados.
Esse par de saídas é o artefato que vale a pena guardar. Cole‑o nas notas de deployment e a próxima pessoa que mudar a imagem base terá uma referência do que é um contêiner saudável, sem precisar entender fontconfig.
Conclusão
Duas camadas e uma sondagem. Instale as dependências .NET, instale ao menos fontconfig e DejaVu, resolva a família perguntando em vez de assumir, e verifique a saída antes de declarar o trabalho concluído. Nada disso é muito código, e tudo isso é o tipo de coisa que parece óbvia em retrospectiva e invisível em um traceback. O repositório de exemplo entrega ambos Dockerfiles, de modo que a diferença entre uma imagem funcional e uma quebrada está a um build de distância.