Ir para o conteúdo
5 min de leitura

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.

ItemO que acontece na cópiaComo tratar
Texto, desenhos e imagens da páginaSão copiados junto com a páginaConfira a primeira e a última página do resultado
Marcadores e sumário lateralNão entram no documento novoRecrie-os ou gere o arquivo com outra ferramenta
Metadados como autor e títuloFicam no documento de origemDefina com setTitle e setAuthor, ou limpe no programa de origem
Camadas ocultas e configuração de camadasTendem a não vir juntoConfira no leitor se o conteúdo oculto continua oculto
Peso das imagensNão é reduzidoComprima 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

  1. Compare o número de páginas do arquivo final com a soma das origens, usando getPageCount em cada documento.
  2. 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.
  3. 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.
  4. 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.