Medi minha própria taxa de falso positivo em 1.728 documentos que não escolhi

985 PDFs do governo americano e 743 da web brasileira, nenhum escolhido por mim — e uma taxa de falso positivo quatro vezes pior do que eu acreditava.

Referência: Sinais de fraude em PDF — o guia de campo

Todo corpus que eu tinha era um corpus que eu escolhi. Processos judiciais dos EUA, contratos públicos brasileiros, amostras de extrato bancário — 705 documentos, coletados com cuidado, e uma taxa de falso positivo calculada em cima de todos eles. Aí eu reparei que a taxa não era um número só. Um alvo dava 3,9% e outro dava 36,4%, para o mesmo sinal. Uma diferença de nove vezes entre fontes que você mesmo escolheu não mede o seu motor. Mede o seu gosto para escolher fontes.

Então fui atrás de duas populações que ninguém aqui montou, rodei 1.728 documentos pelo motor e descobri que ele devolvia algo diferente de baixo em 68,5% dos PDFs publicados pelo governo americano e em 82,8% de uma amostra aleatória da web brasileira. Dois em cada cinco dos brasileiros voltaram como alto.

Todos esses documentos foram publicados pela própria organização que os produziu. Não havia nada de errado com nenhum deles.

Duas populações que eu não escolhi

A primeira é o “1000 .gov PDF dataset” da Biblioteca do Congresso americano, uma amostra aleatória em CC0 de PDFs em domínios .gov, tirada do arquivo web deles. Não foi selecionada por emissor, nem por tipo de documento, nem por mim. O metadata.csv publicado junto me deixou conferir exatamente o que me interessava antes de baixar um único byte: 244 strings /Producer distintas em 1000 arquivos, 144 delas aparecendo uma única vez, cobrindo de PDF 1.1 a 1.7 — Acrobat PDFWriter 3.02, Distiller 4.0, TCPDF, iText, libtiff, Word 2007. Seis fontes escolhidas a dedo não produzem essa variedade.

A segunda eu tive que construir. Não existe equivalente brasileiro do conjunto da LoC, então amostrei o índice do Common Crawl — blocos igualmente espaçados sob o prefixo SURT br,, que por inverter o nome do host cobre com.br, gov.br e edu.br juntos, em vez de uma fatia que eu teria que escolher. Deu 900 URLs em 421 hosts brasileiros distintos, 335 deles com.br.

Um detalhe de método que vale copiar

As URLs vêm do Common Crawl. Os bytes não. O Common Crawl trunca os corpos de resposta que guarda em cerca de 1 MB, e um PDF truncado faria este motor relatar um dano estrutural que o arquivo do publicador não tem — eu teria medido o meu próprio pipeline de corpus e chamado isso de achado. Cada documento foi buscado no servidor de origem que o publicou.

Chegaram 990 e 744 arquivos. 985 e 743 foram analisados, com zero falhas de parsing e zero códigos de aviso. Isso precisa ser dito antes de qualquer taxa abaixo: nada do que vem a seguir é artefato de arquivos que o parser não conseguiu ler.

E a limitação, dita aqui e não numa nota de rodapé: todo documento é presumido autêntico porque foi publicado pelo próprio emissor, então todo sinal acima de info conta como falso positivo. Um PDF do governo que tivesse mesmo sido alterado antes de ser publicado conta contra mim aqui. Essa presunção é a junta mais fraca do estudo inteiro.

A diferença nunca foi entre as fontes

Minha hipótese era que a diferença de 3,9% a 36,4% entre alvos fosse na verdade um efeito de produtor — que um corpus de extratos bancários não é especial por conter extratos, e sim porque aqueles bancos todos imprimem pelas mesmas duas ou três cadeias de ferramentas. Dentro de uma população, um país, um tipo de documento:

Família de produtorDocumentosAcima de baixoSinal principal
TCPDF135100,0%active-content (135)
Adobe PDF Library4195,1%id-inconsistency
iText4891,7%id-inconsistency
Microsoft Office3691,7%producer-fingerprint
Acrobat Distiller34774,4%id-inconsistency
scanner / multifuncional2240,9%id-inconsistency
Acrobat PDFWriter5639,3%producer-fingerprint
gerador de relatório2218,2%metadata-mismatch
conversor de imagem180,0%

De zero a cem por cento em catorze famílias. A diferença de nove vezes que eu tinha visto entre alvos era uma diferença entre produtores vestida de alvo. O TCPDF é o caso limpo: ele escreve um /OpenAction em todo arquivo que produz, então um sinal disparava em 100% do que ele gera e não carregava informação nenhuma.

Dois sinais estavam discutindo com o parser ao lado deles

O motor informa quantas revisões um PDF tem. É um fato estrutural, lido do arquivo: uma revisão significa que o documento foi escrito uma vez e nunca recebeu um acréscimo. Dois dos meus sinais estavam dizendo ao leitor que um documento tinha sido alterado depois de criado, em arquivos cuja contagem de revisões dizia que isso nunca aconteceu.

SinalSeveridadeTaxa EUAem arquivo de 1 revisãoTaxa BRem arquivo de 1 revisão
id-inconsistencymédia44,7%83,4%36,7%88,6%
font-anomaliesalta6,0%76,3%27,1%92,5%
incremental-updatesalta + média8,1%0,0%5,1%0,0%

Essa terceira linha é o controle, e é por causa dela que isso é um bug e não um limiar que eu calibrei mal. O incremental-updates é derivado da contagem de revisões, então nunca discorda dela. Os outros dois estavam inferindo uma segunda gravação a partir de um artefato que produtores comuns geram já na primeira.

O id-inconsistency lê o vetor /ID do trailer. A especificação diz que o primeiro elemento é fixado na criação e o segundo é reescrito a cada salvamento, então os dois diferirem é o registro da própria especificação de que houve um novo salvamento. É isso que produtores em conformidade fazem. O que os produtores fazem de verdade, em 44,7% dos PDFs publicados pelo governo americano, é gerar os dois elementos do zero já na primeira vez.

O font-anomalies era pior, porque disparava em alto — a banda que o meu próprio contrato de pontuação reserva para um sinal que sozinho estabelece uma alteração. A premissa dele: um gerador reúne todos os glifos de que precisa para uma tipografia num único subconjunto, então dois subconjuntos da mesma tipografia significam que glifos foram incorporados em duas ocasiões separadas. Um documento do corpus, com o achado que ele produziu:

produtor:    Acrobat Distiller 8.1.0 (Windows)
revisões:    1
severidade:  alta
achado:      Vários subconjuntos da mesma tipografia incorporados
             Calibri (FLJEPG+, FLJFAG+); Cambria-Bold (FLJEIC+, FLJEJC+);
             Cambria-Italic (FLJECP+, FLJEDA+)
             “…a assinatura deixada quando um texto é acrescentado ou
              substituído num PDF existente com outra ferramenta”
Uma revisão. O arquivo foi escrito uma vez, pelo Distiller, e nunca mais salvo — e o meu motor dizia ao leitor que um texto tinha sido acrescentado depois, na severidade mais alta que ele tem.

O Distiller emite vários subconjuntos de uma mesma tipografia numa única passagem por padrão. Juntar documentos também produz isso — e juntar documentos é o que o iLovePDF, o “combinar” do Word e o botão de anexar de um scanner fazem com documentos absolutamente honestos.

Um sinal não errou o limiar. Errou a premissa.

O producer-fingerprint era o maior sinal da população brasileira — 54,5% de 743 documentos, 82 deles em alto. A premissa dele, escrita no código: extratos, faturas e certificados são emitidos por bibliotecas do lado do servidor, então uma ferramenta de consumo na cadeia é um sinal.

Eis o que ele nomeava de verdade:

