MareX WeBlog

PHP příklady: obrázek s náhodným textem

REKLAMA

Určitě znáte, když se chcete někde například zaregistrovat, většinou na konci formuláře najdete obrázek, který obsahuje nějaká čísla nebo písmena a Vy je musíte opsat, abyste mohli být zaregistrováni (takzvaná captcha). Tak se pokusíme něco podobného, hodně jednoduchého také vytvořit..

Takže nejprve budeme potřebovat vygenerovat nějaké náhodné číslo (text), které nějakým způsobem zašifrujeme (znečitelníme) a toto číslo musíme předat scriptu, který podle něj „nakreslí“ obrázek (věděli jste, že
PHP umí malovat?) a taky jej musíme předat scriptu, který bude zpracovávat nějakou tu registraci, aby mohl ověřit, jestli ho uživatel opsal správně.

Vygenerování a znečitelnění náhodného čísla

Vůbec nevím, jaký způsob se na toto používá všude na internetu. Vytvořil jsem si vlastní způsob, ale ten Vám tady samozřejmě nebudu prozrazovat. Je na Vás, jak číslo znečitelníte tak, aby na něj nikdo nepřišel. Základem je to, že číslo musíte Vy sami umět zpátky rozšifrovat, aby jste ho mohli použít ve scriptu, který kreslí onen obrázek. My si tady pouze vygenerujeme náhodný text a ten nezašifrovaný pošleme scriptům, které s ním budou dále pracovat. (update: nejlépe je text uložit asi do session, tím se pak samozřejmě s nějakými úpravami textu nemusíte zabývat)
Použijeme 6-ti místný text složený z náhodných čísel a písmen bez diakritiky (pouze malá písmena).
To zajistíme tímto:

<?php
$nahodny_text = base_convert(mt_rand(base_convert(100000, 36, 10), base_convert("zzzzzz", 36, 10)), 10, 36);
?>

Co ten krátký kód vlastně dělá?
Funkce mt_rand() generuje náhodné číslo v rozmezí, které určíte zadáním dvou parametrů – min a max číslo. Zadáte-li například mt_rand(4,11), vygeneruje náhodné číslo od 4 do 11 včetně. Tedy můžou to být čísla 4,5,6,7,8,9,10,11.
Jenže my jsme jí předali nějaké šílenosti.
Já Vám tady ten samý kód zapíšu trochu složitěji:

<?php
// toto je text, který může být nejnižší
$min = 100000;

// toto je text, který může být nejvyšší
$max = "zzzzzz";

// oba tyto texty jsou vlastně číslo v 36-kové číselné soustavě
// teď je převedeme do klasické desítkové číselné soustavy
$min_10 = base_convert($min, 36, 10);
$max_10 = base_convert($max, 36, 10);
/* nyní je v proměnné $min_10 uloženo číslo 60466176 a v proměnné $max_10 číslo 2176782335stalo se tak převodem z 36-kové do 10-kové číselné soustavy
zajistili jsme si tím, že výsledný text bude vždycky dlouhý 6 znaků */

// teď tato dvě čísla předáme funkci mt_rand(), aby nám vygenerovala náhodné číslo
$nahodne_cislo = mt_rand($min_10, $max_10);
/* v proměnné $nahodne_cislo je uloženo náhodně vygenerované číslo v 10-kové soustavě, my ho převedeme
do 36-kové soustavy, aby náš náhodný text neobsahoval jen čísla, ale i písmena */
$nahodny_text = base_convert($nahodne_cislo, 10, 36);
?>

