Création et modification de fichiers PDF en Python – Real Python

By | mai 25, 2020

Python pas cher

Jusqu'à présent, vous avez appris à extraire du texte et des pages de fichiers PDF et à concaténer et fusionner deux fichiers PDF ou plus. Ce sont toutes des opérations courantes avec les PDF, mais PyPDF2 possède de nombreuses autres fonctionnalités utiles.

Dans cette section, vous apprendrez comment faire pivoter et rogner des pages dans un fichier PDF.

Rotation des pages

Vous allez commencer par apprendre à faire pivoter les pages. Pour cet exemple, vous utiliserez le ugly.pdf fichier dans le practice_files dossier. le ugly.pdf fichier contient une belle version de Hans Christian Andersen Le vilain petit canard, sauf que chaque page impaire est tournée de quatre-vingt-dix degrés dans le sens antihoraire.

Corrigeons ça. Dans une nouvelle fenêtre interactive IDLE, commencez par importer le PdfFileReader et PdfFileWriter cours de PyPDF2, aussi bien que Chemin classe du pathlib module:

>>>

>>> de pathlib importation Chemin
>>> de PyPDF2 importation PdfFileReader, PdfFileWriter

Créez maintenant un Chemin objet pour le ugly.pdf fichier:

>>>

>>> pdf_path = (
...     Chemin.maison()
...     / "création et modification de pdfs"
...     / "practice_files"
...     / "ugly.pdf"
... )

Enfin, créez de nouveaux PdfFileReader et PdfFileWriter instances:

>>>

>>> lecteur PDF = PdfFileReader(str(pdf_path))
>>> pdf_writer = PdfFileWriter()

Votre objectif est d'utiliser pdf_writer pour créer un nouveau fichier PDF dans lequel toutes les pages ont la bonne orientation. Les pages paires du PDF sont déjà correctement orientées, mais les pages impaires tournent dans le sens antihoraire de quatre-vingt-dix degrés.

Pour corriger le problème, vous utiliserez PageObject.rotateClockwise (). Cette méthode prend un argument entier, en degrés, et fait pivoter une page dans le sens horaire de ce nombre de degrés. Par exemple, .rotateClockwise (90) fait pivoter une page PDF de 90 degrés dans le sens des aiguilles d'une montre.

Il existe plusieurs façons de faire tourner les pages du PDF. Nous allons discuter de deux façons différentes de le faire. Les deux comptent sur .le sens des aiguilles d'une montre(), mais ils adoptent différentes approches pour déterminer les pages à faire pivoter.

La première technique consiste à parcourir les index des pages du PDF et à vérifier si chaque index correspond à une page qui doit être tournée. Si oui, alors vous appellerez .le sens des aiguilles d'une montre() pour faire pivoter la page, puis ajoutez-la à pdf_writer.

Voici à quoi cela ressemble:

>>>

>>> pour n dans gamme(lecteur PDF.getNumPages()):
...     page = lecteur PDF.getPage(n)
...     si n % 2 == 0:
...         page.le sens des aiguilles d'une montre(90)
...     pdf_writer.ajouter une page(page)
...

Notez que la page pivote si l'index est pair. Cela peut sembler étrange car les pages impaires du PDF sont celles qui tournent incorrectement. Cependant, les numéros de page dans le PDF commencent par 1, tandis que les index de page commencent par 0. Cela signifie que les pages PDF impaires ont des indices pairs.

Si cela vous fait tourner la tête, ne vous inquiétez pas! Même après des années à gérer des trucs comme celui-ci, les programmeurs professionnels se font toujours tromper par ce genre de choses!

Maintenant que vous avez fait pivoter toutes les pages du PDF, vous pouvez écrire le contenu de pdf_writer dans un nouveau fichier et vérifiez que tout fonctionne:

>>>

>>> avec Chemin("ugly_rotated.pdf").ouvert(mode="wb") comme fichier de sortie:
...     pdf_writer.écrire(fichier de sortie)
...

