Documenti di Didattica
Documenti di Professioni
Documenti di Cultura
Un po' di contesto
Il modulo in questione lo potete trovare su CPAN, consiglio una ricerca tipo PDF::Reuse. La descrizione
particolarmente illuminante, mi permetto di tradurla:
Questo modulo pu essere utilizzato quando volete produrre, in massa, documenti PDF simili ma non
identici fra loro e riutilizzare modelli, script JavaScript e qualche altra componente. progettato per essere
veloce, e per dare ai vostri programmi la capacit di produrre molte pagine al secondo e documenti PDF
molto grandi, se necessario.
Magari non abbiamo bisogno di andare tanto veloci, per l'idea del riutilizzo stuzzicante!
1 of 12
Flavio Poletti
#!/usr/bin/perl
use strict;
use warnings;
use PDF::Reuse;
5
6
my $ifile = shift;
my $reps = 1;
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
2 of 12
Flavio Poletti
prEnd();
Il primo blocco (righe 1-4) roba gi vista, vero? Si imposta l'interprete di default per Unix, si utilizzano strict e
warnings tanto per stare sicuri, e si utilizza PDF::Reuse perch.... altrimenti non andremo molto lontano.
Il primo argomento a linea di comando deve essere il nome del file PDF di partenza, per cui lo mettiamo in $ifile e
lo togliamo da @ARGV (il tutto alla riga 5), che a questo punto conterr solamente i vari comandi del tipo elencato in
precedenza. La variabile $reps mantiene il numero di ripetizioni globalmente impostato, e viene fatto partire da 1
(riga 6).
La generazione di un file PDF prevede l'apertura e la chiusura del file stesso, che pu essere mandato su STDOUT.
L'apertura viene effettuata utilizzando la funzione prFile (riga 9), la relativa chiusura - avete indovinato? - si ha con
prEnd (riga 48). In mezzo... scorre il fiume delle pagine!
La riga 13 suddivide i vari comandi impostati. Poich abbiamo detto che possiamo separare i campi con spazi e virgole,
filtriamo @ARGV utilizzando map, suddividendo ciascun argomento singolo in sotto-campi, considerando spazi e virgole
come separatori. In uscita dalla map ci ritroveremo dunque ciascun singolo comando, pronto per l'analisi.
Per generalit, lavoreremo sempre come se stessimo trattando un intervallo di pagine, anche se la prima e l'ultima
possono coincidere. In queste ipotesi, per default assumiamo che il comando sia un semplice numero di pagina, ed
impostiamo $start e $end al valore di $cmd stesso (riga 18). La variabile $itreps contiene il numero di ripetizioni
valido per questo comando (sempre riga 18), sar pi chiaro in seguito. Per default, tale numero di ripetizioni coincide
con quello globale.
Successivamente, andiamo a verificare se, per caso, l'utente ha specificato in realt altri tipi di comandi:
impostazione del fattore di ripetizione (controllo alla riga 21): in questo caso estraiamo il numero di ripetizioni, lo
salviamo in $reps e passiamo direttamente al comando successivo (next alla riga 23);
pagina ripetuta un determinato numero di volte (controllo alla riga 25): di nuovo, sia $start che $end
coincidono, ma viene impostato il valore di ripetizione per questa iterazione pari al prodotto fra il valore attuale e
quanto richiesto nel comando (riga 28);
range di pagine (controllo alla riga 30): in questo caso, estraggo i valori e li imposto in $start e $end (righe 31
e 32).
A questo punto sono pronto per generare pagine, ma faccio l'operazione in due tempi per maggiore leggibilit..
L'intervallo $start .. $end viene espanso (riga 37) mediante la map, che applica eventuali ripetizioni impostate per
il ciclo particolare (l'impostazione memorizzata in $itreps); il risultato di questa espansione viene impostato in
@pages, che conterr dunque la lista delle pagine da estrarre per il dato comando, opportunamente moltiplicate.
Successivamente si itera (riga 41) su tale intervallo, per poter estrarre la pagina dal file iniziale con la funzione prDoc
e stampare un feedback su STDERR (righe 42 e 43). Abbiamo finito - baster concludere la stampa del feedback,
chiudere il file PDF di uscita e terminare.
Prendi e manda!
Perl Mongers Italia. Tutti i diritti riservati.
3 of 12
Flavio Poletti
#!/usr/bin/perl
use strict;
use warnings;
use LWP::Simple;
use Mail::Sender;
use Archive::Zip;
7
8
9
# Un po' di configurazione
my $resource
= "http://www.example.com/resource.pdf";
my $smtp_server = '10.20.30.40';
10
11
12
13
# Prendi il documento
print "Prendo $resource\n";
my $content = LWP::Simple::get($resource)
or die "niente da scaricare, riprovare piu` tardi";
14
15
16
17
18
19
20
21
# Impacchetta
print "Impacchetto\n";
my $zip = Archive::Zip->new();
$zip->addString($content, "risorsa.pdf");
my $zipped;
{
open my $fh, ">", \$zipped;
die "impossibile impacchettare!"
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
4 of 12
Flavio Poletti
# concluderlo.
$sender->OpenMultipart()
or die "impossibile chiamare OpenMultipart()";
$sender->Body({msg => "Beh, che si dice?\n"});
$sender->Part(
{
description => "Rassegna stampa del $date",
ctype
=> 'application/x-zip-encoded',
encoding
=> 'Base64',
disposition =>
48
49
50
51
52
53
54
# Finito!
print "Fatto\n";
Il primo blocco (righe 1-6) dichiara le nostre intenzioni: fare le cose pulite (strict e warnings), ed utilizzare tanti bei
moduli disponibili. Dopo una prima fase di configurazione (righe 7-9), in particolare riguardante la risorsa da prendere
e il server SMTP da utilizzare per inviare l'e-mail, si entra nel vivo dello script.
La parte prendi si risolve in poche righe (dalla 10 alla 13): si utilizza LWP::Simple, che mette a disposizione
un'interfaccia minimale ma efficace per scaricare una risorsa su web. Se il download non va a buon fine (nel qual caso
la funzione LWP::Simple::get restituisce undef) si esce subito dallo script, con un messaggio di errore. Altrimenti... si
prosegue.
La parte impacchetta si avvale dei servigi di Archive::Zip. Tale modulo in grado di lavorare direttamente sui dati in
memoria, senza bisogno dunque di generare file temporanei. In questo caso abbiamo a che fare con un'interfaccia
orientata agli oggetti, per cui ci facciamo generare un oggetto (riga 16) e chiamiamo il metodo di aggiunta di un
elemento in memoria all'archivio Zip in costruzione mediante il metodo addString (riga 17), impostando anche il nome
che tale sequenza deve avere come "file" nell'archivio ("risorsa.pdf" nel nostro caso).
Il blocco di codice successivo (righe 18-23) realizza un piccolo inganno per Archive::Zip, che abitutato a scrivere su
file gli archivi compressi. Si apre dunque un file con open (riga 20), ma invece di un vero file nel filesystem si apre, in
realt,, una variabile scalare utilizzata come destinazione, ossia $zipped. Tale possibilit si ha solamente dalla versione
5.8 di Perl, quindi attenzione! Il resto della "scrittura su file" procede come di consueto. Anche qui, se qualcosa va
storto si esce immediatamente con un messaggio di errore.
A questo punto la variabile $zipped contiene il file compresso, come se l'avessimo appena letto da un file vero e
proprio. Siamo pronti alla spedizione, che si avvale - l'avreste mai detto? - di un'interfaccia ad oggetti. Questa volta il
costruttore richiede un po' pi di parametri (righe 26-34); niente di trascendentale come si pu vedere, con la solita
interruzione se qualcosa va male.
Per spedire un'e-mail con un messaggio ed un allegato potremmo utilizzare il metodo MailFile, che ci consente di fare
tutto in un colpo solo; in questo caso, per, vogliamo avere un controllo pi stretto sull'header relativo al file da
spedire, per cui scegliamo la strada pi lunga costruendo il messaggio pezzo per pezzo.
Utilizziamo allora OpenMultipart (riga 39), che imposta appunto il messaggio come composto da pi parti. Il corpo del
messaggio impostato alla riga successiva con il metodo Body (ok, in questo caso ce lo potevamo evitare, visto il
Perl Mongers Italia. Tutti i diritti riservati.
5 of 12
Flavio Poletti
Quello che segue si avvale di tutta la potenza di LWP::Simple (per scaricare la pagina degli spettacoli), e di
HTML::TableExtract per analizzare il file scaricato senza doversi destreggiare troppo nell'infida terra del parsing HTML.
1
2
3
4
5
#!/usr/bin/perl
use strict;
use warnings;
use HTML::TableExtract;
use LWP::Simple;
6 of 12
Flavio Poletti
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
L'inizio il solito (righe 1-5): utilizziamo strict e warnings perch siamo coscienziosi, ed includiamo anche i due moduli
che ci permetteranno di scrivere lo script senza troppe ansie. Alla riga 7 viene impostata l'URL di base del servizio di
Virgilio, che viene poi personalizzata alla riga 9, dove viene anche impostata la citt da analizzare. Il download viene
effettuato mediante il metodo get (riga 11), importato automaticamente da LWP::Simple; questo restituisce undef se
qualche cosa va storto, nel qual caso lo script terminer con un messaggio di errore.
A questo punto la pagina scaricata si trova nella variabile $html. Il modulo HTML::TableExtract mette a disposizione
un'interfaccia ad oggetti, per cui prima di tutto dobbiamo procurarcene uno (riga 15), per poi seguire i passi necessari
all'analisi della tabella che ci interessa. Si noti che, nel costruttore dell'oggetto, vengono impostate le condizioni di
filtraggio: avendo notato che la tabella che ci interessa contiene l'attributo di classe 'listaElenco', indichiamo tale
restrizione in modo da eliminare le altre tabelle (possibilmente) presenti nella pagina.
7 of 12
Flavio Poletti
Vietato ai minori
C' un modulo su CPAN che andrebbe vietato ai minori. Prima che vi prendiate a gomitate per raggiungere il browser
pi vicino e dare fondo alla vostra fantasia per cercare roba tipo Free::Nude su CPAN fatemi andare avanti, potreste
perdere il vostro tempo!
Ricorderete che a scuola, specialmente alle elementari ed alle medie, la parola calcolatrice era stata bandita dal
vocabolario di un qualsiasi insegnante di matematica. La ragione semplice: se devi imparare a fare i conti, ossia se
devi imparare come si fanno i conti e soprattutto perch si fanno in quel modo, avere la pappa pronta in una
macchinetta che sa sempre la risposta giusta non aiuta molto. Quando hai capito, invece, puoi benissimo permetterti di
dimenticare tutto ed usarla!
Con la scrittura credo debba essere uguale: dare delle scorciatoie prima che certi concetti siano ben radicati pu essere
molto, molto pericoloso. E qui veniamo al modulo proibito, quello che mette ordine nel testo al posto nostro:
Text::Beautify. I minori sono gentilmente pregati di tornare alle grammatiche ed alle antologie, grazie.
Text::Beautify consente di eliminare quelle noiosissime violazioni delle regole di buona scrittura che rendono alcuni
testi tanto noiosi da leggere. Se aprite un qualsiasi libro stampato (o quasi), potete infatti notare che vengono
rispettate queste regole semplicissime:
i periodi iniziano con la lettera maiuscola (chi ha voglia di premere quel dannato tasto?);
prima di un carattere di interpunzione (s, la punteggiatura) non va mai messo uno spazio...
... ma dopo si (stiamo scherzando? Non ho tempo da perdere!!!);
Perl Mongers Italia. Tutti i diritti riservati.
8 of 12
Flavio Poletti
#!/usr/bin/perl
use strict;
use warnings;
use Text::Beautify qw( beautify );
5
6
7
8
my $text = "
qualcosa che non
print "Originale:
'$text'\n";
$text = beautify($text);
print "Trasformato: '$text'\n";
Lo script di esempio di una banalit sconvolgente. Da notare che, in fase di utilizzo del modulo (riga 4), viene
specificato il parametro beautify, che ha l'effetto di importare la funzione e renderla direttamente visibile alla riga 7.
Se non avessimo specificato il parametro, visto che l'autore del modulo (Jos Alves de Castro, anche noto come cog) fa
le cose per bene, non avremmo avuto modifiche sul nostro spazio dei nomi ed avremmo dovuto cambiare la riga 7 in:
7bis
$text = Text::Beautify::beautify($text);
Niente di sconvolgente, ma in uno script cos semplice possiamo permetterci di importare la funzione senza temere di
incappare in collisioni.
L'uscita semplicemente:
Originale:
'
qualcosa
che non
Comodo vero? Ma che non sia una scusa per scrivere male!
E se...
... volessi mettere a posto la punteggiatura, ma lasciare le lettere di inizio periodo cos come sono? O tenere le
punteggiature multiple?
Non sono richieste cos campate in aria, mi rendo conto; fortunatamente se n' reso conto anche cog, che ha messo a
disposizione la possibilit di disabilitare le caratteristiche che non interessano. Una rapida occhiata al manuale (che,
ricordo, accessibile con il comando perldoc Text::Beautify dopo che si installato il modulo) ci indica subito
la strada: utilizzare la funzione disable_feature().
1
2
3
#!/usr/bin/perl
use strict;
use warnings;
9 of 12
Flavio Poletti
5
6
7
8
my $errato = beautify($brutto);
print "Errato: '$errato'\n";
9
10
11
va scritto sempre
minuscolo ! ";
disable_feature('uppercase_first');
my $bello = beautify($brutto);
print "Bello : '$bello'\n";
La riga 4 non ci sorprende: poich abbiamo bisogno di un'altra funzione da quel modulo, indichiamo che vogliamo
importarla nel nostro spazio dei nomi.
Alla riga 7 utilizziamo beautify() esattamente come prima, ma il risultato che otteniamo errato, perch la parola
cog viene trasformata con l'iniziale maiuscola. qui che si innesta la funzione disable_feature(): alla riga 9 si disabilita
la trasformazione in maiuscola della prima lettera del periodo, con buona pace di cog. Il risultato finale :
Brutto: ' cog ,sapete,, va scritto sempre
minuscolo ! '
Errato: 'Cog, sapete, va scritto sempre minuscolo!'
Bello : 'cog, sapete, va scritto sempre minuscolo!'
#!/usr/bin/perl -T
use strict;
use warnings;
use CGI;
use File::Basename qw( basename );
use Readonly;
7
8
9
10
# Configuration
Readonly my $fieldname
=> 'uploaded_file';
Readonly my $base_directory => "/percorso/per/upload";
Readonly my $base_url
=> "http://mio.server.it/upload";
11
my $q = CGI->new();
12
13
my $msg;
$msg = gestisci_upload_entrante() if $q->param('uploaded_file');
10 of 12
Flavio Poletti
print $q->header(),
$q->start_html('Upload semplice semplice');
print $q->h1($msg), $q->hr() if $msg;
print $q->h1("Upload"),
$q->start_multipart_form(),
'File: ', $q->filefield(-name => $fieldname), $q->br(),
$q->submit(-name => 'Invia'),
$q->end_form(),
$q->end_html();
23
24
sub gestisci_upload_entrante {
chdir $base_directory or return "chdir(): $!";
25
26
27
28
29
30
31
32
33
my $fn = basename($q->param($fieldname))
or return "nessun nome di file";
$fn =~ s/[^\w\d .-]//g;
($fn) = $fn =~ /([\w\d .-]+)/;
return "Errore: nessun nome di file valido" unless $fn;
my @alphabeth = ('a' .. 'z');
while (-e $fn) {
$fn = $alphabeth[rand @alphabeth] . $fn;
}
34
35
36
37
38
39
my $fh = $q->upload($fieldname)
or return "Problemi nell'upload di '$fn'";
binmode $fh;
open my $out, '>', $fn or return "open(): $!";
binmode $out;
while (read $fh, my $data, 8192) { print {$out} $data }
40
41
L'inizio (righe 1..6) pi o meno il solito: usiamo strict, warnings ed i moduli CGI, visto che stiamo realizzando
uno script CGI, e File::Basename, che ci sar utile per estrarre il nome del file da un path completo.
Avete fatto caso all'opzione -T sulla prima riga? Serve ad attivare il cosiddetto taint mode, ossia una modalit un po'
paranoica che considera tutto quello che viene dall'esterno dello script come potenzialmente pericoloso, e quindi
impedisce che questi dati siano utilizzati in determinate funzioni (come, per fare un esempio, open() nella riga 37).
Questa modalit non vi impedisce di fare stupidaggini, ovviamente, ma vi costringe a prestare una particolare
attenzione ai dati che provengono dal browser, impedendovi di abbassare la guardia.
Le righe 7..10 riportano alcune configurazioni: in particolare, il campo nel form CGI che servir per l'upload verr
chiamato uploaded_file, i file verranno messi tutti dentro la directory /percorso/per/upload e questi saranno
poi visibili attraverso la URL base http://mio.server.it/upload.
Per evitare di spargere queste stringhe in giro per lo script, correndo il rischio di sbagliare qualcosa, ho deciso di
utilizzare delle variabili. L'approccio migliore, in questi casi, consiste nell'utilizzare un sistema che forzi l'utilizzo di
queste variabili come costanti; usiamo qui il modulo Readonly, che fa s che le tre variabili $fieldname,
$base_directory e $base_url non possano essere (involontariamente o meno) modificate.
La riga 11 dichiara e definisce $q, la variabile che verr utilizzata in tutto lo script per la gestione dell'interfaccia CGI.
La riga 13 chiama la funzione gestisci_upload_entrante() nel caso sia definito il parametro CGI in ingresso
uploaded_file; in questo modo, saremo in grado di utilizzare questo script sia per presentare una pagina per
Perl Mongers Italia. Tutti i diritti riservati.
11 of 12
Flavio Poletti
12 of 12