11. Comandos do CLI

← Biblioteca padrão · Índice · Próximo: Receitas →

Doze comandos: quatro que trabalham com PDFs, quatro sobre os scripts, dois de distribuição e dois de licenciamento.

Comando O que faz
run Valida um PDF com um script
compare Compara duas versões de um PDF
watch Monitora uma pasta e valida o que chega
fix Aplica correções e salva um PDF novo
inspect Resumo rápido de um PDF
lint Analisa um script sem executar
fmt Formata um script
doc Gera documentação de um script
pack Empacota perfis e bases
add Instala um pacote
fingerprint Impressão digital do equipamento
license Verificação de licença

Códigos de saída

Todos os comandos que validam usam a mesma convenção:

Código Significado
0 Tudo passou
1 Apenas avisos
2 Erros de validação, ou PDF ilegível
3 Erro de sintaxe no script

Em scripts de shell:

pdfl run perfil.pdfl arquivo.pdf > relatorio.json
case $? in
  0) echo "aprovado" ;;
  1) echo "aprovado com ressalvas" ;;
  2) echo "reprovado — veja relatorio.json" ;;
  3) echo "erro no script de validação" ;;
esac

pdfl run

Valida um PDF com um script.

pdfl run <script.pdfl> <entrada.pdf> [opções]
Opção Padrão O que faz
--output json|csv|html|pdf json Formato do relatório
--output-file <arquivo> Grava em arquivo em vez do stdout
--fail-on error|warning error Com warning, avisos também dão exit 2
--verbose Informação extra no stderr
# Relatório JSON no terminal
pdfl run prepress.pdfl revista.pdf

# HTML para enviar ao cliente
pdfl run prepress.pdfl revista.pdf --output html --output-file laudo.html

# PDF de auditoria (o formato pdf sempre grava em arquivo)
pdfl run prepress.pdfl revista.pdf --output pdf --output-file laudo.pdf

# CSV para planilha
pdfl run prepress.pdfl revista.pdf --output csv --output-file achados.csv

# Rigoroso: avisos também reprovam
pdfl run prepress.pdfl revista.pdf --fail-on warning

O relatório JSON

{
  "script_name": "prepress.pdfl",
  "input_file": "revista.pdf",
  "profile": "offset-revista",
  "status": "FAIL",
  "total_pages_analyzed": 120,
  "error_count": 2,
  "warning_count": 0,
  "info_count": 0,
  "diagnostics": [
    {
      "id": "PDFL-001",
      "severity": "error",
      "check_name": "Cobertura de tinta",
      "message": "page 7: 324% ink (limit 300%)",
      "line": 12
    }
  ]
}

O mesmo PDF com o mesmo script sempre gera o mesmo relatório, byte a byte — dá para versionar e comparar em CI.


pdfl compare

Compara duas versões de um PDF: texto, estrutura e metadados.

pdfl compare <v1.pdf> <v2.pdf> [opções]
Opção Padrão O que faz
--output json|csv|html|pdf json Formato
--output-file <arquivo> Grava em arquivo
--normalize Ignora maiúsculas e espaçamento
--ignore-dates Mascara datas antes de comparar
--similarity-threshold <0-100> 100 Similaridade mínima aceitável
# Comparação simples
pdfl compare aprovado_v1.pdf novo_v2.pdf

# Tolerando pequenas diferenças de formatação e datas
pdfl compare aprovado_v1.pdf novo_v2.pdf --normalize --ignore-dates

# Aceita até 1% de diferença; abaixo disso vira erro
pdfl compare v1.pdf v2.pdf --similarity-threshold 99 \
  --output html --output-file diff.html

Como funciona

  • As páginas são alinhadas por conteúdo, não por número: se uma página foi inserida no meio, o comparador percebe em vez de acusar tudo depois dela como diferente. Funciona em documentos de mais de mil páginas.
  • Cada página alinhada recebe uma nota de similaridade e uma amostra das linhas que mudaram (- saiu, + entrou).
  • Metadados diferentes viram aviso; texto alterado vira erro se ficar abaixo do threshold, aviso se acima.
  • O relatório traz o campo similarity com a nota geral.
page 4 → 4: similarity 97.8% | -título original contos | +título revisado