Vous devriez maintenant avoir un fichier dans votre répertoire de travail actuel appelé ugly_rotated.pdf, avec les pages du ugly.pdf fichier tout tourné correctement.

Le problème avec l'approche que vous venez d'utiliser pour faire pivoter les pages dans le ugly.pdf fichier est qu'il dépend de savoir à l'avance quelles pages doivent être tournées. Dans un scénario réel, il n'est pas pratique de parcourir un PDF entier en prenant note des pages à faire pivoter.

En fait, vous pouvez déterminer quelles pages doivent être tournées sans connaissance préalable. Bien, quelquefois vous pouvez.

Voyons comment, en commençant par un nouveau PdfFileReader exemple:

>>>

>>> lecteur PDF = PdfFileReader(str(pdf_path))

Vous devez le faire car vous avez modifié les pages de l'ancien PdfFileReader par exemple en les faisant tourner. Donc, en créant une nouvelle instance, vous recommencez.

PageObject les instances gèrent un dictionnaire de valeurs contenant des informations sur la page:

>>>

>>> lecteur PDF.getPage(0)
'/Contenu':[IndirectObject(110)IndirectObject(120)[IndirectObject(110)IndirectObject(120)[IndirectObject(110)IndirectObject(120)[IndirectObject(110)IndirectObject(120)
IndirectObject (13, 0), IndirectObject (14, 0), IndirectObject (15, 0),
IndirectObject (16, 0), IndirectObject (17, 0), IndirectObject (18, 0)],
'/ Rotate': -90, '/ Resources': '/ ColorSpace': '/ CS1':
IndirectObject (19, 0), '/ CS0': IndirectObject (19, 0), '/ XObject':
'/ Im0': IndirectObject (21, 0), '/ Font': '/ TT1':
IndirectObject (23, 0), '/ TT0': IndirectObject (25, 0), '/ ExtGState':
'/ GS0': IndirectObject (27, 0), '/ CropBox': [0, 0, 612, 792],
'/ Parent': IndirectObject (1, 0), '/ MediaBox': [0, 0, 612, 792],
'/ Type': '/ Page', '/ StructParents': 0

Oui! Mélangé avec tout ce qui n'a pas de sens est une clé appelée /Tourner, que vous pouvez voir sur la quatrième ligne de sortie ci-dessus. La valeur de cette clé est -90.

Vous pouvez accéder au /Tourner clé sur un PageObject en utilisant la notation en indice, comme vous pouvez le faire sur un Python dicter objet:

>>>

>>> page = lecteur PDF.getPage(0)
>>> page[[[["/Tourner"]
-90

Si vous regardez le /Tourner clé pour la deuxième page lecteur PDF, vous verrez qu'il a une valeur de 0:

>>>

>>> page = lecteur PDF.getPage(1)
>>> page[[[["/Tourner"]
0

Tout cela signifie que la page à l'index 0 a une valeur de rotation de -90 degrés. En d'autres termes, il a été tourné de 90 degrés dans le sens antihoraire. La page à l'index 1 a une valeur de rotation de 0, il n'a donc pas du tout été tourné.

Si vous faites pivoter la première page à l'aide de .le sens des aiguilles d'une montre(), alors la valeur de /Tourner changements depuis -90 à 0:

>>>

>>> page = lecteur PDF.getPage(0)
>>> page[[[["/Tourner"]
-90
>>> page.le sens des aiguilles d'une montre(90)
>>> page[[[["/Tourner"]
0

Maintenant que vous savez comment inspecter /Tourner , vous pouvez l'utiliser pour faire pivoter les pages ugly.pdf fichier.

La première chose que vous devez faire est de réinitialiser votre lecteur PDF et pdf_writer des objets afin de prendre un nouveau départ:

>>>

>>> lecteur PDF = PdfFileReader(str(pdf_path))
>>> pdf_writer = PdfFileWriter()

Maintenant, écrivez une boucle qui boucle sur les pages du pdf_reader.pages itérable, vérifie la valeur de /Tourneret fait pivoter la page si cette valeur est -90:

>>>

>>> pour page dans lecteur PDF.pages:
...     si page[[[["/Tourner"] == -90:
...         page.le sens des aiguilles d'une montre(90)
...     pdf_writer.ajouter une page(page)
...

Non seulement cette boucle est légèrement plus courte que la boucle de la première solution, mais elle ne repose sur aucune connaissance préalable des pages à faire pivoter. Vous pouvez utiliser une boucle comme celle-ci pour faire pivoter des pages dans n'importe quel PDF sans jamais avoir à l'ouvrir et à le regarder.

Pour terminer la solution, écrivez le contenu de pdf_writer vers un nouveau fichier:

>>>

>>> avec Chemin("ugly_rotated2.pdf").ouvert(mode="wb") comme fichier de sortie:
...     pdf_writer.écrire(fichier de sortie)
...

Vous pouvez maintenant ouvrir le ugly_rotated2.pdf fichier dans votre répertoire de travail actuel et le comparer au ugly_rotated.pdf fichier que vous avez généré précédemment. Ils devraient être identiques.

La valeur de /Tourner peut ne pas toujours être ce que vous attendez. Par exemple, si vous numérisez un document papier avec la page tournée de quatre-vingt-dix degrés dans le sens antihoraire, le contenu du PDF apparaîtra pivoté. Cependant, le /Tourner la clé peut avoir la valeur 0.

C'est l'une des nombreuses bizarreries qui peuvent rendre frustrant le travail avec les fichiers PDF. Parfois, il vous suffit d'ouvrir un PDF dans un programme de lecture de PDF et de comprendre manuellement les choses.

Pages de recadrage

Une autre opération courante avec les PDF est le recadrage des pages. Vous devrez peut-être effectuer cette opération pour diviser une seule page en plusieurs pages ou pour extraire uniquement une petite partie d'une page, telle qu'une signature ou une figure.

Par exemple, le practice_files dossier comprend un fichier appelé half_and_half.pdf. Ce fichier PDF contient une partie du texte de Hans Christian Andersen La petite Sirène.

Chaque page de ce PDF comporte deux colonnes. Divisons chaque page en deux pages, une pour chaque colonne.

Pour commencer, importez le PdfFileReader et PdfFileWriter cours de PyPDF2 et le Chemin classe du pathlib module:

>>>

>>> de pathlib importation Chemin
>>> de PyPDF2 importation PdfFileReader, PdfFileWriter

Créez maintenant un Chemin objet pour le half_and_half.pdf fichier:

>>>

>>> pdf_path = (
...     Chemin.maison()
...     / "création et modification de pdfs"
...     / "practice_files"
...     / "half_and_half.pdf"
... )

Ensuite, créez un nouveau PdfFileReader objet et obtenez la première page du PDF:

>>>

>>> lecteur PDF = PdfFileReader(str(pdf_path))
>>> première page = lecteur PDF.getPage(0)

Pour recadrer la page, vous devez d'abord en savoir un peu plus sur la structure des pages. PageObject des exemples comme première page avoir un .mediaBox attribut qui représente une zone rectangulaire définissant les limites de la page.

Vous pouvez utiliser la fenêtre interactive d'IDLE pour explorer la .mediaBox avant de l'utiliser recadrez la page:

>>>

>>> première page.mediaBox
RectangleObject ([0, 0, 792, 612])

le .mediaBox l'attribut renvoie un RectangleObject. Cet objet est défini dans le PyPDF2 package et représente une zone rectangulaire sur la page.

La liste [0, 0, 792, 612] dans la sortie définit la zone rectangulaire. Les deux premiers nombres sont les coordonnées x et y du coin inférieur gauche du rectangle. Les troisième et quatrième chiffres représentent respectivement la largeur et la hauteur du rectangle. Les unités de toutes les valeurs sont des points, qui sont égaux à 1/72 de pouce.

RectangleObject ([0, 0, 792, 612]) représente une région rectangulaire avec le coin inférieur gauche à l'origine, une largeur de 792 points, ou 11 pouces, et une hauteur de 612 points, ou 8,5 pouces. Ce sont les dimensions d'une page au format lettre standard en orientation paysage, qui est utilisée pour l'exemple PDF de La petite Sirène. Une page PDF de format lettre en orientation portrait renverrait la sortie RectangleObject ([0, 0, 612, 792]).

UNE RectangleObject possède quatre attributs qui renvoient les coordonnées des coins du rectangle: .en bas à gauche, .en bas à droite, .en haut à gauche, et .En haut à droite. Tout comme les valeurs de largeur et de hauteur, ces coordonnées sont données en points.

Vous pouvez utiliser ces quatre propriétés pour obtenir les coordonnées de chaque coin de la RectangleObject:

>>>

>>> première page.mediaBox.en bas à gauche
(0, 0)
>>> première page.mediaBox.en bas à droite
(792, 0)
>>> première page.mediaBox.en haut à gauche
(0, 612)
>>> première page.mediaBox.En haut à droite
(792, 612)

Chaque propriété renvoie un tuple contenant les coordonnées du coin spécifié. Vous pouvez accéder aux coordonnées individuelles avec des crochets comme vous le feriez pour tout autre tuple Python:

>>>

>>> première page.mediaBox.En haut à droite[[[[0]
792
>>> première page.mediaBox.En haut à droite[[[[1]
612

Vous pouvez modifier les coordonnées d'un mediaBox en affectant un nouveau tuple à l'une de ses propriétés:

>>>

>>> première page.mediaBox.en haut à gauche = (0, 480)
>>> première page.mediaBox.en haut à gauche
(0, 480)

Lorsque vous modifiez le .en haut à gauche les coordonnées, .En haut à droite l'attribut s'ajuste automatiquement pour conserver une forme rectangulaire:

>>>

>>> première page.mediaBox.En haut à droite
(792, 480)

Lorsque vous modifiez les coordonnées du RectangleObject retourné par .mediaBox, vous recadrez efficacement la page. le première page L'objet contient désormais uniquement les informations présentes dans les limites du nouveau RectangleObject.

Allez-y et écrivez la page recadrée dans un nouveau fichier PDF:

>>>

>>> pdf_writer = PdfFileWriter()
>>> pdf_writer.ajouter une page(première page)
>>> avec Chemin("cropped_page.pdf").ouvert(mode="wb") comme fichier de sortie:
...     pdf_writer.écrire(fichier de sortie)
...

Si vous ouvrez le cropped_page.pdf dans votre répertoire de travail actuel, vous verrez que la partie supérieure de la page a été supprimée.

Comment recadreriez-vous la page pour que seul le texte sur le côté gauche de la page soit visible? Vous devrez réduire de moitié les dimensions horizontales de la page. Vous pouvez y parvenir en modifiant la .En haut à droite coordonnées du .mediaBox objet. Voyons comment cela fonctionne.

Tout d'abord, vous devez obtenir de nouveaux PdfFileReader et PdfFileWriter objets puisque vous venez de modifier la première page de lecteur PDF et l'a ajouté à pdf_writer:

>>>

>>> lecteur PDF = PdfFileReader(str(pdf_path))
>>> pdf_writer = PdfFileWriter()

Obtenez maintenant la première page du PDF:

>>>

>>> première page = lecteur PDF.getPage(0)

Cette fois, travaillons avec une copie de la première page afin que la page que vous venez d'extraire reste intacte. Vous pouvez le faire en important le copie module de la bibliothèque standard de Python et en utilisant copie profonde () pour faire une copie de la page:

>>>

>>> importation copie
>>> côté gauche = copie.copie profonde(première page)

Vous pouvez maintenant modifier côté gauche sans changer les propriétés de première page. De cette façon, vous pouvez utiliser première page plus tard pour extraire le texte sur le côté droit de la page.

Maintenant, vous devez faire un peu de calcul. Vous avez déjà compris que vous devez déplacer le coin supérieur droit du .mediaBox en haut au centre de la page. Pour ce faire, vous allez créer un nouveau tuple avec le premier composant égal à la moitié de la valeur d'origine et l'affecter à la .En haut à droite propriété.

Tout d'abord, obtenez les coordonnées actuelles du coin supérieur droit du .mediaBox.

>>>

>>> current_coords = côté gauche.mediaBox.En haut à droite

Ensuite, créez un nouveau tuple dont la première coordonnée est la moitié de la valeur de la coordonnée actuelle et la deuxième coordonnée est la même que l'original:

>>>

>>> new_coords = (current_coords[[[[0] / 2, current_coords[[[[1])

Enfin, affectez les nouvelles coordonnées au .En haut à droite propriété:

>>>

>>> côté gauche.mediaBox.En haut à droite = new_coords

Vous avez maintenant recadré la page d'origine pour ne contenir que le texte sur le côté gauche! Extrayons ensuite le côté droit de la page.

Obtenez d'abord une nouvelle copie de première page:

>>>

>>> côté droit = copie.copie profonde(première page)

Bouge le .en haut à gauche coin au lieu du .En haut à droite coin:

>>>

>>> côté droit.mediaBox.en haut à gauche = new_coords

Cela définit le coin supérieur gauche sur les mêmes coordonnées que vous avez déplacé le coin supérieur droit lors de l'extraction du côté gauche de la page. Donc, right_side.mediaBox est maintenant un rectangle dont le coin supérieur gauche est en haut au centre de la page et dont le coin supérieur droit est en haut à droite de la page.

Enfin, ajoutez le côté gauche et côté droit pages à pdf_writer et écrivez-les dans un nouveau fichier PDF:

>>>

>>> pdf_writer.ajouter une page(côté gauche)
>>> pdf_writer.ajouter une page(côté droit)
>>> avec Chemin("cropped_pages.pdf").ouvert(mode="wb") comme fichier de sortie:
...     pdf_writer.écrire(fichier de sortie)
...

Maintenant, ouvrez le cropped_pages.pdf fichier avec un lecteur PDF. Vous devriez voir un fichier avec deux pages, la première contenant le texte du côté gauche de la première page d'origine et la seconde contenant le texte du côté droit d'origine.

Vérifie ta compréhension

Développez le bloc ci-dessous pour vérifier votre compréhension:

dans le practice_files / dossier dans le référentiel compagnon de cet article, il existe un fichier appelé split_and_rotate.pdf.

Créez un nouveau fichier appelé rotated.pdf dans le répertoire personnel de votre ordinateur qui contient toutes les pages de split_and_rotate.pdf, mais chacun est tourné de 90 degrés dans le sens antihoraire.

Vous pouvez développer le bloc ci-dessous pour voir une solution:

Configurez le chemin d'accès au fichier PDF:

# Tout d'abord, importez les classes et bibliothèques nécessaires
de pathlib importation Chemin
de PyPDF2 importation PdfFileReader

# Ensuite, créez un objet `Path` dans le fichier PDF.
# Vous devrez peut-être modifier cela pour correspondre au chemin
# sur ton ordinateur.
pdf_path = (
    Chemin.maison()
    / "création et modification de pdfs"
    / "practice_files"
    / "split_and_rotate.pdf"
)

Vous pouvez maintenant créer PdfFileReader et PdfFileWriter instances:

lecteur PDF = PdfFileReader(str(pdf_path))
pdf_writer = PdfFileWriter()

Faites une boucle sur les pages lecteur PDF, tournez-les tous de 90 degrés en utilisant .rotateCounterClockwise ()et ajoutez-les à pdf_writer:

pour page dans lecteur PDF.pages:
    rotated_page = page.RotateCounterClockwise(90)
    pdf_writer.ajouter une page(rotated_page)

Enfin, écrivez le contenu de pdf_writer dans un fichier appelé rotated.pdf dans le répertoire personnel de votre ordinateur:

chemin_sortie = Chemin.maison() / "rotated.pdf"
avec chemin_sortie.ouvert(mode="wb") comme fichier de sortie:
    pdf_writer.écrire(fichier de sortie)