“Ferramenta de edição de consumo na cadeia de produção (…)”Documentos
Microsoft Word206
iLovePDF52
Microsoft: Print To PDF34
Adobe Illustrator34
Quartz PDFContext (macOS)23
Canva15
Google Docs Renderer14

Vinte e seis documentos do Microsoft Word foram pontuados como alto. O que sai do Canva voltou acima de baixo em 100% das vezes.

A premissa só se sustenta se você já sabe que o documento diz ser gerado por máquina. O meu motor não sabe o que está olhando. Ele recebe um arquivo. Numa população aberta a premissa se inverte por completo: uma ferramenta de consumo na cadeia é o que um documento comum tem, e um sinal que dispara no Word é um censo, não um achado.

A distinção que sobrevive ao contato com uma população real não é consumo versus servidor. É a origem — essa ferramenta criou o documento ou consumiu um que já existia? Word, Canva, Google Docs, Illustrator, um driver de impressão, um OCR sobre um escaneamento: a existência do arquivo começa neles, então nomeá-los não diz nada sobre o que mudou depois. iLovePDF, Sejda, PDF-XChange, o editor interativo do Acrobat: esses recebem um PDF existente como entrada e escrevem um novo. Isso é uma afirmação sobre a história do arquivo, e não sobre a autoria dele — e vale alguma coisa, embora ainda não seja prova, porque juntar, comprimir e preencher formulário são coisas que gente honesta faz com PDFs honestos todo dia.

Havia um segundo erro embaixo desse. O sinal subia para alto quando o documento também trazia imagem rasterizada de página inteira, na teoria de que isso corroborava uma edição. Na amostra brasileira essa regra sozinha foi responsável por 71 dos 82 disparos em alto — porque uma página rasterizada inteira é o que um design do Canva, uma exportação do Illustrator e uma página escaneada parecem. Ela marca um documento que é uma imagem. Não diz nada sobre a imagem ter sido editada.

E um relatava um índice de navegação como conteúdo ativo

O active-content pesava o /OpenAction igual a JavaScript embutido. Na amostra americana ele disparou 213 vezes: 189 desses eram um /OpenAction e mais nada, contra 9 de JavaScript de verdade. Uma chave respondia por 19,2% daquele corpus inteiro, enquanto conteúdo de fato executável ficava em 1,2% — o sinal real enterrado sob oito vezes o próprio volume em ruído.

O que torna esse caso limpo de resolver é que o argumento é estrutural, e não estatístico. O /OpenAction quase sempre carrega um destino — abrir na página 1, ajustar à janela — que é o que a maioria das ferramentas escreve por padrão. Quando ele carrega uma ação, esse dicionário traz /S /JavaScript ou /S /Launch, e a varredura de presença do parser já percorre subdicionários embutidos e marca essas flags a partir dele. Ou seja: o caso perigoso não consegue se apresentar como openAction sozinho, o que significa que o caso sozinho é comprovadamente navegação.

O que mudou, e o que isso valeu

Nada foi suprimido. As quatro famílias continuam emitindo os achados delas, com a evidência intacta — os prefixos de subconjunto conflitantes continuam listados, o par /ID divergente continua exibido, o /OpenAction continua relatado. Elas emitem em info, que pontua zero e fica fora da contagem de sinais. Quem revisa um documento juntado continua vendo as fontes. O documento é que deixa de receber uma banda forçada por uma inferência que a própria estrutura dele contradiz.

PopulaçãoAcima de baixoBanda alta
.gov dos EUA (985)68,5% → 18,6%11,8% → 7,4%
Web brasileira (743)82,8% → 30,0%38,2% → 10,0%

Esses números são uma projeção, não uma nova medição, e a diferença importa o bastante para ser dita com todas as letras. Os documentos originais foram apagados depois que os achados foram escritos — isso é uma regra permanente deste corpus, não uma conveniência — então a coluna “depois” vem de reexecutar a função de pontuação sobre os relatórios guardados com as severidades novas. A reexecução reproduz a banda atual do motor em 1.728 de 1.728 documentos, que é o que a torna confiável, e ainda assim não é a mesma coisa que rodar os arquivos de novo. Os dois manifestos de URL sobreviveram, então qualquer pessoa pode fazer isso.

