Skip to main content

receitapdf

Biblioteca Rust, CLI e extensão Python que recebem uma prescrição estruturada e devolvem os bytes do PDF. Os oito modelos usam os layouts ajustados e aprovados em examples/: A, B, B2, A/B veterinários, retinoides, talidomida e Receita de Controle Especial (RCE).

Também gera receita simples, pedidos de exame e documentos (atestado, declaração, termo, encaminhamento) em layouts nativos; veja Documentos nos layouts nativos.

Layouts usados na geração

A CLI e as APIs Rust/Python usam o mesmo renderizador Rust. Os cabeçalhos aprovados estão incorporados em assets/modelos/; os boxes, rótulos, dados, rodapé e QR são desenhados na geração. Não é necessário instalar PyMuPDF, executar scripts Python ou acessar a rede para gerar uma receita.

Os PDFs originais continuam como referência. Os layouts ajustados são derivados desses arquivos; não são os arquivos oficiais intactos. A consulta feita durante os ajustes está em endereço e modelo oficial.

Padrões compartilhados:

  • Página de 595,5 × 842,25 pt; espaçamento de 10 pt entre os boxes.
  • Caixa de numeração com as mesmas dimensões nos oito modelos.
  • Rótulos de 7,5 pt acima dos valores de 10 pt; entrelinha de 14 pt.
  • Nomes com até duas linhas; endereços de emitente/paciente com até três.
  • Emitente e paciente com a mesma altura, calculada pelos dados.
  • Prescrição com quebra automática e limite definido pelo espaço disponível.
  • Rodapé alinhado pela base, com entrelinha de 20 pt. Plataforma e assinatura aceitam até duas linhas cada, sem reservar uma linha vazia.
  • Últimas três instruções em itálico, URLs em negrito itálico ao lado dos rótulos.
  • Token e código acima do QR, alinhados ao seu lado esquerdo; base visível do QR alinhada à última linha do rodapé.
  • Na talidomida, nome/CPF/telefone do responsável ficam lado a lado, com endereço abaixo e espaço para duas linhas. Dose e tempo de tratamento ficam lado a lado.

A prescrição utiliza o espaço restante acima do rodapé: textos adicionais no rodapé reduzem sua altura quando necessário. Conteúdo que excede a capacidade retorna erro; não é truncado nem sobreposto.

CLI

Rust está declarado em mise.toml:

mise install
mise exec rust -- cargo run --release -- examples/notificacao-a.json saida.pdf

O executável recebe <entrada.json> <saida.pdf> [--preview <pasta>]. JSON com modelo gera uma receita Anvisa; JSON com layout gera um documento nativo. Com --preview, grava também a prévia de cada página em <pasta>/pagina-N.webp. Para entrada padrão:

cat examples/notificacao-a.json | mise exec rust -- cargo run --release -- - saida.pdf

API Rust

use receitapdf::{gerar_pdf, Receita};

fn gerar(receita: &Receita) -> Result<Vec<u8>, receitapdf::Erro> {
    gerar_pdf(receita)
}

gerar_pdf valida os campos obrigatórios, número, telefones e capacidade do layout. Retorna os bytes do PDF ou Erro. A assinatura criptográfica, a obtenção do número SNCR e as decisões clínicas são responsabilidades da integração chamadora.

API Python

CPython 3.13 ou superior. A extensão usa PyO3 com ABI estável abi3; Rust/Cargo são necessários para compilar a partir do código-fonte:

python3.13 -m pip install .
import json
from pathlib import Path
from receitapdf import gerar_pdf, url_qr

receita = json.loads(Path("examples/notificacao-a.json").read_text(encoding="utf-8"))
Path("saida.pdf").write_bytes(gerar_pdf(receita))
print(url_qr(receita))

gerar_pdf retorna bytes. Entrada ausente, inválida ou que exceda o espaço disponível gera ValueError; falhas internas de geração geram RuntimeError. Para construir o wheel: maturin build --release.

JSON: campos e formatos

CLI e APIs aceitam a mesma estrutura. Todos os exemplos declaram todos os campos da API; opcionais não usados recebem null. Os arquivos dados-curtos.json e dados-longos.json em cada pasta de modelo também são entradas válidas da CLI. Dados são totalmente fictícios.

Campos da receita

