
Assembler un PDF soigné à partir de fichiers sources dispersés est une tâche routinière mais délicate : une page de couverture doit figurer au début d'un dossier de projet, les pages de tarification appartiennent à leur contrat, un résumé trimestriel rassemble les graphiques d'une douzaine de rapports. Faire cela manuellement signifie jongler avec plusieurs lecteurs PDF et espérer que l'ordre des pages soit correct, les tailles de page disparates aggravant le problème.
Spire.PDF for JavaScript déplace toute l'opération dans le navigateur. Propulsé par WebAssembly, il charge, manipule et enregistre des documents PDF entièrement côté client via un système de fichiers virtuel (VFS), ce qui signifie qu'aucun fichier n'est jamais téléchargé vers un serveur backend. Cet article présente quatre techniques distinctes pour copier des pages PDF entre documents — trois qui déplacent des pages entières et une qui extrait le contenu d'une page sous forme de modèle réutilisable — avec des exemples de code React complets pour chacune.
Pour la configuration du projet et les instructions d'installation, voir Intégration de Spire.PDF for JavaScript dans un projet React. Les exemples ci-dessous supposent que Spire.PDF est installé et que le module WebAssembly a été initialisé.
Quatre façons de copier des pages PDF en un coup d'œil
Avant d'examiner chaque méthode individuellement, le tableau ci-dessous fournit une comparaison rapide. Les trois premières techniques déplacent des pages intactes et reportent automatiquement les dimensions, la rotation et les marges de la page source. La quatrième dissocie le contenu de la géométrie de la page, vous donnant un contrôle total sur la taille de la page cible et la position de dessin.
| Méthode | Appel API | Ce qui est copié | Taille de page | Cas d'utilisation typique |
|---|---|---|---|---|
| Insérer une seule page | InsertPage |
Une page à une position de votre choix | Hérite de la source | Ajouter une page de couverture ou de titre au début |
| Insérer une plage de pages | InsertPageRange |
Un bloc consécutif de pages | Hérite de la source | Ajouter une section spécifique comme des tableaux de tarifs |
| Ajouter un document entier | AppendPage |
Chaque page du document source | Hérite de la source | Concaténer des documents complets de bout en bout |
| Dessiner le contenu d'une page comme modèle |
CreateTemplate + DrawTemplate
|
Contenu de page uniquement, dessiné sur n'importe quelle page | Vous décidez de la taille cible | Réutiliser le contenu sur différentes tailles de page ou le répéter plusieurs fois |
Les trois premières méthodes sont des déplacements de pages simples : choisissez la source, choisissez la destination, et la bibliothèque s'occupe du reste. L'approche par modèle est plus avancée et ouvre des possibilités que la simple copie de pages ne peut pas offrir, comme la mise à l'échelle du contenu pour l'adapter à une taille de page différente ou l'apposition du même contenu sur plusieurs pages. Nous aborderons d'abord les trois méthodes de déplacement de pages, puis nous explorerons en profondeur la technique du modèle.
Copier une seule page à une position spécifique
La plus précise des quatre méthodes, PdfDocument.InsertPage, copie une page d'un document source et la place à un index exact dans la cible. Le paramètre resultPageIndex contrôle l'emplacement de la copie : passez 0 pour la placer au début, passez le nombre actuel de pages de la cible pour l'ajouter à la fin, ou fournissez n'importe quel index intermédiaire pour l'insérer à cette position. Omettez complètement resultPageIndex et la page sera placée par défaut à la fin.
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;
Parmi les quatre méthodes de copie,
resultPageIndexest le seul paramètre qui vous permet de choisir le point d'insertion. Le définir à 0 place la page en premier, à 1 en deuxième, et passer le nombre actuel de pages du document cible produit le même effet qu'un ajout à la fin.
Le document cible passe de deux à trois pages, la première page du document source occupant désormais la position de tête :

Copier une plage de pages à la fin
Lorsque vous avez besoin de plus d'une page mais de moins qu'un document entier, PdfDocument.InsertPageRange copie un bloc contigu de pages défini par un index de début et de fin. Contrairement à InsertPage, cette méthode accepte des arguments positionnels plutôt qu'un objet d'options, et elle ajoute toujours les pages copiées à la fin de la cible — il n'y a aucun paramètre pour choisir la position d'insertion. L'index de fin est inclusif, donc passer (sourceDoc, 1, 2) copie les pages 2 et 3 (indexées à partir de zéro).
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;
Le document cible gagne deux pages supplémentaires, passant d'un total de deux à quatre :