O que eu não corrigi, e o que ainda não posso afirmar

O redaction-exposure dispara em 5,1% da população americana e em 17,0% da brasileira — texto ainda legível sob uma tarja desenhada, texto embaixo de uma imagem colocada por cima. Eu não consigo classificar esses casos. Tarja mal feita em documento público é um fenômeno real e documentado repetidas vezes, então uma fração desconhecida deles são acertos legítimos; e uma imagem colocada sobre um texto também é simplesmente como um timbre se assenta numa página. Resolver isso exige um humano olhando os documentos, que a regra de exclusão fez com que eu não tenha mais. É o único número deste estudo que não pode ser citado como taxa de falso positivo em nenhuma das duas direções.

Atualização, motor 1.19.0 (2026-08-04). Resolvido — e no fim não precisou de humano lendo documento. Os manifestos mantiveram cada arquivo rebaixável, então eu os recuperei (1.731 de 1.734 idênticos byte a byte) e perguntei ao renderizador em vez de ao leitor: renderize a página e olhe os pixels onde estaria o texto supostamente escondido. Texto sob uma tarja é um bloco chapado; texto que não está escondido tem o contraste de glifos. O meu próprio redaction-exposed.pdf dá 0,0 nas duas tarjas, e entre 1.693 trechos cobertos nada caiu entre 0,7 e 12,2 — o limiar é uma lacuna nos dados, não um número que eu escolhi.

Em 1.728 documentos publicados esta família não encontrou nenhuma tarja mal feita, e cerca de nove em cada dez disparos eram errados ou vazios. Duas causas: eu ignorava caminhos de recorte, então a borda de uma célula de tabela — desenhada recortando um anel e preenchendo a célula inteira — virava uma caixa opaca sobre o texto daquela célula; e um trecho coberto contendo apenas espaços era reportado como "texto ainda legível por baixo". Os dois estão corrigidos. 5,1% → 1,9% na população americana e 17,0% → 6,6% na brasileira, com o recall inalterado nas oito operações de falsificação. O que ainda dispara errado são recortes feitos de curvas em vez de retângulos, que eu não modelo.

E a lacuna maior: tudo acima é especificidade. Todo documento foi presumido autêntico, então isso mede com que frequência o motor erra sobre arquivos inocentes e não diz absolutamente nada sobre com que frequência ele acerta sobre os culpados. Não existe número de recall aqui porque não existe corpus público de documentos rotulados como adulterados — eu procurei. Produzir um significa sintetizá-lo: pegar documentos autênticos e aplicar edições conhecidas, com verdade de referência conhecida. Até isso existir, “alta precisão, recall desconhecido” é a posição honesta — e a metade da precisão só virou uma afirmação medida nesta semana.

Por que publicar isso

68% de falso positivo não é um bom número para colocar no blog do próprio produto. Mas a alternativa era seguir citando uma taxa calculada sobre documentos que eu mesmo escolhi — e o valor inteiro de uma ferramenta forense é que os achados dela signifiquem alguma coisa quando são inconvenientes.

A lição generalizável não é sobre PDF. É que um detector medido contra um corpus que o próprio autor montou está medindo o autor. Descobrir isso custou dois conjuntos de dados públicos, uma tarde de download e uma taxa quatro vezes pior do que eu acreditava. Não descobrir teria custado um cliente descobrindo primeiro, nos documentos dele, enquanto pagava por isso.

Rode em um arquivo seu

O verificador gratuito recebe um PDF e devolve o mesmo relatório que este texto descreve, com a evidência bruta embaixo de cada achado. Sem conta, sem guardar nada.

Se quiser ver a recalibração na prática, rode algo que você fez no Word ou exportou do Canva. Uma semana atrás, boa parte desses voltava como alto. Era esse o bug.