Campo Obrigatório Formato / uso
modelo Sim Um dos identificadores da tabela de modelos abaixo.
numero Sim, inclusive RCE Exatamente 0000.0-00.0000000: 4 dígitos, ponto, 1 dígito, hífen, 2 dígitos, ponto, 7 dígitos.
numero_sipeagro Não, somente RCE Registro SIPEAGRO no formato 00000-000000-00 (o primeiro bloco aceita de uma a cinco letras ou dígitos), impresso à direita no fim do bloco Prescrição.
prescritor Sim Objeto da tabela de emitente.
paciente Sim Objeto da tabela de paciente.
medicamento Sim Objeto da tabela de prescrição.
plataforma Sim Texto livre, até duas linhas no PDF.
data Sim Texto já formatado; padrão dos exemplos: DD/MM/AAAA.
assinatura Não Texto pronto, até duas linhas. Ausente, null ou em branco: Assinado digitalmente por {prescritor.nome} em {data}. Não cria assinatura criptográfica.
link_verificacao Sim URL fornecida pela plataforma. Deve caber em uma linha junto ao rótulo.
token_verificacao Não Texto pronto para consultar em link_verificacao, até duas linhas; não é extraído da URL.
codigo_verificacao Não Texto pronto para consultar em link_verificacao, até duas linhas; não é extraído da URL.
dados_especificos Não, exceto campos exigidos na talidomida Objeto com quantidade_por_extenso e responsavel. Pode ser omitido nos demais modelos.

Campos obrigatórios não aceitam texto em branco. Os limites de linhas são medidos pela fonte e largura efetivas do PDF; uma palavra que não caiba na largura é rejeitada. \n cria uma quebra explícita. Datas, CPF/passaporte, CID e valores clínicos são textos fornecidos pela integração; o componente não valida sua semântica.

Emitente (prescritor)

Campo Obrigatório Formato / uso
nome Sim Até duas linhas.
conselho Sim Sigla do conselho, como CRM, CRMV ou CRO.
uf Sim UF, como BA; fornecida pela integração.
registro Não Número de inscrição, como 000000; exibido como CRM/BA 000000. Campo novo, opcional para compatibilidade; os exemplos sempre o informam.
registro_mapa Não Registro do profissional no MAPA; nos modelos veterinários A/B e na RCE é exibido após a inscrição, como CRMV/BA 000000 (MAPA: 000000).
documento Talidomida CPF em texto, como 000.000.000-00.
cadastro Talidomida Número do cadastro do prescritor, em texto.
especialidade Talidomida Texto em uma linha.
endereco Sim Endereço completo em até três linhas.
telefone Sim E.164, como +557130000000; exibido no padrão brasileiro.

Paciente / proprietário (paciente)

Campo Obrigatório Formato / uso
nome Sim Até duas linhas; nos modelos veterinários é o nome do proprietário.
documento Sim CPF ou passaporte em texto, uma linha.
endereco Sim Endereço completo, até três linhas.
telefone Não E.164; impresso no modelo de talidomida.
data_nascimento Retinoides e talidomida Texto já formatado; exemplos em DD/MM/AAAA, uma linha.
sexo Retinoides e talidomida Texto em uma linha.
cid Talidomida Texto em uma linha.
especie A/B veterinários Espécie do animal, combinada com raça em uma linha.
raca A/B veterinários Raça do animal.
porte A/B veterinários Texto em uma linha.
responsavel Não Objeto de responsável; alternativa a dados_especificos.responsavel na talidomida.

Campos específicos de um modelo são impressos somente nele. Telefones preenchidos são validados mesmo quando o campo não é impresso.

Prescrição (medicamento)

Campo Obrigatório Formato / uso
nome Sim Nome do medicamento/substância em texto.
concentracao Sim Concentração com unidade, como 10 mg.
forma_farmaceutica Sim Como Comprimido.
quantidade Sim Texto; na talidomida, quantidade em algarismos, como 30.
posologia Sim Texto com quebra automática, limitado pela altura disponível.
dose_por_unidade Talidomida Texto com unidade, como 100 mg.
tempo_tratamento Talidomida Texto, como 30 dias.
outras_orientacoes Não Texto impresso na talidomida; em branco quando ausente.

Na talidomida, o box usa quantidade, dose, tempo de tratamento, posologia e outras orientações. Nome, concentração e forma farmacêutica continuam obrigatórios na entrada estruturada, embora não sejam impressos nesse modelo.

Dados específicos e responsável