pdfl watch

Monitora uma pasta e valida cada PDF que chega ou muda.

pdfl watch <pasta> --script <script.pdfl> [opções]
Opção Padrão O que faz
--pattern <glob> *.pdf Quais arquivos processar
--exclude <glob> Quais ignorar
--output-dir <pasta> ao lado do PDF Onde gravar os relatórios
--depth <n> 1 Níveis de subpasta
--debounce <ms> 1000 Espera o arquivo parar de ser copiado
--report json|csv|html|pdf json Formato dos relatórios
--fail-fast Para no primeiro erro
--once Processa o que já está lá e sai
# Pasta de entrada da gráfica, rodando continuamente
pdfl watch entrada/ --script preflight.pdfl --output-dir laudos/ --report html

# Modo lote para CI: processa tudo e sai com o pior código
pdfl watch entrada/ --script preflight.pdfl --once
echo "resultado: $?"

# Ignorando rascunhos
pdfl watch entrada/ --script preflight.pdfl \
  --pattern "*.pdf" --exclude "*_rascunho*"

O debounce existe porque arquivos grandes chegam aos poucos: o watch só processa quando o arquivo para de mudar, evitando ler um PDF pela metade.

Os relatórios saem como <nome>.report.json (ou .csv, .html, .pdf).


pdfl fix

Aplica operações fix:: e salva um PDF novo. Detalhes no capítulo 8.

pdfl fix <entrada.pdf> <script.pdfl> --output <saida.pdf> [opções]
Opção O que faz
--output <arquivo> PDF de saída (obrigatório)
--dry-run Lista as operações sem salvar
--report json|csv|html|pdf Formato do relatório
--report-file <arquivo> Grava o relatório em arquivo
# Ver o que seria feito, sem tocar em nada
pdfl fix original.pdf normalizar.pdfl --output saida.pdf --dry-run

# Aplicar de verdade
pdfl fix original.pdf normalizar.pdfl --output corrigido.pdf

pdfl inspect

Resumo rápido de um PDF, sem script.

pdfl inspect <arquivo.pdf>
File:     revista.pdf
Size:     26 KB (27284713 bytes)
SHA-256:  af1029842e5bfeae338ead82fb449ef851be742b1d63117c12596e3ea123a616

Pages:    120
Page size: 496 x 709 pt
Boxes:    MediaBox, TrimBox, BleedBox

Metadata:
  Title: Revista Exemplo
  Creator: Adobe InDesign 19.3

Fonts:    26
  ABCDEF+Helvetica — embedded
  Arial — NOT embedded
Images:   81 (minimum DPI 136, spaces: DeviceCMYK, Indexed)
Max. estimated TAC: 300% (RGB render approximation)

Warnings:
  ! there are non-embedded fonts
  ! 3 image(s) below 300 DPI

É o primeiro comando a rodar quando um arquivo novo chega: em segundos você sabe se vale a pena abrir.


pdfl lint

Analisa um script sem executar, apontando problemas de qualidade.

pdfl lint <script.pdfl>

Detecta:

  • variáveis, parâmetros de bloco e functions declarados e nunca usados (prefixe com _ para silenciar: _page)
  • checks duplicados ou vazios
  • namespace desconhecido (text::, struct::, visual::, prepress::, codes::, fix::, data::)
  • assert/require fora de qualquer check
  • uso de fix:: (que só roda em pdfl fix)
$ pdfl lint perfil.pdfl
perfil.pdfl: warning: variable 'LIMITE' declared and never used
perfil.pdfl: warning: check "Fontes" declared 2 times

Sai com código 1 se houver avisos — dá para usar em CI.


pdfl fmt

Formata o script: indentação de 2 espaços, espaçamento consistente, linhas em branco colapsadas. Preserva comentários e unidades (3mm continua 3mm).

