GD
En af de gode ting ved at benytte PHP som webserver-scriptingsprog, er, at der er indbygget en masse funktioner i PHP's moduler. PHP's udviklere har taget kendte og afprøvede open source- og freeware-kodebiblioteker, og indbygget dem i PHP, og på den måde kan PHP tilbyde en fantastisk stor mængde funktioner. Udover en stor mængde fødte databasegrænseflader, byder PHP også på XML-behandling, genererede flash-filer, krypteringsalgoritmer og det, vi skal se på i denne artikel, nemlig generering af grafikker.
GD-biblioteket er skrevet af den amerikanske programmør Thomas Boutell
Det kan være en god ide at downloade PHP-manualen fra http://www.php.net/docs.php, så man har den ved hånden.
Vi vil som sagt udvikle et dynamisk billedgalleri. Ved blot at pege på en mappe i vores webserver-hierarki, skal siden danne en række thumbnails, af de billeder, der nu måtte være i mappen. Vores thumbnails - eller billedfrimærker - skal kunne variere i højden og bredden, og vi skal kunne vælge en vilkårlig grad af kompression.
I denne første artikel kigger vi på scriptet, der skal generere thumbnails udfra billeder på harddisken, og i den næste artikel kigger vi på et script, der kan vise thumbnails for alle billeder i en mappe, samt navigere rundt i de mapper, som vi tillader brugeren at kigge i.
Scripting er ofte en iterativ proces, i endnu højere grad end "rigtig" programmering: Først udvikler man noget, der virker, så kan man udbygge det gradvist. Man skal selvfølgelig holde tungen lige i munden, så det ikke ender i spaghetti, og undgå gigantiske lange scripts, hvor hele webstedets funktionalitet er proppet ind.
Det følgende er skevet med henblik på Windows-miljøet, men UNIX-folket kan sagtens kigge med - der er kun filstierne til forskel i eksemplerne.
Så lad os først starte med at åbne et JPEG-billede og vise det via PHP. Det er altså PHP, og ikke webserveren, der står for fremvisningen.
Det første billede
Du kan downloade hele scriptet her.
Vi starter med følgende stump:
<?php
Header( "Content-type: image/jpeg");
?>
Den første linie fortæller, at her starter der noget php-kode, (ligesom <% .. %> i ASP), og den tredie linie fortæller browseren, at nu kommer der et JPEG-billede. Den skal altid stå øverst.
Nu skal vi hente et billede.
<?php
Header( "Content-type: image/jpeg");
$imagefile = "C:\\Inetpub\\wwwroot\\php\\billeder\\mit_billede.jpg";
if(file_exists($imagefile)) {
$im = @ImageCreateFromJPEG($imagefile);
ImageJPEG($im,"",75);
ImageDestroy($im);
}
?>
I linie 5 definerer vi en variabel, $imagefile, som peger på et JPEG-billede. Alle variabler i php skal begynde med $-tegn, og som i C og Java skal backslash \ skrives to gange - ellers bliver det læst som et styretegn. Iøvrigt minder PHP's sprogkonstruktioner meget om C, Java og JavaScript, så hvis man har lidt kendskab til disse sprog skulle der ikke være nogen ko på isen.
I den næste linie tjekker vi om filen eksisterer - altid en god ting. Hvis det er tilfældet, så laver vi et billede, og gemmer det i variablen $im, i linie 8. Ved at sætte et snabel-a foran funktionskaldet ImageCreateFromJPEG() undertrykker vi eventuelle fejlmeddelelser, der måtte komme, hvis noget gik galt.
I næste linie, nummer 9, spytter vi billedet ud til browseren, med funktionen ImageJPEG($im,"",75). Den første parameter er billedvariablen, og den sidste er JPEG-kvaliteten på en skala fra 0 (dårligst og mindste filstørrelse) til 100 (bedst og størst). Parameteren i midten ignorerer vi. Til allersidst frigører vi billedet fra hukommelsen, for en god ordens skyld, med funktionen ImageDestroy().
Så er det bare at gemme php-filen et sted, for eksempel i C:\Inetpub\wwwroot\php\, kald den for eksempel for thumbnail.php3, og sørge for, at der ligger et billede med navnet mit_billede.jpg i mappen C:\Inetpub\wwwroot\php\billeder.
Det bliver lidt mere avanceret
Nu, hvor læseren forhåbentlig er blevet imponeret over, hvor nemt det er at arbejde med billeder i PHP, sætter vi temperaturen lidt op.
Allerførst vil vi godt være i stand til at vise et hvilket som helst billede i billed-mappen og dens undermapper. Vi vil ikke gøre scriptet i stand til at læse hvorsomhelst på harddisken - det kunne nemt ende med en sikkerhedsbrist. Helt konkret vil vi have, at når brugeren skriver
http://localhost/php/thumbnail.php3?PATH=mormor\foedselsdag\lagkagen.jpg
- så får vi billedet
C:\Inetpub\wwwroot\php\billeder\mormor\foedselsdag\lagkagen.jpg
frem i browseren. Det gør vi sådan:
<?php
Header( "Content-type: image/jpeg");
$startpath = "C:\\Inetpub\\wwwroot\\php\\billeder\\";
$imagefile = $startpath.$PATH;
if(file_exists($imagefile))
?>
I den nye linie 5 har vi indsat stien til mappen, som er udgangspunkt for vores billedbrowsing. I linie 7 danner vi hele stien til billedet. $PATH er værdien af PATH-parameteren i GET-delen af forespørgslen. Hvis kaldet ser sådan ud:
http://localhost/php/thumbnail.php3PATH=mormor\foedselsdag\lagkagen.jpg
- så ser $PATH-variable sådan her ud:
mormor\foedselsdag\lagkagen.jpg
Punktummet i mellem $startpath og $PATH er PHP's version af streng-konkatenering - det er ligesom "+" i C-familien og "&" i Basic.
Fejlhåndtering
Fejlhåndtering
Prøv scriptet med forskellige billeder og undermapper i vores billed-rodmappe. Det virker tilsyneladende glimrende. Men prøv også at skrive noget volapyk i PATH-parameteren - for eksempel
http://localhost/php/thumbnail.php3?PATH=mormor\foedselsdag\volakpyk
- så kommer der et brækket lænke-ikon frem i browseren. Og det er jo ikke smukt. Så først vil vi her se på fejlhåndtering:
Vi putter en else-del ind i vores if-sætning i scriptet:
if(file_exists($imagefile)) {
$im = @ImageCreateFromJPEG ($imagefile);
ImageJPEG($im,"",75);
ImageDestroy($im);
} else {
$im = imagecreate (300,100);
$black = ImageColorAllocate ($im, 0, 0, 0);
$white = ImageColorAllocate ($im, 255, 255, 255);
ImageTTFText ($im, 12, 0, 10, 20, $white,
"C:\\WINDOWS\\FONTS\\ARIAL.TTF", "Desværre, ingen fil");
ImageJPEG($im,"",75);
ImageDestroy($im);
}
Lad os gennemgå else-delen, som bliver udført, hvis billedet ikke kan findes, og funktionen file_exists() dermed returnerer falsk: Første linie allokerer et nyt billede med dimensionerne højde 100 og bredde 300. Den næste linie allokerer en RGB-farve, som automatisk bliver sat som baggrundfarve. Den næste linie igen allokerer en farve $white sat til RGB-værdien 255,255,255 (som er hvid).
Den næste linie igen er interessant: funktionen ImageTTFText() skriver på billedet med en TrueType-font fra skrifttypemappen. Du kan se mere om funktionen i PHP-manualen her.
Lad os gennemgå parametrerne i funktionen ImageTTFText: $im er billedvariablen, det næste tal er font-størrelsen i pixels, $white er tekstfarven, som vi definerde før, "C:\\WINDOWS\\FONTS\\ARIAL.TTF" er stien til TrueType-skriften Arial (som kan ses ved at gå ind i Kontrolpanel > Skrifttyper og vælge Vis > Detaljer). Den sidste parameter er strengen der skal skrives.
De to sidste linier er som før: spyt billedet ud og frigør hukommelsen.
Prøv nu at indtaste volapyk-adressen fra før: Nu skulle du gerne se billedet
Thumbnail
Thumbnail
Nu kommer vi til det, der var pointen med scriptet - nemlig at generere en thumbnail.
Vi laver en funktion, der kan skalere et billede til bredden $w og højden $h:
function scaleImg($srcIm, $w, $h) {
$temp = imagecreate($w,$h);
imagecopyresized ($temp, $srcIm, 0, 0, 0, 0, $w, $h,
imagesx($srcIm), imagesy($srcIm));
return $temp;
}
Vi indsætter funktionen lige efter Header-linien i scriptet. Lad os gennemgå funktionen, linie for linie:
Funktionen kaldes med argumenterne $srcIm, som er billedet, der skal skaleres, og $w og $h der er henholdvis bredde og højde.
Linie 1 allokerer et billede, $temp, med bredden $w og højde $h. Linie 2 kalder funktionen imagecopyresized(), som skalerer $srcIm, og kopierer det ind i billedet $temp. En fuld dokumentation af funktionen kan findes her. Til sidst returnerer vi det skalerede billede. Funktionerne imagesx($srcIm) og imagesy($srcIm) der indgår i kaldet, returnerer henholdsvis bredde og højde af billedet $srcIm.
I if-delen af scriptet til føjer vi en linie efter linie 2:
if(file_exists($imagefile)) {
$im = @ImageCreateFromJPEG ($imagefile);
$im = scaleImg($im, $WIDTH, $HEIGHT);
ImageJPEG($im,"",75);
ImageDestroy($im);
}
Her kalder vi scaleImg-funktionen med parameterne $WIDTH og $HEIGHT, som vi henter fra GET-kaldet:
http://localhost/php/thumbnail.php3?
PATH=mormor\lagkagen.jpg&WIDTH=100&HEIGHT=150
Nu bliver vores lagkage-billede skaleret til 100 gange 150 pixels uden respekt for propertionerne. Det ser jo ikke så smukt ud, så vi skriver straks to nye funktioner:
function scalePropW($srcIm, $w) {
return scaleImg($srcIm, $w,
imagesy($srcIm)*$w/imagesx($srcIm));
}
function scaleBound($srcIm, $w, $h) {
if($h/$w>imagesy($srcIm)/imagesx($srcIm)) {
return scalePropW($srcIm, $w);
} else {
return scalePropW($srcIm,
$h*imagesx($srcIm)/imagesy($srcIm));
}
}
Den første funktion scalePropW() kalder scaleImg() på en sådan måde, at billedet skaleres propertionalt i forhold til bredden.
Den anden funktion scaleBound() kalder scalePropW() således at billedet bliver skaleret, så det befinder sig inden for kassen (bounding box) defineret af $w og $h.
De to funktioner bygger på simpel 9.-klasses geometri, og det springer vi let hen over, men læseren kan jo eventuelt rådføre sig med afkom i den skolesøgende alder, hvis sådanne forefindes i hjemmet.
Vi ændrer nu tredie linie i if-delen af scriptet til
$im = scaleBound($im, $WIDTH, $HEIGHT);
så ser det sådan ud:
if(file_exists($imagefile)) {
$im = @ImageCreateFromJPEG ($imagefile);
$im = scaleBound($im, $WIDTH, $HEIGHT);
ImageJPEG($im,"",75);
ImageDestroy($im);
}
Nu kan vi kalde vores thumbnails med adressen
http://localhost/php/thumbnail_pcw1.php3
PATH=mormor\lagkagen.jpg&WIDTH=200&HEIGHT=100
og nu holder thumbnails'ne sig indenfor en kasse med dimensionerne 200 gange 100.
Nu skal vi lige sørge for, at vores fejl-billeder også har den rigtige størrelse. Det er nemt nok. Vi laver første linie i else-delen om, så bredde og højde bliver sat af $WIDTH og $HEIGHT:
} else {
$im = imagecreate ($WIDTH,$HEIGHT);
Vi er næsten - næsten! - færdige nu. Nå vi i næste uge laver vores galleri-side, ville det være rart, hvis alle vores thumbnails havde samme størrelse. Hvis vi kender bredde og højde, kan vi benytte WIDTH- og HEIGHT-attributterne i <IMG>-tagget, og få vores sider til at blive fremvist hurtigere. Vi vælger at lægge en sort baggrund til at fylde hele arealet ud. Det gør vi ved at tilføje nogle linier til if-delen:
if(file_exists($imagefile)) {
$im = @ImageCreateFromJPEG ($imagefile);
$im = scaleBound($im, $WIDTH, $HEIGHT);
$im2 = imagecreate ($WIDTH,$HEIGHT);
$black = ImageColorAllocate ($im2, 0, 0, 0);
$dst_x = ($WIDTH - imagesx($im))/2;
$dst_y = ($HEIGHT - imagesy($im))/2;
ImageCopy ($im2, $im, $dst_x, $dst_y, 0, 0, imagesx($im), imagesy($im));
ImageDestroy($im);
ImageJPEG($im2,"",75);
ImageDestroy($im2);
}
I tredie linie skaber vi et nyt billede med de sædvandlige dimensioner. Derefter allokerer vi farven $black, der automatisk bliver baggrundfarven i $im2. I de næste linier regner vi ud, hvor thumbnail-billedet ($im) skal kopieres ind, således at det optræder centreret i højde og bredde på den sorte baggrund.
Så benytter vi funktionen ImageCopy, der kopierer $im over på den sorte baggrund. Se en beskrivelse af funktionen her. Derefter fritsætter vi den hukommelse, som $im optog, og spytter $im2 ud i browseren. Prøv det - nu skulle der gerne være en pæn, sort baggrund, og billedet har faktisk dimensionerne $WIDTH og $HEIGHT.
Til allerallersidst vil vi også godt have variabel kompressionsgrad, så brugere med langsomme forbindelser kan komprimere deres thumbnails mere, end brugere med hurtige forbindelser. Det er hurtigt gjort - den næst-sidste linie af if-delen
ImageJPEG($im2,"",75);
udskifter vi med
ImageJPEG($im2,"",$QUALITY);
og så kan vi kalde scriptet således:
http://localhost/php/thumbnail.php3
?PATH=mormor\lagkagen.jpg&WIDTH=100&HEIGHT=150&QUALITY=50
Prøv med forskellige værdier af QUALITY.
Du kan downloade hele scriptet her.
I næste uge bygger vi et script, der viser galleri-siderne, og giver mulighed for at navigere rundt i mapperne.