Ajouter un document entier
Pour le cas le plus simple — déplacer toutes les pages d'un document dans un autre — PdfDocument.AppendPage élimine le besoin de calculer des index. Passez l'objet du document source et toutes ses pages sont ajoutées à la cible dans leur séquence d'origine. Pour concaténer plusieurs documents, appelez AppendPage de façon répétée avec chaque document source à tour de rôle.
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;
Les quatre pages du document source rejoignent la cible, la faisant passer de deux à six pages :

Copier le contenu d'une page avec un modèle
Les trois méthodes ci-dessus traitent une page comme une unité indivisible : elle se déplace avec sa taille, sa rotation et ses marges préservées. Mais l'assemblage de documents dans le monde réel exige souvent un contrôle plus fin — placer le contenu d'une page sur une page de taille différente, le mettre à l'échelle vers le haut ou vers le bas, ou apposer le même contenu sur plusieurs pages. C'est là qu'intervient PdfPageBase.CreateTemplate.
CreateTemplate extrait le contenu visuel d'une page dans un objet PdfTemplate. Vous dessinez ensuite ce modèle sur n'importe quelle page à l'aide de Canvas.DrawTemplate, en spécifiant la position et la taille de la zone de dessin. Le modèle est dissocié de la géométrie de la page d'origine, vous pouvez donc le rendre à n'importe quelle échelle, à n'importe quelle position, sur n'importe quelle taille de page — et vous pouvez dessiner le même modèle autant de fois que nécessaire.
Cela rend les modèles particulièrement utiles pour des scénarios tels que :
- Placer le contenu d'une couverture A5 centré sur une page A4 sans bordure blanche
- Créer un filigrane ou un motif d'arrière-plan à partir d'une page existante
- Dupliquer une mise en page de formulaire sur plusieurs nouvelles pages à différentes échelles
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;
Quelques détails à noter à propos de DrawTemplate :
- Argument de taille : Lorsque le troisième argument (taille cible) est omis, le modèle est rendu à ses dimensions d'origine sans mise à l'échelle. Sur une page cible plus grande, le contenu n'occupe qu'une partie de l'espace disponible.
-
Création de page : Les dimensions et marges de la page cible proviennent de
Pages.Insert, et non du modèle. Dans l'exemple, des marges nulles sur tous les côtés font coïncider l'origine du dessin avec le coin supérieur gauche de la page. -
Dessins multiples : Le même objet
templateest dessiné deux fois sur deux pages distinctes à des positions et échelles différentes, démontrant la capacité de réutilisation.
Le contenu de la page 1 apparaît maintenant sur deux pages A4 nouvellement insérées à différentes échelles et positions, faisant passer le document de quatre à six pages :

FAQ
La création d'une page avec new PdfMargins(0.0) lève Arg_NullReferenceException
Cause : Le constructeur PdfMargins interprète un argument numérique nu comme un handle interne plutôt qu'une valeur de marge. Appeler new pdfModule.PdfMargins(0.0) produit donc un objet qui ne représente pas des marges valides — accéder à sa propriété Left ou Top déclenche Arg_NullReferenceException, et le passer à la création de page donne des résultats inattendus.
Solution : Passez toujours les marges sous forme d'objet de configuration. Pour des marges nulles uniformes, utilisez { margin: 0.0 } ; pour des valeurs par côté individuelles, spécifiez chaque côté explicitement :
// 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 });
Une erreur de plage hors limites ou inversée est levée lors de la copie de pages
Cause : Les index de page sont basés sur zéro, et endIndex dans InsertPageRange est inclusif. La plage valide va donc de 0 à Pages.Count - 1. Fournir un index en dehors de cette plage déclenche Index out of range, tandis que définir startIndex supérieur à endIndex déclenche The start index is greater then the end index.
Solution : Protégez la borne supérieure en la limitant à Pages.Count avant d'appeler la méthode :
// 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);
Une page pivotée ressort avec une mauvaise orientation après la copie
Cause : CreateTemplate() capture le contenu dessiné de la page mais pas son angle de rotation (l'entrée /Rotate). Lorsque la page source porte une rotation, le système de coordonnées du modèle est désaligné avec la page cible — le dessiner directement place le contenu en dehors de la zone visible, et la copie résultante a une Rotation de 0.
Solution : Pour les pages sources pivotées, privilégiez une copie de page entière afin que l'angle de rotation accompagne le contenu :
// Whole-page copy: the rotation angle comes with the page
targetDoc.InsertPage({ ldDoc: sourceDoc, pageIndex: 0, resultPageIndex: 1 });
Si l'approche par modèle est inévitable, effacez temporairement la rotation de la page source avant d'extraire le modèle, puis restaurez l'angle d'origine sur la source et sur la nouvelle page :
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;
Pour supprimer le filigrane d'évaluation des documents de sortie ou débloquer l'accès complet aux fonctionnalités, contactez le service commercial pour une licence temporaire de 30 jours.