Retrouvez cet article dans : Linux Magazine 97
Depuis le numéro 59, les Mongueurs de Perl vous proposent tous les mois de découvrir les scripts jetables qu’ils ont pu coder ou découvrir dans leur utilisation quotidienne de Perl. Bref, des choses trop courtes pour en faire un article, mais suffisamment intéressantes pour mériter d’être publiées. Ce sont les perles de Mongueurs.
Supprimer une page d’un fichier PDF
C’est avec beaucoup d’intérêt que j’ai suivi la série Heroes (qui est passée cet été en France). La chaîne NBC, qui produit celle-ci, utilise beaucoup Internet pour garder vivace l’intérêt des spectateurs pendant les périodes de pause de la série (parfois plus d’un mois !). Ainsi, les blogs des personnages sont mis à jour, certains envoient même des emails aux spectateurs inscrits, etc.
La chaîne NBC propose également en téléchargement gratuit sur son site web des comic-books qui racontent des histoires de quelques pages qui se passent entre les épisodes de la série et rentrent dans les détails d’événements vus à la télévision.
Le premier numéro de cette bande dessinée annexe est disponible à l’adresse suivante : http://www.nbc.com/Heroes/novels/downloads/Heroes_novel_001.pdf. Vous devriez arriver à trouver les autres épisodes (il y en a plus de trente à l’heure où j’écris ces lignes) sans difficulté, si cela vous intéresse. ;-)
Pour ceux qui n’avaient pas remarqué en regardant la série, il y a un fabricant d’automobiles qui a sûrement beaucoup payé pour être cité dans quasiment chaque épisode... C’est également le cas du comic-book, qui commence par une énorme page de pub (beaucoup plus grosse que les pages du reste du PDF), ce qui gâche un peu la lecture (certains lecteurs ne sachant pas zoomer correctement pour afficher les pages suivantes au mieux).
Cette page de publicité me gêne et me gâche la lecture. Voici comment je m’en suis débarrassé.
PDF::API2
Le module PDF::API2 permet de créer, lire, manipuler et sauvegarder des fichiers PDF. La documentation est très succincte et ne suffit pas forcément pour arriver à produire ses premiers PDF. Le module a une interface d’assez bas niveau, et il vaut mieux avoir quelques connaissances de PostScript ou de PDF pour s’en servir au mieux.
Je vais créer un document PDF qui contient toutes les pages du PDF original, sauf la première. Afin de ressembler au document original, je vais de plus créer des bookmarks qui permettent d’accéder directement à chaque page.
Dans le script qui suit, seules quelques méthodes de PDF::API2 seront utilisées :
open()etnew(), qui permettent respectivement d’ouvrir un document PDF existant et d’en créer un vide.importpage(), qui permet d’importer une page d’un document dans un autre, en donnant le numéro de la page à importer et celui de la page à ajouter dans le nouveau document.info()etpreferences()permettent de manipuler les informations associées au document, et les préférences à l’ouverture.- La méthode
outlines()permet de créer un objet représentant les marque-pages (bookmarks) permettant d’accéder aux pages individuelles du document.PDF::API2ne semble pas donner accès aux marque-pages d’un document existant (ou, en tout cas, je n’ai pas trouvé comment faire). Nous nous contenterons donc de liens titrés « Page 1 », « Page 2 », etc.
L’objetoutlinescréé avec la méthodeoutlines()(au pluriel) contiendra les marque-pages créés avec la méthodeoutline(). Les paramètres de ces marque-pages sont définis avectitle()etdest(). end()permet de libérer la mémoire occupée par le document. C’est important ici, car le script permet de traiter des fichiers en boucle.
Voici le programme complet :
#!/usr/bin/env perl
use strict;
use warnings;
use PDF::API2;
use Getopt::Long;
my %conf;
GetOptions( \%conf, ‚verbose!‘, ‚clobber!‘ )
or die „Usage: remove_ad [ --verbose ] [ --clobber] file ...\n”;
# traite tous les fichiers passés sur la ligne de commande
for my $file (@ARGV) {
# ouvre l’ancien PDF et affiche le nombre de pages
my $old_pdf = PDF::API2->open($file);
print «$file: «, $old_pdf->pages - 1, « pages, « if $conf{verbose};
my $old_size = -s $file;
# ouvre le nouveau PDF
my $new_pdf = PDF::API2->new();
my $outlines = $new_pdf->outlines();
# copie les pages en boucle, à partir de la page 2
for my $page_num ( 2 .. $old_pdf->pages ) {
# importe la page à la fin du document
my $page = $new_pdf->importpage( $old_pdf, $page_num );
# ajoute un lien vers la page
my $outline = $outlines->outline();
$outline->title( «Page « . ( $page_num - 1 ) );
$outline->dest($page);
}
# copie la structure d’information (Author, etc)
$new_pdf->info( $old_pdf->info() );
# le lecteur ouvrira le document sur la première page
# qui remplira la page au mieux
$new_pdf->preferences(
-singlepage => 1,
-firstpage => [ $new_pdf->openpage(1), -fit => 1 ],
);
# sauve le nouveau fichier en écrasant l’ancien
$file =~ s/\.pdf$/-noad.pdf/ if !$conf{clobber};
$new_pdf->saveas($file);
# statistiques de gain de taille
print int( 100 * ( ( -s $file ) - $old_size ) / $old_size ), «%\n»
if $conf{verbose};
# détruit les objets en mémoire
$new_pdf->end();
$old_pdf->end();
}
J’en ai profité pour faire des calculs sur la différence de taille entre la version avec et sans publicité.
À l’exécution, cela donne :
$ ./remove_pdf_ad -v Heroes_novel_*.pdf
Heroes_novel_001.pdf: 6 pages, -14%
Heroes_novel_002.pdf: 6 pages, -21%
Heroes_novel_003.pdf: 6 pages, -10%
...
L’option --clobber que j’ai ajoutée permet d’écraser l’ancien fichier par le nouveau.
Bilan
La suppression de cette première publicité a permis un gain entre 15% et 30% sur la taille des divers PDF, ce qui est appréciable quand l’original pèse entre 10 et 20 mégaoctets (pour 6 à 9 pages).
Et la lecture est beaucoup plus agréable ! :-)


