Určitě jste se setkali s tím, že se vám, při použití knihovny GD, nedařilo správně zobrazovat české znaky s diakritikou. A když už jste konečně přišli jak na to, zjistili jste, že s fonty TTF je vše úplně jinak? Dnes bych vám rád ukázal jak na to.

Grafická knihovna GD rozšiřuje PHP o možnost grafických výstupů. Výsledkem takového PHP skriptu může být i bitmapový obrázek. To se velice hodí například pro grafické znázornění nějakých dat. O tom, jak nakreslit graf pomocí PHP, si můžete přečíst v minulém článku. Nedělitelnou součástí nějakého grafu by měly být popisky os, legenda, případně ještě další doplňující informace. Nutně tedy potřebujeme do takového obrázku napsat i nějaký text. Pomocí funkce ImageString to jde velice snadno, jenže vše funguje správně pouze do té doby, dokud se v těchto textech nevyskytují české znaky s diakritikou. Konkrétně se jedná o znaky Ž, Š, Ť a ž, š, ť. Takové znaky ve výsledném obrázku prostě chybí. Je to způsobeno rozdílným kódováním ve zdrojovém textu vašeho PHP skriptu a kódováním knihovny GD.

Knihovna GD používá kódování ISO 8859/2 Latin 2. Uspořádání znaků v tomto kódování, tedy horní polovina kódové tabulky (znaky 128 – 255), vypadá takto:

Pokud své zdrojové texty skriptů PHP píšete ve windows, používáte nejspíše kódování Windows CP 1250. Uspořádání znaků v tomto kódování, tedy horní polovina kódové tabulky (znaky 128 – 255), vypadá takto:

Tato dvě kódování se samozřejmě liší. Co se českých znaků týká, rozdíl mezi nimi je právě ve výše zmíněných znacích Ž, Š, Ť a ž, š, ť, které, jak můžete vidět, leží v těchto tabulkách na různých místech.

Převod CP1250 na ISO 8859/2 Latin 2

Pro převod mězi těmito kódovými stránkami použijeme funkci StrTr.

$novy = StrTr($puvodni, chr(138).chr(141).chr(142).chr(154).chr(157).chr(158),
       chr(169).chr(171).chr(174).chr(185).chr(187).chr(190));

Funkce StrTr má tři vstupní parametry, kterými jsou vstupní řetězec, tabulka znaků, které se mají ve vstupním řetězci nahradit, a tabulka znaků, kterými se nahrazované znaky mají nahradit. Výsledkem funkce je převedený textový řetězec. V našem případě je původní text uložen v proměnné $puvodni. Řetězec chr(138).chr(141).chr(142).chr(154).chr(157).chr(158) představuje našich šest znaků v kódování CP1250, které potřebujeme ve vstupním řetězci nahradit znaky v kódování ISO 8859/2 Latin 2. Znaky, kterými budeme nahrazovat, obsahuje poslední parametr, řetězec chr(169).chr(171).chr(174).chr(185).chr(187).chr(190). Zde ještě malá ukázka, jak tento převod použít v praxi.

// pro snadné použití si převod nadefinujeme jako funkci
function PrevedNaISO($vstup_str)
{
  return StrTr($vstup_str, chr(138).chr(141).chr(142).chr(154).chr(157).chr(158),
                        chr(169).chr(171).chr(174).chr(185).chr(187).chr(190));
}
$puvodni = „Živohošť – žšťŽŠŤ“;// původní řetězec
Header(„Content-Type: image/gif“);// hlavička obrázku
$fontsize=4;// velikost znaků
$im = ImageCreate(240, 60);// alokace prostoru pro obrázek
$bila = ImageColorAllocate($im, 255, 255, 255);// definování bílé barvy pro podklad
$cerna=ImageColorAllocate($im, 0, 0, 0);// definování černé barvy pro písmo
ImageFill($im, 0, 0, $bila);// vyplnit celou plochu bílou barvou
ImageColorTransparent($im, $bila);// pro zajímavost nastavím bílou jako průhledný podklad
ImageString($im, $fontsize, 10,10,“puvodni: „.$puvodni, $cerna);// vytisknu řetězec v původní podobě
ImageString($im, $fontsize, 10,30,“novy : „.PrevedNaISO($puvodni), $cerna);// vytisknu řetězec po převodu
ImageGIF($im);// uzavřu obrázek, dojde k jeho odeslání prohížeči

