Copier et réutiliser des pages PDF entre documents avec JavaScript

2026-09-28 08:36:18 Allen Yang
AI Summarize:
ChatGPT
ChatGPT ✓
Claude ✓
Grok ✓
Perplexity ✓
Quick
Quick
Concise overview
Highlights
Key takeaways
Detailed
Structured explanation
Brief
One sentence summary
Summarize |

Copy and reuse PDF pages across documents with JavaScript

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, resultPageIndex est 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 :

Page 1 of the source document now sits in front of the target document, which goes from 2 pages to 3


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 :

After pages 2 and 3 of the source document are appended, the document has 4 pages


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 :

After all 4 pages of the source document are appended, the document has 6 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 template est 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 :

The content of page 1 in the source document is scaled onto two new A4 pages, taking the document from 4 pages to 6


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.


Voir aussi