Campo Obrigatório Formato / uso
dados_especificos.quantidade_por_extenso Talidomida Como trinta comprimidos. O PDF combina 30 (trinta comprimidos).
dados_especificos.responsavel Não Objeto abaixo, quando aplicável à talidomida. Tem prioridade sobre paciente.responsavel.
responsavel.nome Se houver responsável Até duas linhas na primeira coluna.
responsavel.documento Se houver responsável CPF ou passaporte em uma linha.
responsavel.telefone Não E.164, uma linha.
responsavel.endereco Se houver responsável Endereço completo, até duas linhas na largura do box.

Sem responsável, o box permanece vazio. O endereço dele reserva duas linhas conforme o layout aprovado.

Telefones e endereços

Telefones devem usar E.164 sem espaços ou pontuação: +5571930000000. Outros formatos, como (71) 93000-0000, são rejeitados. No PDF, números brasileiros aparecem como (71) 93000-0000 ou (71) 3000-0000; telefones de outros países mantêm E.164.

Endereços são strings completas. Padrão usado nos exemplos: logradouro, número, complemento, bairro, cidade/UF, CEP 00000-000. A ordem é uma convenção dos exemplos, não uma validação normativa implementada. Veja a consulta sobre endereço.

Modelos, exemplos e QR

modelo JSON completo PDF gerado
notificacao_a A PDF
notificacao_b B PDF
notificacao_b2 B2 PDF
notificacao_a_veterinaria A veterinário PDF
notificacao_b_veterinaria B veterinário PDF
retinoides Retinoides PDF
talidomida Talidomida PDF
receita_controle_especial RCE PDF

Nas Notificações, o QR usa a consulta SNCR:

https://sncr.anvisa.gov.br/receita/consultar?numero={numero}

Na RCE, o QR usa exatamente link_verificacao, conforme o layout aprovado neste projeto. O número da RCE segue o mesmo padrão textual do A, mas seu QR é da plataforma. url_qr retorna a URL correspondente nos oito modelos.

Documentos nos layouts nativos

Os layouts simples_carimbo e simples_digital geram receita simples, fórmula manipulada, pedidos de exame, atestados, declarações, termos e encaminhamentos. Diferem só no bloco de assinatura: espaço para carimbo ou caixa de assinatura digital. A geometria é fixa, como nos modelos Anvisa: texto que excede o limite do campo gera erro. Cabeçalho, paciente e título se repetem em todas as páginas; assinatura e "Emitido em" ficam só na última.

mise exec rust -- cargo run --release -- examples/documentos/receita-simples.json receita.pdf
use receitapdf::{gerar_documento, Documento, Imagens};

fn gerar(documento: &Documento, logo_png: &[u8]) -> Result<Vec<u8>, receitapdf::Erro> {
    let imagens = Imagens { logo: Some(logo_png), marca_dagua: None };
    Ok(gerar_documento(documento, imagens)?.pdf)
}
import json
from pathlib import Path
from receitapdf import gerar_documento

pasta = Path("examples/documentos")
documento = json.loads((pasta / "receita-simples.json").read_text(encoding="utf-8"))
gerado = gerar_documento(
    documento,
    logo=(pasta / "logo-exemplo.png").read_bytes(),
    marca_dagua=(pasta / "marca-dagua-exemplo.png").read_bytes(),
)
Path("documento.pdf").write_bytes(gerado.pdf)
print(gerado.areas_assinatura)  # [], ou as áreas com assinatura.coordenadas

A biblioteca Rust recebe as imagens em bytes (Imagens) e não lê arquivos nem acessa a rede. clinica.logo (cabeçalho) e marca_dagua (rodapé, a 10% de opacidade) aceitam PNG de até 2 MB; ambos são opcionais, e sem marca d'água o rodapé fica sem marca:

  • CLI: caminho relativo ao JSON (ou ao diretório atual, com stdin) ou endereço https://, baixado com tempo limite de 10 s, sem redirecionamento para HTTP. O download depende da feature rede, padrão na CLI e ausente na extensão Python.
  • Python: só endereços https:// são baixados automaticamente; para arquivos locais, passe os bytes em logo= e marca_dagua=.

Campos do documento