pdfl fmt <script.pdfl>            # formata no lugar
pdfl fmt <script.pdfl> --check    # não altera; sai com 1 se estiver fora do padrão
# Em CI, garantindo padrão na equipe
for f in perfis/*.pdfl; do pdfl fmt "$f" --check || exit 1; done

pdfl doc

Gera a documentação de um script a partir do próprio código.

pdfl doc <script.pdfl> [--output markdown|html]

Produz: perfil, tabela de constantes, functions, imports e — para cada check — as tags e o que ele valida (as mensagens dos assert viram a descrição).

# Markdown para o repositório
pdfl doc prepress.pdfl > docs/perfil-prepress.md

# HTML para enviar a quem não lê código
pdfl doc prepress.pdfl --output html > perfil.html

É o artefato para o gerente de produção entender o que o perfil valida sem abrir o script.


pdfl pack

Empacota scripts e bases em um arquivo .pdflpkg distribuível.

pdfl pack <pasta> [--name <nome>] [--version <versão>] [--output <arquivo>]

Inclui .pdfl, .csv, .txt, .json e .xlsx da pasta (recursivamente), com um manifest.json que registra o SHA-256 de cada arquivo. O pacote é determinístico: mesma pasta gera bytes idênticos.

pdfl pack perfis/grafica --name perfil-grafica --version 1.0.0
# cria perfil-grafica.pdflpkg

pdfl add

Instala um pacote local, conferindo os hashes do manifesto.

pdfl add <pacote.pdflpkg> [--dir <pasta>]
pdfl add perfil-grafica.pdflpkg
# instala em ./pdfl_profiles/perfil-grafica@1.0.0/

pdfl run pdfl_profiles/perfil-grafica@1.0.0/prepress.pdfl arquivo.pdf

Se algum arquivo tiver hash diferente do registrado, a instalação é recusada — pacote corrompido ou adulterado não entra.

Repositório remoto e assinatura digital não fazem parte desta versão: o add instala a partir de arquivos locais.


pdfl fingerprint

A impressão digital deste equipamento — o valor que o cliente envia para receber uma licença.

pdfl fingerprint generate

Ela sai sozinha no stdout; o texto de apoio vai no stderr, então pdfl fingerprint generate 2>/dev/null dá um valor pronto para canalizar.

$ pdfl fingerprint generate
Device fingerprint for this machine:

1a7553e711c53306be083ea8927c9cbe

Send it to your vendor to be issued a license.
It changes if the operating system is reinstalled.

A impressão digital vem do identificador de máquina do sistema (/etc/machine-id, IOPlatformUUID ou MachineGuid). Ela não sobrevive a uma reinstalação do sistema, e máquinas virtuais clonadas da mesma imagem compartilham o valor. Para CI e contêineres — onde a máquina é destruída a cada execução — existe a licença seatless, que não olha o equipamento.


pdfl license

Confere uma licença recebida. O pdfl apenas verifica: quem emite é o fornecedor, com a chave privada, que não acompanha o binário.

pdfl license check <token>
$ pdfl license check "$(cat license.txt)"
signature:  ok
customer:   Example Print Shop
expires:    2027-01-01
features:   all
device:     1a7553e711c53306be083ea8927c9cbe
this machine: matches

Sai com 0 se a licença vale para esta máquina e 2 se não vale — inclusive quando a assinatura está boa mas a licença é de outro equipamento, caso em que a mensagem diz exatamente isso.

Opção O que faz
--pubkey <base64> Chave pública a usar, em vez da embutida na compilação

Onde a licença é exigida

run, fix, watch, compare e inspect param com código 2 se não houver licença válida para este equipamento. fingerprint generate nunca exige — é ele que produz o valor necessário para pedir uma licença. lint, fmt, doc e pack mexem só em script, sem tocar em PDF, e também não exigem.

O token é procurado nesta ordem:

  1. $PDFL_LICENSE — o caminho para CI, onde ele vira secret
  2. ~/.config/pdfl/license
  3. pdfl.license ao lado do executável
$ pdfl run perfil.pdfl arquivo.pdf
error: no license found

Run 'pdfl fingerprint generate' and send the value to your vendor
to be issued a license. Then set PDFL_LICENSE, or save the token to
/home/voce/.config/pdfl/license.

Uma licença seatless tem teto de execuções, contado em ~/.config/pdfl/state. O mesmo arquivo guarda quando foi a última execução e recusa rodar se o relógio andar para trás — com uma hora de tolerância, para não brigar com sincronização de horário.


← Biblioteca padrão · Índice · Próximo: Receitas →