13. Mudanças
O que mudou em cada versão, e o que isso pode quebrar do seu lado.
A versão ainda é 0.x, então um salto de minor pode quebrar alguma coisa.
Quando quebra, a entrada diz exatamente o quê e como se adaptar. Nada aqui muda
em silêncio.
Corrigido
Seção intitulada “Corrigido”- Um script podia abortar o processo em vez de ser rejeitado. O aninhamento
no código virava aninhamento na pilha de chamadas, então cerca de dois mil
parênteses estouravam a pilha e a execução morria com SIGABRT — sem código de
saída 3, sem mensagem, sem nada capturável. Seis formas faziam isso:
((((…)))),!!!!…,----…, arrays aninhados, blocos aninhados e"#{"#{…}"}". O parser agora conta a profundidade e recusa além de 128 níveis, muito acima de qualquer script escrito por uma pessoa e muito abaixo de onde a pilha acaba. O pior caso era justamente o comando que se roda primeiro num script alheio: opdfl lintmorria em vez de reportar. - Um pacote podia nomear um arquivo fora da pasta em que se instala. A
checagem recusava
..e/inicial, mas nãoC:/Windows/evil.dllnem\\servidor\share\x— nenhum dos dois contém aquilo, e ambos são absolutos no Windows, onde juntá-los à pasta de instalação descarta a pasta. Agora todo componente de todo caminho precisa ser um nome simples, por regras que não mudam com a plataforma que desempacota. - Um pacote podia esgotar a memória durante a leitura. As entradas eram lidas inteiras para a memória antes de o manifesto ser consultado — inclusive entradas que o manifesto nunca cita — então um arquivo de 299 KB de zeros comprimidos tomava 380 MB, e um maior tomava a máquina. O desempacotamento agora para em 64 MB, muito acima de qualquer pacote real de scripts e tabelas.
- O
--pages 1-20000000alocava os números de página antes mesmo de abrir um documento — 214 MB para depois não dizer nada. Intervalos acima de um milhão de páginas são recusados.
Vale saber
Seção intitulada “Vale saber”- As correções acima colocam tetos onde não havia nenhum, então algumas coisas
antes aceitas deixam de ser: um script aninhado além de 128 níveis, um pacote
que expande além de 64 MB, um pacote que nomeia arquivo com
:ou\, e um intervalo de páginas acima de um milhão. Cada um está muito fora do que um script, pacote ou documento real contém — mas nenhum deles falhava antes, então ficam registrados em vez de serem descobertos.
Os modos do visualizador foram removidos. O Flip, o Fade, o slider
Blend e o slider Differences, junto com as teclas space e d que os
controlavam, não existem mais.
Só o visualizador gerado é afetado — nenhum relatório, código de saída ou flag muda, então nada que lê a saída do
pdflpercebe. Três painéis respondem ao que aqueles modos serviam: os dois arquivos ficam na tela ao mesmo tempo, em vez de um de cada vez. Se você escreveu instruções próprias citando um modo ou uma tecla, elas precisam de revisão.
- O visualizador do
pixelcompareagora são três painéis lado a lado — o original, o arquivo novo, e os dois com as diferenças pintadas por cima — em vez de um painel com escolha de modos. As duas referências ficam paradas enquanto o terceiro varre, então dá para ver entre o que o wipe está cortando. - Cada painel tem o mesmo par de barras — uma em pé, uma deitada — na mesma posição, e as seis se movem juntas. Elas acompanham o ponteiro: não há o que agarrar nem o que mirar, o cruzamento é onde o mouse está. No painel da diferença as barras cortam, então o arquivo novo aparece à direita da em pé e abaixo da deitada, e o cruzamento é o canto do que está sendo revelado; nos outros dois elas são réguas sobre a mesma coluna e a mesma linha da página. A barra deitada começa no topo, o que deixa a em pé como um wipe simples de altura inteira até o ponteiro entrar num painel.
- A roda do mouse dá zoom nos três painéis juntos, até 8×, em torno do ponto sob o ponteiro, e para no ajuste à página ao diminuir. Arrastar com qualquer botão do mouse move a própria página, nos três de uma vez e na velocidade do ponteiro seja qual for o zoom. As barras mantêm a espessura em qualquer zoom. O Reset view devolve o zoom, a panorâmica e as barras ao ponto de partida e fica desabilitado enquanto não houver o que desfazer; ir para outra página faz o mesmo, já que um zoom pertence à página em que foi feito.
- Os painéis são dimensionados contra a janela: a comparação inteira cabe na tela sem rolagem, em qualquer formato de janela, e cada um mantém a proporção da página. Onde os dois arquivos discordam do tamanho de uma página, cada uma é mostrada inteira dentro da caixa comum, em vez de esticada para preenchê-la.
- Ele abre nas páginas que diferem, com All para trazer as outras de volta. Num documento longo, são essas páginas o motivo de ele ter sido aberto.
O identificador de um achado de loading muda. Ele é derivado da mensagem,
e a mensagem não carrega mais as quebras de linha que o pdfium colocava nela.
Só achados de um documento que não pôde ser lido são afetados — nenhum check seu produz um desses. Se alguma baseline aprova esse identificador, ele precisa ser registrado de novo. É por isso que esta versão é minor e não patch: identificador é a base de uma baseline, então não muda em silêncio.
Corrigido
Seção intitulada “Corrigido”- O
pdfl packgravava um arquivo aninhado com o separador da máquina que montou o pacote — no Windows,dados\lotes.csv. Um pacote é montado numa máquina e instalado em outra, então qualquer coisa diferente de/é um pacote que instala e depois não acha os próprios arquivos; nem a nossa verificação achava. Pacotes montados em Linux e macOS nunca foram afetados. - O
--outpute o--output-fileeram ignorados quando o PDF não podia ser lido. Orun, ofixe ocompareimprimiam JSON no stdout qualquer que fosse o pedido, então uma pipeline que pedia JUnit em arquivo não recebia arquivo nenhum e sim um relatório num fluxo que ela não estava lendo — um arquivo corrompido parecia uma execução que nunca aconteceu, embora o código de saída dissesse10corretamente. O relatório de falha agora sai pelo mesmo caminho de todos os outros. - Um erro do pdfium chegava ao relatório como um enum do Rust impresso em três linhas. Um diagnóstico é um campo de uma linha de CSV e um atributo de XML, então agora ele é dobrado numa única linha.
-
O
pdfl pixelcomparemostra uma barra de progresso em cada etapa — rasterizar cada arquivo, comparar, gravar o visualizador. Ela só é desenhada quando o stderr é um terminal, porque a barra sobrescreve a própria linha e um arquivo de log não tem cursor para mover; redirecionada, fica em silêncio. O--quietsilencia em qualquer caso. -
O
--jobs <n>nopdfl pixelcomparecompara essa quantidade de páginas ao mesmo tempo, e por padrão usa uma por CPU: 41 páginas a 150 dpi caem de 3,6s para 1,2s. Só a comparação é paralela — o pdfium serializa toda chamada atrás de um único lock global, então a rasterização não pode ser, e é por isso que o ganho é de três vezes e não de oito. O relatório não muda com o valor: as páginas são recompostas na ordem de página, então os diagnósticos, sua ordem e suas impressões digitais saem idênticos — e os arquivos do visualizador também.Aqui o padrão é uma por CPU, enquanto
testewatchusam--jobs 1. Lá cada job é um processo filho segurando o próprio documento; aqui as páginas já estão na memória e as threads as compartilham.
- O
pdfl pixelcomparecompara dois PDFs pela aparência, e não pelo texto, página por página, e informa a fração de pixels que difere. Ele alinha uma página que apenas se deslocou antes de comparar, para que um pixel fora do lugar não enterre a mudança que importa. - O
--viewer <pasta>dopixelcomparegrava uma aplicação autocontida — sem CDN, sem bundler, sem servidor — para varrer, alternar ou misturar os dois arquivos com as diferenças pintadas no lugar: vermelho para tinta que sumiu, verde para tinta nova, azul para o mesmo peso em outra cor. A faixa de páginas filtra para Changed only, e as setas seguem o filtro — num documento longo, passar pelas páginas que não mexeram é a parte lenta.
if/else if/else, como expressão: o valor é a última expressão do ramo que rodou, a mesma regra que uma função já segue. Serve tanto como valor (const LIMITE = if couche { 300 } else { 260 }) quanto como guarda em volta de comandos, sem uma segunda sintaxe. Um ramo que não roda devolvenull, e cada ramo tem escopo próprio — atribuir a uma variável que já existe fora continua atualizando aquela.
Corrigido
Seção intitulada “Corrigido”- O
--varagora chega aopdfl teste aopdfl watch, não só aopdfl run. Nenhum dos dois repassava para os filhos que disparam, então um script que lêvars.*não podia ser testado nem observado: todo caso ou arquivo falhava com “was not provided”, não importa o conteúdo.
- O
pdfl packnão empacota mais planilhas (.xlsx,.xls,.ods), e diz qual arquivo ficou de fora. Nenhuma funçãodata::abre uma, então um pacote que a carregava instalava direitinho e falhava na primeira consulta. Se você empacotava planilha, exporte antes para.csvou.json.
--tags TAGnorunfiltra quais checks rodam. Repetível; um check roda quando carrega qualquer uma das tags informadas.--jsonnoinspecte nolint, e--output jsonnodoc. Todo subcomando passa a ser legível por programa.--output sarife--output junit, onde quer que se escolha um formato de relatório —run,compare,watchefix. SARIF é o que o GitHub code scanning lê; JUnit é o que o painel de testes de qualquer CI lê.pdfl completions <shell>imprime o script de autocompletar para bash, zsh, fish, elvish ou powershell.--quietem todos os comandos silencia progresso e confirmações no stderr. Os erros continuam aparecendo, e oprint()fica intacto — aquilo é a saída do próprio script, e sumir com ela mudaria o que o script faz.data::load_datasetedata::lookup_valueleem.jsonalém de.csv: um array de arrays, ou um array de objetos cujo primeiro objeto nomeia as colunas na ordem em que o arquivo as escreve.pdfl test <script>roda um script sobre uma pasta de PDFs e compara cada relatório com o gravado ao lado, então um perfil que passa a achar outra coisa quebra um teste em vez de surpreender alguém lá na frente. O--updategrava os relatórios esperados.--jobs <n>nopdfl testroda essa quantidade de casos ao mesmo tempo, cada um em seu processo. Oito arquivos de 41 páginas: 8,9s com--jobs 1, 1,1s com--jobs 8. O padrão segue1, já que cada job segura um documento na memória;--jobs 0dá um por CPU.--jobs <n>também nopdfl watch: os arquivos são validados por processos filhos, então a passada em lote escala igual (9,5s para 1,2s em oito arquivos de 41 páginas). O laudo escrito é idêntico independente do--jobs.--eventsnopdfl watchespera pelas notificações do sistema de arquivos em vez de por tempo. É opt-in, não o padrão: o inotify num mount NFS ou SMB só reporta o que a máquina local escreve, então uma pasta de rede ficaria muda. Se o observador não puder ser criado, o watch avisa e volta para o tempo.--journal <arquivo>nopdfl watch: registro append-only do que foi validado, um objeto JSON por arquivo. Rodar de novo com o mesmo journal pula os arquivos que ele cobre — um lote interrompido no quatro mil de cinco mil termina os mil que faltam — sem deixar de reportar os veredictos, então um lote retomado nunca diz que a pasta está limpa.--timeout <s>nopdfl watchmata a análise de um arquivo depois desse tanto de segundos e o reporta como recusado — um achado,check_name: "timeout"— em vez de deixar o lote travado. A recursão num script.pdfljá tem limite de profundidade, então isto é para o que um script não pode causar: o pdfium entrando em loop ou travando num PDF malformado ou adversário.
Vale saber
Seção intitulada “Vale saber”- Uma tag que nenhum check carrega é erro, não aprovação vazia. Do contrário uma pipeline com a tag escrita errada não validaria nada e reportaria arquivo limpo.
- Um
rulenão tem tags, então--tagso pula — mesma resposta que um check sem tag recebe. - O relatório JSON ganhou
checks_run, os checks e rules que rodaram. Ele não sobe oschema_version, porque quem ignora campo desconhecido sobrevive a isso. O JUnit precisa dele: os diagnósticos só nomeiam os checks que acharam algo, e uma execução limpa reportada como zero testes é, para uma CI, uma execução que não aconteceu.
Corrigido
Seção intitulada “Corrigido”- O
pdfl watchagora acorda quando o arquivo mais novo terminou de assentar, em vez de até um intervalo inteiro depois. Com--debounce 3000, um arquivo que chega é reportado uns 3s depois, e não até 6s.
- Scripts recebem valores pela linha de comando:
--var nome=valor, lidos comovars.nome. Um valor ausente nomeia a flag que o forneceria, em vez de resolver para nada. - Quatro exemplos completos de comparação entre dois documentos com
visual:::proof.pdfl,reprint.pdfl,scope.pdfleintake.pdfl.
Nada. Um script que não menciona vars se comporta exatamente como antes.
Os identificadores de diagnóstico mudaram de forma. Eram PDFL-001, um
contador dentro da execução; agora derivam do próprio achado, como
PDFL-093751a2.
Qualquer coisa que case com
PDFL-\d+deixa de casar. Em troca, o identificador sobrevive a um check inserido acima dele — que é o que torna possível manter uma linha de base aprovada.
Entrada ilegível sai com 10 em vez de 2. Arquivo corrompido e arquivo
reprovado eram indistinguíveis para uma pipeline.
Se a sua CI trata
2como “este arquivo foi reprovado”, ela verá10quando o arquivo nunca chegou a ser julgado. Os achados seguem em0,1e2; erro de sintaxe no script segue em3.
- Um check pode declarar a severidade dos seus achados:
check "..." severity: warning { ... }—error(padrão),warningouinfo. É isso que dá ao--fail-on warningalgo sobre o que agir. - O relatório JSON abre com
schema_version, para o consumidor saber que forma está lendo. Ele sobe só quando quem lia a saída anterior quebraria; acrescentar campo não o faz subir.
Corrigido
Seção intitulada “Corrigido”- O relatório em PDF estava parcialmente em português: o cabeçalho da seção dizia
Diagnósticose cada diagnóstico trazia(linha N). Ambos estão em inglês agora, que é o que a documentação sempre prometeu.
Os alvos de release passaram de x64 para amd64, então todo nome de asset
mudou.
Os arquivos portáteis deixaram de ser publicados, exceto um para Linux amd64.
Qualquer coisa que baixe
pdfl-<versão>-linux-x64.tar.gz, ou qualquer portátil que não seja o Linux amd64, precisa mudar. Em CI, instale pelo.deb— as receitas desta documentação foram atualizadas para isso — ou use o tarball de Linux amd64 onde instalar não for possível.
Corrigido
Seção intitulada “Corrigido”- Duas lacunas encontradas auditando a documentação contra o código:
text::detect_personal_dataetext::detect_piiaceitam uma string opcional que não estava documentada, efix::reorder_pagesestava escrito de duas formas diferentes entre os idiomas.
- Instaladores para todas as plataformas:
.debno Linux,.dmgno macOS,-setup.exee.msino Windows. - Builds para macOS Intel, compilados cruzado a partir do runner Apple Silicon.
Corrigido
Seção intitulada “Corrigido”- O instalador do Windows era construído com caminhos que resolviam para o diretório errado, e por isso nunca produzia arquivo.
- Os pacotes de release carregavam os headers C e os arquivos de build do pdfium, que só interessam a quem compila contra ele. Cerca de 550 KB por pacote.
- Windows x64 entra nas plataformas publicadas.
A
pdfium.dllembutida fica empdfium\bin, não empdfium\lib. Se você empacotar opdflpor conta própria, mantenha o layout como distribuído: o binário procura a biblioteca ao lado de si mesmo.
Os assets de release carregam a versão no nome, como
pdfl-<versão>-<alvo>.tar.gz, e o diretório de dentro também.
.../releases/latest/download/<nome>deixa de resolver, porque esse endpoint exige o nome exato. Baixe por padrão em vez disso:gh release download --pattern 'pdfl-*-linux-amd64.*'.
- O código, o README e os exemplos estão em inglês. A documentação segue em sete idiomas.
Primeira versão pública. A linguagem, o interpretador e dez comandos de CLI, com documentação em sete idiomas.