Tento kód je vlastně úplně to samé, co ten první kód, akorát jsme v tom prvním udělali všechno v jednom příkazu. Nemuseli jsme ukládat každé číslo zvlášť do proměnných.
Ještě si trošku přiblížíme funkci base_convert(). Tato funkce převádí čísla mezi číselnými soustavami. Co jsou číselné soustavy? Nevím, jak bych to napsal. Prostě: například desítková číselná soustava je nám všem dobře známa. Používáme ji denně, používali jsme ji už na základní škole v matematice (a nejen v ní). Je to soustava, která obsahuje 10 základních čísel – čísla 0 až 9. Různým skládáním těchto čísel získáváme čísla jiná (větší). číslo 500 zapíšeme jako pět nula nula.
Stejně to funguje i u jiných číselných soustav. Například 16-ková soustava obsahuje 16 čísel: čísla 0 až 9 a písmena A až F. Takže písmeno A znamená 11, písmeno B je 12, atd., až F je 15. A opět složením třeba dvou písmen F dostaneme 255 (FF = 255, AA = 170, 11 = 17).
Funkci base_convert() předáváme 3 parametry. První je číslo, které chceme převést, druhý je z jaké číselné soustavy a třetí do jaké číselné soustavy. Takže například:
base_convert(15, 10, 16);
nám převede číslo 15 z desítkové do šestnáctkové číselné soustavy, takže výsledek bude F.
Pokud bude číslo, které chcete převádět obsahovat písmena, musíte ho dát do uvozovek jako řetězec. Takže takto: base_convert(„ff“, 16, 10);

My jsme tedy převáděli do 36-kové soustavy – ta obsahuje všechna čísla a celou abecedu (bez diakritiky), takže čísla 0 až 9 a písmena A až Z.

Takže teď máme nějaký náhodný text o délce šesti znaků. Ten potřebujeme předat za prvé scriptu, který bude generovat obrázek a za druhé scriptu, který nám bude zpracovávat data z registračního formuláře. U toho registračního formuláře je to jednoduché. Předáme jej zároveň s formulářem v nějakém skrytém poli:

<input type="hidden" name="overovaci_text" value="<?php echo $nahodny_text; ?>">

A teď ještě potřebujeme na této stránce zobrazit obrázek s tím textem, který musí uživatel opsat, aby jsme ho zaregistrovali.
Dejme tomu, že script, který nám bude kreslit obrázek se jmenuje obrazek.php a je umístěn ve stejném adresáři jako registrační formulář:

<img src="obrazek.php?overovaci_text=<?php echo $nahodny_text; ?>">

Tím jsme scriptu obrazek.php předali superglobální proměnnou $_GET["overovaci_text"], která obsahuje onen náhodný text. Script vrátí tagu IMG obrázek a ten jej zobrazí na stránce.

Tvorba obrázku

Zde je celý kód scriptu obrazek.php, potom si postupně vysvětlíme, jak funguje:

<?php
$obrazek = ImageCreate(300, 55);
$pozadi = ImageColorAllocate($obrazek, 0, 0, 0);
$txt = ImageColorAllocate($obrazek, 255, 255, 255);
ImageFill($obrazek, 0, 0, $pozadi);
ImageFttext ($obrazek, 40, 0, 10, 42, $txt, "./verdana.ttf", $_GET["overovaci_text"]);
Header("Content-type: Image/PNG");
ImagePNG($obrazek);
ImageDestroy($obrazek);
?>

První řádek: funkce imagecreate() nám vytvoří obrázek a uloží jeho zdroj (nevím, jak bych to měl správně pojmenovat – prostě zdroj obrázku) do proměnné $obrazek – tento zdroj je důležitý, používá se ve všech funkcích kreslení. Funkce má dva parametry: šířku a výšku v pixelech. My tedy vytváříme obrázek o velikosti 300×55 bodů.

Druhý a třetí řádek: funkce imagecolorallocate() vytvoří barvu a uloží ji do proměnné. Předávají se jí čtyři parametry: zdroj obrázku, a hodnoty barevných složek RGB: červená, zelená, modrá. V našem případě tedy máme v proměnné $pozadi uloženou černou barvu a v proměnné $txt bílou barvu.

čtvrtý řádek: funkce imagefill() vyplní plochu obrázku zadanou barvou od zvoleného bodu na všechny strany, dokud nenarazí na jinou barvu, než má bod, na kterém vyplňování začalo. Takže pokud bude obrázek celý bílý, a Vy jej budete chtít vyplnit černou barvou od bodu 20 zleva a 20 zvrchu, vyplní se celý. Ale pokud bude kolem tohoto bodu nějaký rámeček, který bude mít třeba červenou barvu, vyplní se jen do tohoto rámečku. Předávané parametry této funkci jsou čtyři: zdroj obrázku, bod zleva (x), bod zvrchu (y), barva. V našem scriptu se obrázek vyplní celý černou barvou ($pozadi) a začne na bodu 0×0 – zde se bere první bod jako bod 0 v dané ose (x, y), takže pokud máme obrázek o velikosti 300×55 bodů, poslední bod v ose X je 299 a v ose Y 54.