Campo Conteúdo
layout simples_carimbo ou simples_digital
titulo Título em maiúsculas abaixo do paciente, por exemplo Receita digital
numero Opcional: número SNCR da Receita Sujeita a Retenção (RET), no formato 0000.0-00.0000000. Aparece numa caixa à direita do título, e o QR do cabeçalho passa a consultar https://sncr.anvisa.gov.br/receita/consultar?numero={numero}
assinatura Opcional: campo_sig e coordenadas (padrão false) e url_qr (QR da caixa de assinatura digital; padrão verificacao.url_qr)
clinica nome (sem limite de linhas no cabeçalho, que cresce quando o texto passa do QR; uma linha de até 294 pt no rodapé); opcionais cnpj, endereco, contato e logo
emissor nome, especialidade (opcional) e conselho com sigla, numero e uf
contingencia Opcional (padrão false): emissão em contingência. A faixa do paciente fica com uma linha só, com o nome e o selo "Emitido em contingência", e o título e o corpo sobem; o endereço do paciente não é impresso
paciente nome; opcionais documento já rotulado (CPF: ...) e endereco. Sem limite de linhas: a faixa do paciente cresce além das três linhas do design e empurra o título e o corpo
corpo Lista de blocos {"paragrafo": "..."}, {"medicamento": {...}} ou {"exame": {...}}
cids Opcional: códigos CID-10 exibidos após o corpo (até duas linhas)
orientacoes Opcional: orientações ao paciente após o corpo, uma por item (até quatro linhas cada)
signatarios_extras Até um assinante além do emissor, como o paciente num termo. Em simples_carimbo assina à mão; em simples_digital assina digitalmente, ao lado da caixa, que fica mais alta para o texto do emissor ter três linhas
emitido_em Data e hora já formatadas
marca_dagua Opcional: caminho ou URL do PNG da marca d'água do rodapé
verificacao url_qr (QR do cabeçalho), site, token e codigo
  • medicamento: nome (até duas linhas), quantidade, detalhe (apresentação, até duas linhas) e observacao (posologia, até quatro linhas).
  • exame: nome (até duas linhas), codigos (lista de rotulo/valor, até duas linhas) e instrucao (até três linhas).

Medicamentos e exames são numerados em sequências próprias e nunca se dividem entre páginas; parágrafos quebram por linha. CID-10 e orientações aparecem depois do corpo, abaixo de um tracejado, com o rótulo sempre junto da primeira linha.

Assinatura

  • simples_carimbo: reserva 64 pt acima da linha do emissor para carimbo e assinatura manual ou carimbo visual do serviço de assinatura. O rodapé só afirma a assinatura ICP-Brasil com campo_sig ou coordenadas.
  • simples_digital: caixa com QR e texto de assinatura ICP-Brasil. O QR usa assinatura.url_qr (por exemplo, o validador da assinatura) ou, sem ele, verificacao.url_qr.
  • campo_sig: cria campos de assinatura vazios (/FT /Sig) na última página, onde serviços PAdES assinam pelo nome: assinatura_emissor e, num termo em simples_digital, assinatura_paciente. Em simples_digital o campo do emissor é invisível.
  • coordenadas: retorna areas_assinatura, uma lista com campo, pagina, x, y, largura, altura, largura_pagina e altura_pagina, em pontos com origem no canto inferior esquerdo (o mesmo espaço do /Rect do PDF). A CLI imprime a lista em JSON. Em simples_carimbo a área é o espaço do carimbo do emissor; em simples_digital o emissor não tem área (a caixa já mostra a assinatura) e, num termo, o paciente tem a área acima da linha dele, ao lado da caixa.
Documento JSON PDFs
Receita simples JSON carimbo, digital
Fórmula manipulada JSON carimbo, digital
Exames laboratoriais JSON carimbo, digital
Atestado JSON carimbo, digital
Termo de consentimento JSON carimbo, digital

Os demais exemplos, inclusive documentos de várias páginas, estão em examples/documentos, com prévias e contato visual.

Prévia em WebP

gerar_preview rasteriza todas as páginas de um PDF gerado (receita Anvisa ou documento nativo) e devolve uma imagem WebP sem perdas por página, com a marca d'água diagonal "PRÉVIA — SEM VALIDADE" em cinza translúcido. O PDF não muda: a marca existe só nas imagens.

let paginas: Vec<Vec<u8>> = receitapdf::gerar_preview(&pdf, 144.0)?;
from receitapdf import gerar_preview

for numero, webp in enumerate(gerar_preview(gerado.pdf, dpi=144), start=1):
    Path(f"pagina-{numero}.webp").write_bytes(webp)
  • dpi vai de 36 a 300 (144 dpi gera A4 com 1190 × 1684 px). Fora da faixa, ou com PDF ilegível, gera Erro::PreviaInvalida (ValueError em Python).
  • Usa o hayro, rasterizador em Rust puro, sem PyMuPDF nem bibliotecas nativas. Fica na feature preview, padrão na CLI e incluída na wheel, onde acrescenta cerca de 2,5 MB. No Python, a função libera o GIL.
  • Exemplo de uma receita de três páginas.