Výsledek by měl vypadat takto:

Převodní řetězce můžeme zapsat i jinak, pomocí hexadecimálních kódů:

function PrevedNaISO($vstup_str)
{
  return strtr($vstup_str, „\x8A\x8D\x8E\x9A\x9D\x9E“,
                        „\xA9\xAB\xAE\xB9\xBB\xBE“);
}

Domnívám se, že tento způsob by měl být rychlejší, protože převodní řetězce jsou předem dány, a při každém převodu se nemusejí opakovaně jednotlivé znaky sčítat.

Použití fontu TTF

Kromě základního fontu, kterým disponuje knihovna GD, máme možnost používat libovolné TrueType (TTF) fonty, musíme však kromě knihovny GD mít ještě knihovnu FreeType. To nám dává možnost použít ve svých obrázcích téměř libovolné písmo. Přináší to však další problém s kódováním českých znaků. TTF fonty jsou vytvořeny v kódování UTF8. A když si zkusíme vytisknout znaky 128 až 255, zjistíme, že většina českých znaků chybí. Ještě jednou se podívejte na horní polovinu tabulky CP 1250. Barevně jsou označeny české znaky, které v následující tabulce UTF8, v rozsahu od 128 do 255 úplně chybí. Nacházejí se totiž na místech s mnohem vyššími kódy. Jak se k nim dostat se dozvíte za malou chvilku.

A takhle vypadá tabulka UTF8 v rozsahu 128 – 255.

Převod CP1250 na UTF8

Když používáme v knihovně GD fonty TTF, místo funkce ImageString pro výstup textového řetězce musíme použít funkci ImageTTFText. Tato funkce má o tři parametry více.

ImageString($im, $velikost_pisma, $uhel, $x, $y, $barva, $soubor_fontu, $text);

Parametr $im je handle obrázku, $velikost_pisma určuje velikost písma v bodech, $uhel určuje úhel, pod kterým bude text vypsán. $x a $y jsou počáteční souřadnice umístění textu, $barva určuje barvu textu. Proměnná $soubor_fontu ukazuje cestu k souboru s TTF fontem. Nakonec proměnná $text obsahuje výstupní textový řetězec.

Do výstupního řetězce můžeme napsat prostý text, ale české znaky ve výstupním obrázku pochopitelně nebudou. Můžeme je však nahradit jejich UTF8 sekvencemi, které vypadají takto: &#xxx;, xxx představuje číselný kód znaku v tabulce UTF8.

Patřičné kódy českých znaků můžete nalézt na FTP serveru o UNICODE, nebo ve směrnici RFC 1345. Protože celá kódová tabulka UTF8 je příliš veliká, uvádím pouze vybrané české znaky.

Č010cLATIN CAPITAL LETTER C WITH CARON
č010dLATIN SMALL LETTER C WITH CARON
Ď010eLATIN CAPITAL LETTER D WITH CARON
ď010fLATIN SMALL LETTER D WITH CARON
Ě011aLATIN CAPITAL LETTER E WITH CARON
ě011bLATIN SMALL LETTER E WITH CARON
Ň0147LATIN CAPITAL LETTER N WITH CARON
ň0148LATIN SMALL LETTER N WITH CARON
Ř0158LATIN CAPITAL LETTER R WITH CARON
ř0159LATIN SMALL LETTER R WITH CARON
Š0160LATIN CAPITAL LETTER S WITH CARON
š0161LATIN SMALL LETTER S WITH CARON
Ť0164LATIN CAPITAL LETTER T WITH CARON
ť0165LATIN SMALL LETTER T WITH CARON
Ů016eLATIN CAPITAL LETTER U WITH RING ABOVE
ů016fLATIN SMALL LETTER U WITH RING ABOVE
Ž017dLATIN CAPITAL LETTER Z WITH CARON
ž017eLATIN SMALL LETTER Z WITH CARON

