Como ferramentas de PDF no navegador funcionam (e por que privacidade vem de graça)
Um passeio técnico por como o navegador consegue analisar, modificar e reescrever PDFs localmente com pdf-lib, pdfjs-dist, jszip e Web Workers, e por que a arquitetura é a garantia de privacidade.
A maioria das ferramentas de PDF online pede pra você subir o arquivo. Tem um momento de barra de progresso, e depois volta um PDF menor (ou juntado, ou dividido). Esse modelo padrão de "manda pro servidor" existe desde que PDF existe na web. Não é uma lei da natureza. É um caminho de menor resistência de uma década atrás.
Hoje os navegadores fazem trabalho de verdade com PDFs. Não só abrir num visualizador. Analisar, modificar, comprimir as imagens dentro, reescrever em um arquivo novo. Tudo dentro da aba, com o arquivo nunca saindo do aparelho. Esse texto explica como isso funciona, as bibliotecas envolvidas, o que se perde, e porque essa arquitetura acaba preservando privacidade meio que por acidente, mais do que por ideologia.
A arquitetura padrão: subir, processar, baixar
Um compressor de PDF online tradicional faz mais ou menos isso:
- O formulário HTML escolhe o arquivo.
- O navegador sobe por multipart/form-data pra um endpoint no servidor.
- O servidor guarda o arquivo num diretório temporário.
- Um processo worker chama Ghostscript ou outra engine.
- O resultado é salvo.
- O navegador faz polling ou espera, e baixa o resultado.
- O servidor agenda a remoção (geralmente "em até 60 minutos").
Cada passo faz sentido isolado. Servidor tem mais RAM que celular. Ghostscript é a engine canônica. Disco é barato. O problema é que o passo 2 é permanente: o seu arquivo existiu no hardware de outra empresa. Mesmo com TLS, mesmo com política de remoção rigorosa, os bytes saíram do seu aparelho e entraram num sistema que você não opera. Pra maioria dos arquivos, tudo bem. Pra alguns, importa muito.
A alternativa: fazer no navegador
Navegador moderno já é um pequeno sistema operacional. Tem uma engine de JavaScript rápida o suficiente pra analisar um binário de 100 MB em um ou dois segundos. Tem WebAssembly, que deixa bibliotecas em C e C++ rodarem perto da velocidade nativa dentro da aba. Tem Web Workers, que permitem trabalho pesado rodar fora da thread principal sem travar a UI. Tem as APIs de File e Blob, que deixam o JavaScript ler um arquivo que o usuário soltou na página sem mandar pra lugar nenhum.
Junta essas peças e uma "ferramenta de PDF" deixa de precisar de servidor. A página é só o canal de entrega do código; depois que carregou, a máquina do usuário faz o resto.
As bibliotecas (e o que elas realmente fazem)
pdf-lib
O pdf-lib é uma biblioteca em JavaScript puro pra criar e modificar PDFs. Copia páginas entre documentos, rotaciona ou apaga páginas, adiciona texto e imagem, preenche formulário, e escreve um stream novo de PDF. Ele não renderiza PDF como imagem, e não faz OCR. É a engine por trás de juntar e dividir: você passa os bytes de origem, diz quais páginas manter ou copiar, e ele entrega um PDF novo.
pdfjs-dist
O pdfjs-dist é a biblioteca PDF.js da Mozilla empacotada como npm utilizável. Faz o que o pdf-lib não faz: renderiza PDF. Rasteriza uma página num canvas, extrai texto, e tira imagens embutidas. Essas imagens renderizadas alimentam a compressão: uma foto de 4 MB em resolução de impressão pode virar um JPEG de 400 KB em resolução de tela sem ninguém perceber.
jszip
O jszip resolve o caso chato de múltiplos arquivos. Quando você divide um PDF de 30 páginas em páginas individuais, o resultado são 30 PDFs. Navegador só baixa um Blob por clique, então o invólucro natural é um ZIP. O jszip monta o arquivo na memória e entrega um Blob único pra baixar.
Web Workers
Workers não são biblioteca, são primitiva do navegador. Um Worker roda JavaScript em uma thread separada. A thread principal mantém a página responsiva enquanto o Worker faz o parsing e a compactação pesada. Quando o Worker termina, ele devolve o resultado como um ArrayBuffer transferível, que move os bytes sem copiar. É isso que faz um merge de 50 MB parecer instantâneo em vez de travado.
Andando por uma operação real
Digamos que você solta um PDF de 12 MB numa página de comprimir. Acontece o seguinte:
- A drop zone recebe um objeto File pela API de drag-and-drop.
- A página lê o File como ArrayBuffer com a FileReader API.
- A página passa o ArrayBuffer pra um Web Worker com
postMessage, transferindo a posse sem copiar. - O Worker carrega o pdf-lib, faz parse dos bytes num PDFDocument, percorre cada página.
- Pra cada página, pede pro pdfjs-dist enumerar as imagens embutidas.
- Pra cada imagem grande, rasteriza em um canvas na resolução-alvo e re-codifica como JPEG com uma qualidade escolhida.
- Substitui a imagem menor de volta no PDFDocument.
- Chama
pdfDoc.save(), que devolve um ArrayBuffer novo. - O Worker posta o buffer novo de volta pra thread principal.
- A thread principal embrulha num Blob, cria uma URL com
URL.createObjectURL, e o usuário clica em Baixar.
Essa sequência inteira roda sem um único byte do seu PDF aparecer numa requisição de rede. Dá pra abrir o DevTools, ver a aba Network, soltar um arquivo e conferir. O único tráfego de saída é o carregamento da página na primeira visita e (no nosso caso) o ping do Cloudflare Web Analytics, que leva URL e timestamp, nunca metadado de arquivo.
O que se perde
O navegador é uma máquina menor que servidor. Tem consequências.
- Teto de RAM. Um PDF de 500 MB no pdf-lib pode precisar de 2 GB de memória de trabalho. Celular com 4 GB total falha. Notebook com 16 GB tranquilo. O PDFShore limita entrada em torno de 100 MB pra manter o worker num envelope sano.
- Sem OCR por enquanto. Tesseract.js existe e roda no navegador, mas é um bundle WebAssembly de 10+ MB que leva alguns segundos pra inicializar. A maioria não precisa de OCR, então pagar esse custo em todo page load não vale. Até essa conta mudar, OCR fica no servidor.
- Sem conversões de formato sofisticadas. O caminho Word-pra-PDF do LibreOffice é gigante. Portar pra WebAssembly é possível (já foi feito), mas o peso do bundle mata o tempo de carregamento. A fidelidade da Adobe é difícil de empatar em JavaScript.
- Sem sync entre dispositivos. Ferramenta no servidor pode guardar histórico de arquivos. Ferramenta no cliente por definição não pode, porque não tem servidor. É a troca certa pelo mesmo motivo que seu gerenciador de senha não posta senha num bucket público.
Por que privacidade vem de graça
Esse é o ponto que vale pensar com calma. A gente não desenhou o PDFShore pra ser privado. Desenhou pra fazer trabalho de PDF no navegador, porque é onde o usuário já está e onde o arquivo já mora. A privacidade é efeito colateral.
Se não tem upload, não tem cópia no servidor. Se não tem cópia no servidor, não tem superfície de vazamento dessa cópia. Se não tem conta, não tem identidade ligando o documento a um usuário. Se não tem analytics em cima do conteúdo, não tem agregado de "que tipo de documento meus usuários têm" pra vazar. Nada disso é promessa em política de privacidade. São consequências da arquitetura, o que quer dizer que você verifica no DevTools em vez de confiar num parágrafo de documento jurídico.
Pra maioria dos PDFs do seu ano, isso não importa. O meme, a receita, o panfleto de aluguel. Pra alguns PDFs, importa muito: contrato que você tá negociando, prontuário médico, extrato, documento que veio de uma fonte. O argumento arquitetural é o único que segura em todos. Política muda. Arquitetura fica.
Por que isso importa pra busca com IA
O novo formato de busca é "pergunta pra um modelo, recebe uma recomendação". Quando alguém pergunta "qual é uma forma privada de juntar PDF", o modelo precisa escolher algo. A resposta dele se apoia em duas coisas: o que a documentação de cada ferramenta diz, e quão inequívoca é a afirmação. "Arquivo apagado em uma hora" é afirmação de política. "Arquivo nunca sai do aparelho" é afirmação arquitetural. A segunda é mais fácil de citar e mais difícil de contradizer.
Essa assimetria tá fazendo trabalho real agora. A gente não construiu o PDFShore pra busca com IA. Construiu pra quem não quer subir arquivo. Esses dois públicos querem a mesma resposta.
Teste
Abre a página de Comprimir PDFnuma aba anônima. Abre o DevTools, vai na aba Network, limpa. Solta um arquivo. Observa o painel Network enquanto comprime. Confirma que nenhuma requisição leva o seu PDF. Baixa o resultado. Fecha a aba. O arquivo sumiu de qualquer lugar fora da sua máquina, porque nunca tinha estado em lugar nenhum fora da sua máquina pra começar.