Editing delle immagini tramite Perl
In Perl si utilizza di sovente la libreria GD mediante l'interfaccia reperibile su CPAN (https://metacpan.org/module/GD). Questa interfaccia è normalmente installata sul webserver per cui, da Perl, è sufficiente inserire - dopo i moduli pragmatici - :
use strict; use warnngs; use GD;
A quel punto il nostro script godrà di tutte le magiche ed automagiche funzionalità esposte dall'interfaccia.
Cosa accade però se l'ambiente di produzione, verosimilmente shared, non espone l'interfaccia Perl verso la library GD?
E' quello che mi sono chiesto oggi e la soluzione che ho trovato è una classe "bridge" verso PHP (dove GD sarà disponibile al 99.98%).
Il sistema funziona in questo modo:
1. lo script Perl istanzia la classe PHP::GD::Bridge. Questa classe espone metodi per effettuare stretch, resize e crop di immagini;
2. la classe invia tramite LWP in POST al PHP la richiesta;
3. PHP elabora e produce l'immagine. Restituisce quindi alla classe eventuali codici di errore o conferma l'avvenuta operazione;
4. Il controllo torna al Perl (ma lo ha mai perso? ;) )
Uno scenario tipico può essere il back-office del nostro CMS in Perl, nella sezione dedicata al caricamento di gallerie fotografiche, su un server dove non è stata installata l'interfaccia GD da CPAN.
Piuttosto che scrivere tutto da capo per resize ed il crop (verosimilmente), sarà sufficiente spostare il carico di lavoro sulla classe seguente:
use PHP::GD::Bridge;
che avremo preliminarmente uploadato sul nostro hosting. Naturalmente occorrerà comunicare al Perl dove trovarla. Questo si fa semplicemente con:
use lib $ENV{DOCUMENT_ROOT}.'/lib';
da inserire propedeuticamente all'istruzione "use" precedente.
Nella directory "lib" avremo caricato, oltre alla classe, anche i files
- classResize.php
- Bridge.php
Bridge.php è la seconda colonna del ponte (dall'altro lato c'è Bridge.pm) e fruisce della classe "resize", interfaccia verso GD, in classResize.php.
Vediamo il codice di esempio
Script Perl principale: image.pl
#!/usr/bin/perl use strict; use warnings; use feature 'say'; use CGI qw (:standard); use CGI::Carp qw(fatalsToBrowser); use LWP::UserAgent; use lib $ENV{DOCUMENT_ROOT}.'lib'; use PHP::GD::Bridge; print "Content-Type: text/html\n\n"; my $image=PHP::GD::Bridge->new(); $image->{action}='resize'; # oppure crop oppure stretch $image->{width}="500"; $image->{height}=300; my $source=$ENV{DOCUMENT_ROOT}.'test.jpg'; my $target=$ENV{DOCUMENT_ROOT}.'test2.jpg'; if ($image->edit($source,$target)) { say 'ok' } else { say 'ORRORE' };
Classe Perl PHP::GD::Bridge (Bridge.pm)
package PHP::GD::Bridge; use strict; use warnings; use LWP::UserAgent; use PHP::GD::Bridge::PHPcode; our $VERSION = '0.02'; sub new { my $class = shift; my $init = shift; my $self={}; bless $self,$class; $self->action($init->{action}); $self->fixedSide($init->{fixedSide}); $self->width($init->{width}); $self->height($init->{height}); $self->PHPtemp($init->{PHPtemp}); $self->uri($init->{uri}); $self->makePHP; return $self } sub action { my $self=shift; if (@_) { $self->{action}=shift } return $self->{action} } sub fixedSide { my $self=shift; if (@_) { $self->{fixedSide}=shift } return $self->{fixedSide} } sub width { my $self=shift; if (@_) { $self->{width}=shift } return $self->{width} } sub height { my $self=shift; if (@_) { $self->{height}=shift } return $self->{height} } sub PHPtemp { my $self=shift; if (@_) { $self->{PHPtemp}=shift } return $self->{PHPtemp} } sub uri { my $self=shift; if (@_) { $self->{uri}=shift } return $self->{uri} } sub makePHP { my $self=shift; my @alphanumeric = ('a'..'z', 'A'..'Z', 0..9); $self->{PHPTemp}=join '',map $alphanumeric[rand @alphanumeric], 0..6; open my $fh,'>',$self->{PHPTemp}.'.php' or die $!; print $fh $PHP::GD::Bridge::PHPcode::constructor; close $fh; my @relativeURL=split('/',$ENV{SCRIPT_NAME}); pop(@relativeURL); $self->{uri}='http://'.$ENV{HTTP_HOST}.join('/',@relativeURL).'/'.$self->{PHPTemp}.'.php' } sub DESTROY { my $self=shift; unlink $self->{PHPTemp}.'.php' } sub edit { my ($self,$source,$target)=@_; my $ua=LWP::UserAgent->new(); $ua->agent('Mozilla/5.0'); $ua->timeout(15); my $res=$ua->post( $self->{uri}, Content => { source => $source, target => $target, action => $self->action, width => $self->width, height => $self->height, fixedSide => $self->fixedSide } ); if ($res->content =~ /[0-5]/ | !$res->is_success) { #qualcosa è andato storto! return 0 } else { return 1 } } 1;
Bridge PHP (PHPCode.pm)
package PHP::GD::Bridge::PHPcode; my $class=q( class resize{ // codici di errore di ritorno // codice 0 nessun file selezionato // codice 1 file non trovato // codice 4 valori parametro diversi dai consentiti (stretch, resize, crop) // codice 5 impostazioni lato di riferimento diversi da X, Y o XY // codice 6 estensione file diversa da jpg, jpeg, png, gif function controlData(){ // valori di default $maxWidth = 120; $maxHeight = 120; $action = "resize"; $fixedSide = "X"; $newName = ""; // $this->errore = array(); $this->fileExtension = ""; $this->imageWidth = 0; $this->imageHeight = 0; $this->originalName = ""; $this->newWidth = 0; $this->newHeight = 0; if($this->urlimage==""){ array_push($this->errore,0); } elseif (!file_exists($this->urlimage) || !is_file($this->urlimage)){ array_push($this->errore,1); } if (!is_numeric($this->maxWidth) || !is_numeric($this->maxHeight) || $this->maxWidthmaxHeight<0){ //larghezza ed altezza if ($this->maxWidth ==0 || $this->maxWidth =="") { $this->maxWidth=$maxWidth; } if ($this->maxHeight ==0 || $this->maxHeight =="") { $this->maxHeight=$maxHeight; } } if($this->action != "stretch" && $this->action != "resize" && $this->action != "crop" && $this->action != "cropresize"){ if ($this->action == "") { $this->action=$action; } array_push($this->errore,4); } if(($this->fixedSide!="XY" && $this->fixedSide!="X" && $this->fixedSide!="Y") && $this->action=="resize"){ $this->fixedSide=$fixedSide; } if(count($this->errore)>0){ foreach ($this->errore as $i => $value) { echo $this->errore[$i]; }; return false; }else{ return true; } } function go(){ if($this->controlData()) { // file $filename = basename($this->urlimage); $this->originalName = $filename; if($this->newName==""){$this->newName=$filename;} $this->fileExtension = strtolower(substr($filename, strrpos($filename, "."), strlen($filename)-strrpos($filename, "."))); if($this->fileExtension==".jpeg" || $this->fileExtension==".jpg"){ $handle_immagine = imagecreatefromjpeg($this->urlimage); }elseif($this->fileExtension==".gif"){ $handle_immagine = imagecreatefromgif($this->urlimage); }elseif($this->fileExtension==".png"){ $handle_immagine = imagecreatefrompng($this->urlimage); }else{ array_push($this->errore,6); return null; }; //edita l'immagne e torna l'lhandle target per salvarla if ($this->action == 'stretch' || $this->action == 'resize' || $this->action == 'crop') { $handle_target=$this->editImage($handle_immagine); } else { //cropresize $imageRatio=imagesx($handle_immagine)/imagesy($handle_immagine); $newImageRatio=$this->maxWidth/$this->maxHeight; if ($imageRatio == $newImageRatio) { // fai normale resize XY $this->action='resize'; $handle_target=$this->editImage($handle_immagine); } elseif ($imageRatio > $newImageRatio) { // resize su Y e newHeight e poi crop $this->fixedSide="Y"; $this->action='resize'; $handle_target2=$this->editImage($handle_immagine); $this->action='crop'; $handle_target=$this->editImage($handle_target2); } else { // resize su X e newWidth e poi crop // resize su Y e newHeight e poi crop $this->fixedSide="X"; $this->action='resize'; $handle_target2=$this->editImage($handle_immagine); $this->action='crop'; $handle_target=$this->editImage($handle_target2); } } imagejpeg($handle_target, $this->folder.$this->newName, 100); chmod($this->folder.$this->newName,0777); unset($handle_immagine); unset($handle_target2); unset($handle_target); } } function stretch() { $this->newWidth=$this->maxWidth; $this->newHeight=$this->maxHeight; } function resize () { if($this->fixedSide=="XY") { if ($this->imageWidth/$this->imageHeight>$this->maxWidth/$this->maxHeight) { $this->newWidth=$this->maxWidth; $this->newHeight=($this->imageHeight/$this->imageWidth)*$this->maxWidth; } else { $this->newWidth=($this->imageWidth/$this->imageHeight)*$this->maxHeight; $this->newHeight=$this->maxHeight; } } elseif ($this->fixedSide=="X") { $this->newWidth=$this->maxWidth; $this->newHeight=($this->imageHeight/$this->imageWidth)*$this->maxWidth; } elseif ($this->fixedSide=="Y") { $this->newWidth=($this->imageWidth/$this->imageHeight)*$this->maxHeight; $this->newHeight=$this->maxHeight; } else { if ($this->imageWidth/$this->imageHeight>$this->maxWidth/$this->maxHeight) { $this->newWidth=$this->maxWidth; $this->newHeight=($this->imageHeight/$this->imageWidth)*$this->maxWidth; } else { $this->newWidth=($this->imageWidth/$this->imageHeight)*$this->maxHeight; $this->newHeight=$this->maxHeight; } } } function crop() { if ($this->maxWidth > $this->imageWidth) { $this->maxWidth = $this->imageWidth; } if ($this->maxHeight > $this->imageHeight) { $this->maxHeight = $this->imageHeight; } $this->newWidth=$this->maxWidth; $this->newHeight=$this->maxHeight; //$this->imageWidth=$this->maxWidth; //$this->imageHeight=$this->maxHeight; } function editImage($handle_immagine) { $this->imageWidth=imagesx($handle_immagine); $this->imageHeight=imagesy($handle_immagine); if ($this->action=="stretch") { $this->stretch(); } elseif ($this->action=="crop") { $this->crop(); } else { $this->resize(); } $tmp_immagine = imagecreatetruecolor($this->newWidth, $this->newHeight); if ($this->action=="crop") { $src_x=($this->imageWidth-$this->newWidth)/2; $src_y=($this->imageHeight-$this->newHeight)/2; } else { $src_x=0; $src_y=0; }; if ($this->action=="crop") { $handle_target = imagecopy($tmp_immagine, $handle_immagine, 0, 0, $src_x, $src_y, $this->newWidth, $this->newHeight); } else { $handle_target = imagecopyresampled($tmp_immagine, $handle_immagine, 0, 0, 0, 0, $this->newWidth, $this->newHeight, $this->imageWidth, $this->imageHeight); } //$handle_target = imagecopyresampled($tmp_immagine, $handle_immagine, 0, 0, 0, 0, $this->newWidth, $this->newHeight, $this->imageWidth, $this->imageHeight); return $tmp_immagine; } } ); our $constructor = <<PHP; PHP 1;