Gerar todos os exemplos

python -m pip install pymupdf pillow
python examples/gerar_exemplos.py

O script compila a CLI Rust e gera:

  • Os oito PDFs principais em examples/.
  • Dezesseis PDFs e PNGs, com dados curtos e longos, em modelos-gerados.
  • Prévias de 144 dpi e contato visual.
  • Os quatro exemplos de rodapé com plataforma/assinatura em uma ou duas linhas, em examples/rodape-plataforma-*.pdf.
  • Cada documento de examples/documentos/ nos layouts simples_carimbo e simples_digital, com PNGs de todas as páginas em examples/documentos/png/ e as prévias WebP da receita longa, geradas pela CLI, em examples/documentos/previa/.

PyMuPDF e Pillow são usados apenas nas prévias. Os PDFs são produzidos pela mesma CLI que o consumidor usa.

Os scripts examples/recriar_*.py mantêm as referências de ajuste visual e usam os mesmos JSONs da API. Após alterar os cabeçalhos nesses estudos, execute python examples/preparar_modelos.py para atualizar assets/modelos/ e recompile a biblioteca. As fontes Open Sans e os avisos vetoriais vêm dos originais; a composição Rust usa printpdf e lopdf. DejaVu Sans é incorporada nos dados e DejaVu Sans Bold Oblique nas URLs. Os layouts simples_* usam Quicksand (OFL).

Compilação e testes

mise exec rust -- cargo check --locked --all-targets --features python
mise exec rust -- cargo test --locked
mise exec rust -- cargo test --locked --features python
maturin build --release

O código é MIT (LICENSE); fontes e modelos de origem mantêm seus próprios termos.

Publicar no PyPI

O workflow publish-pypi.yml cria o sdist e o wheel Linux x86_64, executa os testes e publica via Trusted Publishing. Para habilitar a publicação, cadastre um publisher pendente nas configurações do PyPI com estes valores:

  • Projeto PyPI: receitapdf
  • Owner do GitHub: ramon
  • Repositório: receitapdf
  • Workflow: .github/workflows/publish-pypi.yml
  • Environment do GitHub: pypi

Depois desse cadastro, a publicação pode ser iniciada manualmente em master ou ao publicar uma release do GitHub com tag vX.Y.Z igual à versão em pyproject.toml. O job usa OIDC; não é necessário guardar token do PyPI como secret.

Metadata

Release files for receitapdf 0.5.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for receitapdf 0.5.0
File Size Uploaded Provenance Actions
receitapdf-0.5.0.tar.gz 68.6 MB With provenance Details for receitapdf-0.5.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for receitapdf 0.5.0
File Interpreter ABI Platform Actions
receitapdf-0.5.0-cp313-abi3-manylinux_2_28_x86_64.whl CPython 3.13 abi3 Linux glibc 2.28+ x86-64 Details for receitapdf-0.5.0-cp313-abi3-manylinux_2_28_x86_64.whl

Total release size: 74.3 MB

Release files / receitapdf-0.5.0.tar.gz

Download URL receitapdf-0.5.0.tar.gz
Size 68.6 MB
Tags Source
SHA-256 checksum
How to use checksums
49e8210f05e5cfb7a7110c40263357528b71f032ceb4a2884fd851eeb129dd31
BLAKE2b-256 checksum
How to use checksums
6cdafce58edf4cba776515f8dab8827107691fde744e4471d207e05f4a40b98c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 16, 2026.

Transparency log

Release files / receitapdf-0.5.0-cp313-abi3-manylinux_2_28_x86_64.whl

Download URL receitapdf-0.5.0-cp313-abi3-manylinux_2_28_x86_64.whl
Size 5.7 MB
Tags CPython 3.13 Linux glibc 2.28+ x86-64 abi3
SHA-256 checksum
How to use checksums
5db294159227913bca783d41ae20a5dfa15c47c685cb04bb70697ca9ff295818
BLAKE2b-256 checksum
How to use checksums
c68ae33a3d4672ad44e4841f90b38ed001b9591a4345414b4d4ba69570c1f22a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 16, 2026.

Transparency log

Security for receitapdf 0.5.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

Both files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for receitapdf-0.5.0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 16, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page