
Montar um PDF refinado a partir de arquivos de origem dispersos é uma tarefa rotineira, mas delicada: uma página de capa precisa ficar na frente de um resumo de projeto, páginas de preços pertencem ao seu contrato, um resumo trimestral reúne gráficos de uma dúzia de relatórios. Fazer isso manualmente significa lidar com vários leitores de PDF e torcer para que a ordem das páginas saia correta, com tamanhos de página incompatíveis complicando ainda mais o problema.
O Spire.PDF for JavaScript transfere toda a operação para o navegador. Com o suporte do WebAssembly, ele carrega, manipula e salva documentos PDF inteiramente no lado do cliente por meio de um sistema de arquivos virtual (VFS), o que significa que nenhum arquivo é enviado a um servidor de backend. Este artigo apresenta quatro técnicas distintas para copiar páginas de PDF entre documentos — três que realocam páginas inteiras e uma que extrai o conteúdo da página como um template reutilizável — com exemplos completos de código React para cada uma.
Para instruções de configuração do projeto e instalação, consulte Integrating Spire.PDF for JavaScript in a React Project. Os exemplos abaixo pressupõem que o Spire.PDF esteja instalado e o módulo WebAssembly tenha sido inicializado.
Quatro Maneiras de Copiar Páginas de PDF em Resumo
Antes de examinar cada método individualmente, a tabela abaixo oferece uma comparação rápida. As três primeiras técnicas movem páginas intactas e transferem automaticamente as dimensões, a rotação e as margens da página de origem. A quarta desvincula o conteúdo da geometria da página, dando a você controle total sobre o tamanho da página de destino e a posição de desenho.
| Método | Chamada de API | O Que É Copiado | Tamanho da Página | Caso de Uso Típico |
|---|---|---|---|---|
| Inserir uma única página | InsertPage |
Uma página em uma posição que você escolher | Herdado da origem | Adicionar uma capa ou página de título na frente |
| Inserir um intervalo de páginas | InsertPageRange |
Um bloco consecutivo de páginas | Herdado da origem | Anexar uma seção específica, como tabelas de preços |
| Anexar um documento inteiro | AppendPage |
Todas as páginas do documento de origem | Herdado da origem | Concatenar documentos completos de ponta a ponta |
| Desenhar o conteúdo da página como template |
CreateTemplate + DrawTemplate
|
Apenas o conteúdo da página, desenhado em qualquer página | Você decide o tamanho de destino | Reutilizar conteúdo em tamanhos de página diferentes ou repeti-lo várias vezes |
Os três primeiros métodos são simples movimentações de páginas — escolha a origem, escolha o destino, e a biblioteca faz o resto. A abordagem com template é mais avançada e abre possibilidades que a simples cópia de páginas não consegue atender, como dimensionar o conteúdo para caber em um tamanho de página diferente ou carimbar o mesmo conteúdo em várias páginas. Abordaremos primeiro os três métodos de movimentação de páginas e, em seguida, exploraremos a técnica de template em profundidade.
Copiar uma Única Página para uma Posição Específica
O mais preciso dos quatro métodos, PdfDocument.InsertPage, copia uma página de um documento de origem e a coloca em um índice exato no destino. O parâmetro resultPageIndex controla onde a cópia é inserida: passe 0 para colocá-la no início, passe a contagem atual de páginas do destino para anexá-la, ou forneça qualquer índice intermediário para inseri-la nessa posição. Omita resultPageIndex completamente e a página será colocada no final por padrão.
function App() {
const copyPageAtPosition = async () => {
// Get the Spire.PDF WASM module
const pdfModule = window.wasmModule?.spirepdf;
// Check whether the module is ready
if (!pdfModule) {
alert('Spire.PDF is not ready yet');
return;
}
// Load both the source and the target document into the VFS
const sourceFileName = 'SourceDocument.pdf';
const targetFileName = 'TargetDocument.pdf';
await window.spire.FetchFileToVFS(sourceFileName, "", `${process.env.PUBLIC_URL}/data/`);
await window.spire.FetchFileToVFS(targetFileName, "", `${process.env.PUBLIC_URL}/data/`);
// Load the two documents
const sourceDoc = new pdfModule.PdfDocument();
sourceDoc.LoadFromFile(sourceFileName);
const targetDoc = new pdfModule.PdfDocument();
targetDoc.LoadFromFile(targetFileName);
// Copy page 1 of the source document to the front of the target document
// pageIndex comes from the source document, resultPageIndex is where the copy lands
targetDoc.InsertPage({ ldDoc: sourceDoc, pageIndex: 0, resultPageIndex: 0 });
// Save the result document
const outputFileName = 'CopyPageAtPosition.pdf';
targetDoc.SaveToFile(outputFileName);
sourceDoc.Close();
targetDoc.Close();
// Read the generated file from the VFS and trigger the download
const fileArray = window.dotnetRuntime.Module.FS.readFile(outputFileName);
const blob = new Blob([fileArray], { type: 'application/pdf' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = outputFileName;
a.click();
URL.revokeObjectURL(url);
};
return (
<div style={{ textAlign: 'center', height: '300px' }}>
<h1>Copy Page at Position</h1>
<button onClick={copyPageAtPosition}>
Start
</button>
</div>
);
}
export default App;
Entre todos os quatro métodos de cópia,
resultPageIndexé o único parâmetro que permite escolher o ponto de inserção. Defini-lo como 0 coloca a página em primeiro lugar, 1 a coloca em segundo, e passar a contagem atual de páginas do documento de destino produz o mesmo efeito que anexar.
O documento de destino cresce de duas para três páginas, com a primeira página do documento de origem agora ocupando a posição inicial:

Copiar um Intervalo de Páginas para o Final
Quando você precisa de mais de uma página, mas menos do que um documento inteiro, PdfDocument.InsertPageRange copia um bloco contíguo de páginas definido por um índice inicial e final. Diferentemente de InsertPage, este método aceita argumentos posicionais em vez de um objeto de opções, e sempre anexa as páginas copiadas ao final do destino — não há parâmetro para escolher a posição de inserção. O índice final é inclusivo, portanto passar (sourceDoc, 1, 2) copia as páginas 2 e 3 (com base zero).
function App() {
const appendPageRange = async () => {
// Get the Spire.PDF WASM module
const pdfModule = window.wasmModule?.spirepdf;
// Check whether the module is ready
if (!pdfModule) {
alert('Spire.PDF is not ready yet');
return;
}
// Load both the source and the target document into the VFS
const sourceFileName = 'SourceDocument.pdf';
const targetFileName = 'TargetDocument.pdf';
await window.spire.FetchFileToVFS(sourceFileName, "", `${process.env.PUBLIC_URL}/data/`);
await window.spire.FetchFileToVFS(targetFileName, "", `${process.env.PUBLIC_URL}/data/`);
// Load the two documents
const sourceDoc = new pdfModule.PdfDocument();
sourceDoc.LoadFromFile(sourceFileName);
const targetDoc = new pdfModule.PdfDocument();
targetDoc.LoadFromFile(targetFileName);
// Append pages 2 to 3 of the source document to the end of the target document
// Note: these are positional arguments, not an object; endIndex is inclusive
targetDoc.InsertPageRange(sourceDoc, 1, 2);
// Save the result document
const outputFileName = 'CopyPageRange.pdf';
targetDoc.SaveToFile(outputFileName);
sourceDoc.Close();
targetDoc.Close();
// Read the generated file from the VFS and trigger the download
const fileArray = window.dotnetRuntime.Module.FS.readFile(outputFileName);
const blob = new Blob([fileArray], { type: 'application/pdf' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = outputFileName;
a.click();
URL.revokeObjectURL(url);
};
return (
<div style={{ textAlign: 'center', height: '300px' }}>
<h1>Copy Page Range</h1>
<button onClick={appendPageRange}>
Copy pages 2-3
</button>
</div>
);
}
export default App;
O documento de destino ganha duas páginas adicionais, passando de um total de duas para quatro:

Anexar um Documento Inteiro
Para o caso mais simples — mover todas as páginas de um documento para outro — PdfDocument.AppendPage elimina a necessidade de calcular índices. Passe o objeto do documento de origem e todas as suas páginas serão anexadas ao destino em sua sequência original. Para concatenar vários documentos, chame AppendPage repetidamente com cada documento de origem, um por vez.
function App() {
const appendWholeDocument = async () => {
// Get the Spire.PDF WASM module
const pdfModule = window.wasmModule?.spirepdf;
// Check whether the module is ready
if (!pdfModule) {
alert('Spire.PDF is not ready yet');
return;
}
// Load both the source and the target document into the VFS
const sourceFileName = 'SourceDocument.pdf';
const targetFileName = 'TargetDocument.pdf';
await window.spire.FetchFileToVFS(sourceFileName, "", `${process.env.PUBLIC_URL}/data/`);
await window.spire.FetchFileToVFS(targetFileName, "", `${process.env.PUBLIC_URL}/data/`);
// Load the two documents
const sourceDoc = new pdfModule.PdfDocument();
sourceDoc.LoadFromFile(sourceFileName);
const targetDoc = new pdfModule.PdfDocument();
targetDoc.LoadFromFile(targetFileName);
// Use AppendPage when the whole document has to be copied; all pages are appended in order
targetDoc.AppendPage({ doc: sourceDoc });
// Save the result document
const outputFileName = 'CopyAllPages.pdf';
targetDoc.SaveToFile(outputFileName);
sourceDoc.Close();
targetDoc.Close();
// Read the generated file from the VFS and trigger the download
const fileArray = window.dotnetRuntime.Module.FS.readFile(outputFileName);
const blob = new Blob([fileArray], { type: 'application/pdf' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = outputFileName;
a.click();
URL.revokeObjectURL(url);
};
return (
<div style={{ textAlign: 'center', height: '300px' }}>
<h1>Copy Whole Document</h1>
<button onClick={appendWholeDocument}>
Start
</button>
</div>
);
}
export default App;
Todas as quatro páginas do documento de origem se juntam ao destino, expandindo-o de duas para seis páginas:

Copiar o Conteúdo da Página com um Template
Os três métodos acima tratam uma página como uma unidade indivisível: ela se move com seu tamanho, rotação e margens preservados. Mas a montagem de documentos no mundo real frequentemente exige um controle mais refinado — colocar o conteúdo de uma página em uma página de tamanho diferente, ampliá-lo ou reduzi-lo, ou carimbar o mesmo conteúdo em várias páginas. É aqui que PdfPageBase.CreateTemplate entra em cena.
CreateTemplate extrai o conteúdo visual de uma página para um objeto PdfTemplate. Em seguida, você desenha esse template em qualquer página usando Canvas.DrawTemplate, especificando a posição e o tamanho da área de desenho. O template é desvinculado da geometria da página original, então você pode renderizá-lo em qualquer escala, em qualquer posição, em qualquer tamanho de página — e pode desenhar o mesmo template quantas vezes precisar.
Isso torna os templates especialmente úteis para cenários como:
- Colocar o conteúdo de uma capa A5 centralizado em uma página A4 sem borda branca
- Criar uma marca d'água ou padrão de fundo a partir de uma página existente
- Duplicar o layout de um formulário em várias páginas novas, em escalas diferentes
function App() {
const copyPageWithTemplate = async () => {
// Get the Spire.PDF WASM module
const pdfModule = window.wasmModule?.spirepdf;
// Check whether the module is ready
if (!pdfModule) {
alert('Spire.PDF is not ready yet');
return;
}
// Load the PDF file to work on into the VFS
const inputFileName = 'SourceDocument.pdf';
await window.spire.FetchFileToVFS(inputFileName, "", `${process.env.PUBLIC_URL}/data/`);
// Load the document
const doc = new pdfModule.PdfDocument();
doc.LoadFromFile(inputFileName);
// Take the page to be reused and turn it into a template: read the content once, draw it many times
const sourcePage = doc.Pages.get_Item(0);
const template = sourcePage.CreateTemplate();
// First placement: insert an A4 page at position 2, a different size from the source,
// and draw the content scaled to 297.6 x 421.6 at (80, 80)
const page1 = doc.Pages.Insert(1, new pdfModule.SizeF(595.0, 842.0), new pdfModule.PdfMargins({ margin: 0.0 }));
page1.Canvas.DrawTemplate(template, new pdfModule.PointF(80.0, 80.0), new pdfModule.SizeF(297.6, 421.6));
// Second placement: insert another A4 page, drawing the same template smaller in the lower right
const page2 = doc.Pages.Insert(2, new pdfModule.SizeF(595.0, 842.0), new pdfModule.PdfMargins({ margin: 0.0 }));
page2.Canvas.DrawTemplate(template, new pdfModule.PointF(320.0, 460.0), new pdfModule.SizeF(200.0, 283.3));
// Save the result document
const outputFileName = 'CopyPageWithTemplate.pdf';
doc.SaveToFile(outputFileName);
doc.Close();
// Read the generated file from the VFS and trigger the download
const fileArray = window.dotnetRuntime.Module.FS.readFile(outputFileName);
const blob = new Blob([fileArray], { type: 'application/pdf' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = outputFileName;
a.click();
URL.revokeObjectURL(url);
};
return (
<div style={{ textAlign: 'center', height: '300px' }}>
<h1>Copy Page with Template</h1>
<button onClick={copyPageWithTemplate}>
Start
</button>
</div>
);
}
export default App;
Alguns detalhes que vale a pena observar sobre DrawTemplate:
- Argumento de tamanho: Quando o terceiro argumento (tamanho de destino) é omitido, o template é renderizado em suas dimensões originais, sem escala. Em uma página de destino maior, o conteúdo ocupa apenas uma parte do espaço disponível.
-
Criação de página: As dimensões e margens da página de destino vêm de
Pages.Insert, não do template. No exemplo, margens zero em todos os lados fazem a origem do desenho coincidir com o canto superior esquerdo da página. -
Múltiplos desenhos: O mesmo objeto
templateé desenhado duas vezes em duas páginas separadas, em posições e escalas diferentes, demonstrando a capacidade de reutilização.
O conteúdo da página 1 agora aparece em duas páginas A4 recém-inseridas, em escalas e posições diferentes, fazendo o documento crescer de quatro para seis páginas:

Perguntas Frequentes
Criar uma página com new PdfMargins(0.0) gera Arg_NullReferenceException
Causa: O construtor de PdfMargins interpreta um argumento numérico simples como um identificador interno, e não como um valor de margem. Portanto, chamar new pdfModule.PdfMargins(0.0) produz um objeto que não representa margens válidas — acessar sua propriedade Left ou Top dispara Arg_NullReferenceException, e passá-lo para a criação de página produz resultados inesperados.
Solução: Sempre passe as margens como um objeto de configuração. Para margens zero uniformes, use { margin: 0.0 }; para valores individuais por lado, especifique cada lado explicitamente:
// Zero margins on all four sides
const margins = new pdfModule.PdfMargins({ margin: 0.0 });
// Or set each side separately
const custom = new pdfModule.PdfMargins({ left: 20.0, top: 20.0, right: 20.0, bottom: 20.0 });
Um erro de intervalo fora dos limites ou invertido é lançado ao copiar páginas
Causa: Os índices de página são com base zero, e endIndex em InsertPageRange é inclusivo. O intervalo válido, portanto, vai de 0 a Pages.Count - 1. Fornecer um índice fora desse intervalo gera Index out of range, enquanto definir startIndex maior que endIndex gera The start index is greater then the end index.
Solução: Proteja o limite superior limitando-o em relação a Pages.Count antes de chamar o método:
// To copy pages 2 to 4: start = 1, end = 3, with the page count as the upper bound
const start = 1;
const end = Math.min(3, sourceDoc.Pages.Count - 1);
targetDoc.InsertPageRange(sourceDoc, start, end);
Uma página rotacionada sai com a orientação errada após a cópia
Causa: CreateTemplate() captura o conteúdo desenhado da página, mas não seu ângulo de rotação (a entrada /Rotate). Quando a página de origem possui uma rotação, o sistema de coordenadas do template fica desalinhado com a página de destino — desenhá-lo diretamente coloca o conteúdo fora da área visível, e a cópia resultante tem Rotation igual a 0.
Solução: Para páginas de origem rotacionadas, prefira uma cópia de página inteira para que o ângulo de rotação acompanhe o conteúdo:
// Whole-page copy: the rotation angle comes with the page
targetDoc.InsertPage({ ldDoc: sourceDoc, pageIndex: 0, resultPageIndex: 1 });
Se a abordagem com template for inevitável, limpe temporariamente a rotação da página de origem antes de extrair o template e depois restaure o ângulo original tanto na origem quanto na nova página:
const rotation = sourcePage.Rotation.value;
// Zero it temporarily so the template exports at the page's real coordinates
sourcePage.Rotation = 0;
const newPage = doc.Pages.Insert(1, sourcePage.Size, new pdfModule.PdfMargins({ margin: 0.0 }));
newPage.Canvas.DrawTemplate(sourcePage.CreateTemplate(), new pdfModule.PointF(0.0, 0.0));
// Restore the source page and give the copy the same angle
sourcePage.Rotation = rotation;
newPage.Rotation = rotation;
Para remover a marca d'água de avaliação dos documentos de saída ou desbloquear o acesso completo aos recursos, entre em contato com vendas para obter uma licença temporária de 30 dias.