PDF em Node.js com pdf-lib: juntar, girar e dividir
Como juntar, girar e dividir PDFs em Node.js com a biblioteca pdf-lib, com código pronto, o que ela copia e o que perde, e quando usar a API.
Para juntar PDFs em Node.js, instale a biblioteca com npm install pdf-lib, carregue cada arquivo com PDFDocument.load, copie as páginas para um documento novo com copyPages e grave o resultado com save. O mesmo padrão gira páginas e divide intervalos. A biblioteca não recomprime imagens, e os marcadores do documento original não vêm junto.
Instalar e carregar um PDF
O pdf-lib funciona com ou sem TypeScript. Ele não precisa de programa instalado no sistema, o que simplifica o deploy em containers e em funções serverless. Cada arquivo é lido como bytes e carregado em memória, então um lote com PDFs de centenas de megabytes vai pesar no processo.
npm install pdf-lib
import { PDFDocument } from 'pdf-lib';
import { readFile } from 'node:fs/promises';
const bytes = await readFile('contrato.pdf');
const documento = await PDFDocument.load(bytes);
console.log(documento.getPageCount());Juntar vários arquivos na ordem certa
A ordem das páginas no arquivo final é a ordem em que os arquivos são lidos. Um laço sobre a lista de caminhos resolve isso: cada arquivo tem todas as páginas copiadas para o fim do documento de destino, na sequência.
import { PDFDocument } from 'pdf-lib';
import { readFile, writeFile } from 'node:fs/promises';
async function juntar(arquivos, saida) {
const destino = await PDFDocument.create();
for (const caminho of arquivos) {
const origem = await PDFDocument.load(await readFile(caminho));
const paginas = await destino.copyPages(origem, origem.getPageIndices());
paginas.forEach((pagina) => destino.addPage(pagina));
}
await writeFile(saida, await destino.save());
}
await juntar(['contrato.pdf', 'anexo-1.pdf', 'anexo-2.pdf'], 'pacote.pdf');Esse código não confere o tamanho dos arquivos nem a quantidade de páginas. Antes de chamar a função, compare o total com o limite que vai receber o arquivo. O AjustaPDF, por exemplo, aceita 25 MB por arquivo sem conta e 200 MB por arquivo no plano Pro.
Girar páginas
Girar uma página é trocar o valor do atributo de rotação dela. O pdf-lib faz isso com setRotation, recebendo um ângulo múltiplo de 90 graus. O conteúdo da página não é redesenhado, por isso a operação é rápida.
import { PDFDocument, degrees } from 'pdf-lib';
const documento = await PDFDocument.load(bytes);
const primeira = documento.getPage(0);
primeira.setRotation(degrees(90));
await writeFile('girado.pdf', await documento.save());A página de índice 0 é a primeira. Se um PDF tem uma única página de cabeça para baixo, descubra o índice dela e gire apenas essa posição. Para uma lista de páginas, repita a chamada para cada índice.
Dividir em intervalos
Para extrair um intervalo de páginas, crie um documento vazio e copie só os índices pedidos. Atenção à contagem: o pdf-lib começa em zero. Para a página 1 a 3, o índice é 0, 1 e 2. A sintaxe do site, como 1-3,7,10-z, usa números de página a partir de 1 e z para a última página, então a conversão fica por sua conta no código.
async function extrair(origem, indices, saida) {
const destino = await PDFDocument.create();
const paginas = await destino.copyPages(origem, indices);
paginas.forEach((pagina) => destino.addPage(pagina));
await writeFile(saida, await destino.save());
}
await extrair(documento, [0, 1, 2], 'parte-1.pdf');
await extrair(documento, [3, 4, 5, 6], 'parte-2.pdf');Para partir um arquivo em partes de tamanho fixo, como 40 páginas por vez, calcule os intervalos antes e chame a função em laço. Esse é o mesmo raciocínio de quem usa o dividir PDF do AjustaPDF uma vez por parte.
O que a cópia traz e o que deixa para trás
Copiar páginas não é o mesmo que copiar o arquivo. Parte do conteúdo fica no documento original e não entra no documento novo. A tabela resume o que esperar ao usar copyPages.
| Item | O que acontece na cópia | Como tratar |
|---|---|---|
| Texto, desenhos e imagens da página | São copiados junto com a página | Confira a primeira e a última página do resultado |
| Marcadores e sumário lateral | Não entram no documento novo | Recrie-os ou gere o arquivo com outra ferramenta |
| Metadados como autor e título | Ficam no documento de origem | Defina com setTitle e setAuthor, ou limpe no programa de origem |
| Camadas ocultas e configuração de camadas | Tendem a não vir junto | Confira no leitor se o conteúdo oculto continua oculto |
| Peso das imagens | Não é reduzido | Comprima depois, com o Ghostscript ou o site |
A mesma regra aparece no AjustaPDF. Juntar ali usa qpdf, que copia páginas e o que pertence a cada uma. O guia sobre links e sumário ao juntar PDF mostra o que sobrevive e o que precisa ser refeito, e vale para o código tanto quanto para o site.
Onde o pdf-lib é a escolha certa
A biblioteca serve bem para montar documentos gerados pelo próprio sistema: recibos, boletos em lote, certificados e relatórios. Nesses casos o PDF nasce no código, sem marcadores complexos, e a falta de compressão não pesa. Também é um bom caminho para aplicar uma marca d’água em lote, que o AjustaPDF faz com a mesma base de pdf-lib.
Onde ela não resolve
O pdf-lib não faz OCR, não edita o texto de um PDF pronto, não assina digitalmente e não gera PDF/A. Um arquivo danificado pode falhar ao carregar. Nesse caso, abra o original em um leitor de PDF antes de culpar o código, porque o erro costuma estar no arquivo.
Para um pipeline com muita compressão e muitos arquivos grandes, o caminho costuma ser outro: Ghostscript e qpdf rodando na mesma máquina, como no guia de compressão no terminal, ou Python, com o pypdf e o pikepdf, que cobrem parte do que o pdf-lib não cobre.
Quando chamar a API
Se o seu código só precisa de uma etapa pontual, como comprimir um anexo antes de um envio, chamar uma API costuma ser mais simples que manter a biblioteca. A API de PDF do AjustaPDF recebe o arquivo por POST e devolve o PDF processado. Ela serve para testes e uso leve, porque a cota do plano sem conta é pequena, e o arquivo sai do seu sistema para o servidor.
Como conferir o resultado
- Compare o número de páginas do arquivo final com a soma das origens, usando getPageCount em cada documento.
- Abra o arquivo em um leitor de PDF e percorra a primeira e a última página de cada parte, porque erro de ordem costuma aparecer nas bordas.
- Confira o tamanho do arquivo gerado. Se ele pesa muito mais que os originais, há recursos repetidos, e a comparação ajuda a achar a causa.
- Se o arquivo vai para um portal, verifique o limite dele antes da entrega, porque o código não sabe qual é o limite do destino.
Para conferir o que sobrou no arquivo sem abrir cada página, o comando pdfinfo, do pacote Poppler, lista o número de páginas e os metadados. Rode-o no arquivo gerado depois de cada mudança no código, e o problema aparece antes de chegar ao usuário.
Perguntas frequentes
O pdf-lib consegue juntar PDFs sem instalar nada no servidor?
Sim. O pdf-lib é uma biblioteca JavaScript pura, instalada com npm, sem programa externo como o Ghostscript ou o qpdf. Por isso ela roda em qualquer ambiente onde o Node.js roda. A contrapartida é que ela não recomprime imagens, então a junção fica do tamanho da soma dos arquivos.
Por que os marcadores do PDF somem depois de juntar com pdf-lib?
Porque o copyPages copia as páginas e o que pertence a cada uma, não o que é do documento inteiro. Marcadores, configuração de camadas e metadados do arquivo de origem não entram no documento novo. Recrie os marcadores com a API do pdf-lib ou no próprio leitor depois de gerar o arquivo.
Posso comprimir um PDF só com pdf-lib?
Não de forma útil. Ele não reamostra imagens existentes, e reduzir o peso de fotos exige reescrever o arquivo com outra ferramenta. Para isso, use o Ghostscript, como mostra o guia de compressão no terminal, ou a ferramenta de comprimir PDF do site.
O pdf-lib abre PDF protegido por senha?
Depende da senha. Um arquivo que pede senha para abrir não é lido sem a senha, e o pdf-lib não quebra senhas. Um arquivo com senha só de permissão pode ser lido com a opção ignoreEncryption. Para o caso de senha de abertura, desbloqueie o arquivo antes com a senha certa.
Como usar a API do AjustaPDF em vez de escrever o código?
Faça um POST multipart para https://api.ajustapdf.com/pdf/compress (ou outro caminho /pdf/, como /pdf/merge), com o campo file, e receba o PDF de volta. Não há chave de API, e a cota do plano sem conta é de 3 operações por dia. Para volume maior, rode as ferramentas na sua infraestrutura.