Pátý řádek: funkce imagefttext() nám vepíše do obrázku náš text. Parametry jsou:

imagefttext (zdroj obrázku, velikost, úhel, osa x, osa y, barva, soubor fontu, text)

zdroj obrázku je jasný (proměnná $obrazek)

velikost je velikost fontu v bodech na výšku

úhel je úhel natočení textu (0 = klasicky vodorovně)

osa x je bod na ose X, kde má text začínat

osa y je bod na ose Y, kde má text začínat – pozor, myslí se spodní hrana řádku, tedy tam, kde je spodek písmen, která nezasahují pod řádek (pod řádek zasahuje např. malé P, malé Y, malé J atd.)

barva je také jasná (proměnná $txt – barva textu)

soubor fontu je cesta k souboru s písmem TrueType – v tomto případě se předpokládá, že ve stejném adresáři jako tento script je uložen soubor verdana.ttf, který obsahuje TrueType font Verdana

text je náš náhodně vygenerovaný text (proměnná, kterou jsme tomuto scriptu předali)

Této funkci se může ještě předávat devátý, volitelný parametr, který vůbec nevím, k čemu slouží, proto jej tu nepopisuji.

Další řádek je funkce header(). Tato funkce odesílá prohlížeči takzvanou „hlavičku“. V tomto případě odesílá hlavičku content-type s hodnotou image/png. Zjednodušeně řečeno funkce header() v našem případě prohlížeči nakecá, že se jedná o obrázek PNG, prohlížeč se podle toho zachová a bude náš script považovat za obrázek typu PNG. Stejně tak tam může být image/gif nebo image/jpeg – podle toho by se ale musel změnit název funkce na dalším řádku:
funkce na tomto řádku imagepng() nedělá nic jiného, než že vytvoří samotný obrázek a odešle ho prohlížeči. Tato funkce může mít ještě druhý parametr a to cestu, kam se má obrázek uložit. To my nepotřebujeme, náš obrázek je jen pro tuto chvíli, takže tento parametr nezadáváme. Stejná je funkceimagegif(), ta pro změnu vytvoří obrázek GIF. Naproti tomu funkce imagejpeg(), která vytvoří obrázek JPEG, může mít ještě třetí, volitelný parametr a tím je stupeň komprese. Pokud zadáte jako třetí parametr číslo 100, bude obrázek v maximální kvalitě bez komprese. čím nižší číslo, tím horší kvalita obrázku, ale menší velikost, vzhledem k vyšší kompresi. Pokud tento parametr nezadáte, je nějaká výchozí hodnota (tuším že 75, ale krk bych za to nedal).

No a tím jsme skončili. Výsledný obrázek může vypadat třeba nějak takto:

Ukázkový obrázek

Toto je samozřejmě asi nejjednodušší řešení. Takový obrázek Vám přečte jakýkoliv OCR program a tím pádem i sebeprimitivnější robot. Určitě víte, že se tyto obrázky většinou vykreslují s různě barevným pozadím, každé písmenko má třeba jinou barvu, je jinak velké, jinak nakloněné, používají se různé čáry a pavučiny přes text… To proto, aby se robotům co nejvíce ztížilo „přečtení“ textu z obrázku. O tomhle a vůbec dalších funkcích pro malování v PHP si můžeme povědět zase jindy.

Malá rada nakonec: pokud budete něco takového na svých stránkách používat, dejte si pozor, abyste to s nečitelností textu na obrázku nepřehnali. Sice potřebujete, aby text na obrázku nepřečetl robot, ale zase potřebujete, aby ho přečetl normální člověk. Pro uživatele není totiž nic otravnějšího (a taky nic spolehlivějšího, co by tohoto uživatele odradilo například od registrace na Vašich stránkách), než když se mu ani na desátý pokus nepodaří správně opsat kód. Uvědomte si, že například písmenko O a číslo nula se rádi pletou (řešením je použít font, u kterého je nula proškrtnutá) a taky, že pokud používáte ve Vašem obrázku proměnlivou velikost písmen, může se hodně plést třeba malé a velké O, malé a velké Y, malé a velké X, malé a velké Z atd.

REKLAMA

PHP příklady: obrázek s náhodným textem