Potřebujeme tedy ve výstupním řetězci nahradit české znaky patřičným UTF kódem. Na to nám funkce StrTr nestačí, neboť dokáže zaměňovat pouze jeden znak za jeden znak. Vyměnit jeden znak za řetězec znaků nezvládne. Musíme si na to napsat složitější funkci.

Využijeme výbornou vlastnost asociativních polí. V takovém poli totiž můžeme jednotlivým prvkům přiřadit jejich vlastní indexové hodnoty, což znamená, že pole nemusí obsahovat prvky 1 až 10, ale třeba jen 1, 5, 10, a to se nám hodí. My totiž potřebujeme 18 znaků z intervalu 0-255 nahradit jejich UTF8 kódy. Vytvoříme si tedy osmnáctiprvkové asociativní pole. Podívejte se, jak taková převodní funkce bude vypadat.

function PrevedNaUTF($vstup_str)
{// vytvořím si asociativní pole pro 18 znaků ve tvaru „původní znak“=>kód UTF8
  $tabulkaUTF = array(„Č“=>268, „č“=>269,
                      „Ď“=>270, „ď“=>271,
                      „Ě“=>282, „ě“=>283,
                      „Ň“=>327, „ň“=>328,
                      „Ř“=>344, „ř“=>345,
                      „Š“=>352, „š“=>353,
                      „Ť“=>356, „ť“=>357,
                      „Ů“=>366, „ů“=>367,
                      „Ž“=>381, „ž“=>382);
  $vystup_str = „“; // vynuluji výstupní řetězec
  for($i=0; $i < strlen($vstup_str); $i++) // projdu všechny znaky vstupního řetězce
  {
    if ($tabulkaUTF[$vstup_str[$i]]) // pokud se znak nachází v tabulce
      $vystup_str .= „&#“ . $tabulkaUTF[$vstup_str[$i]] . „;“; // zaměním jej
    else
      $vystup_str .= $vstup_str[$i]; // jinak vezmu původní znak
  }
  return $vystup_str; // vracím překódovaný řetězec
}

Ke komentářům v ukázce zdrojového kódu není co dodat. Pro doplnění ještě přikládám ukázku, jak převodní funkci PrevedNaUTF použít ve skriptu.

$puvodni = „ČčĎďĚěŇňŘřŠšŤťŮůŽž“; // vstupní řetězec
Header(„Content-type: image/gif“); // úvodní hlavička obrázku
$im = ImageCreate(240, 60); // alokace prostoru pro obrázek
$bila = ImageColorAllocate($im, 255, 255, 255); // příprava bílé barvy do palety
$cerna = ImageColorAllocate($im, 0, 0, 0); // příprava černé barvy do palety
ImageTTFText($im, 14, 0, 10, 30, $cerna, „c:\\windows\\fonts\\arial.ttf“, „puvodni: „. $puvodni); // řetězec bez převodu
ImageTTFText($im, 14, 0, 10, 50, $cerna, „c:\\windows\\fonts\\arial.ttf“, „novy : „. PrevedNaUTF($puvodni)); // převod řetězce
ImageGIF($im); // uzavření a výstup obrázku

Výsledek by měl vypadat takto:

Převodní procedura PrevedNaUTF v tomto příkladu převádí z kódování Windows CP1250. Pokud potřebujete mít své zdrojové kódy v ISO 8859/2 Latin 2, stačí když přepíšete malé a velké š, ť a ž. Ostatní znaky mají stejné kódy, proto není potřeba dalších úprav.

A to je pro dnešek vše.

Starší komentáře ke článku

Pokud máte zájem o starší komentáře k tomuto článku, naleznete je zde.

1 Příspěvěk v diskuzi

Odpovědět