Wrzasq.pl

Przecinek, kropka i wykres w ImageMagick

Thursday, 16 June 2016, 10:47

ImageMagick

Nie, nie chodzi o interpunkcję. Do projektu, który właśnie wykonuję potrzeba było dorobić generowanie wykresów. Projekt w PHP więc praktycznie miałem dwie możliwości: GD i ImageMagick. Już nieraz przekonałem się jakie ograniczenia ma biblioteka GD i o ogromnej przewadze nad nią tej drugiej dlatego bez chwili wahania zdecydowałem się użyć tejże. Nie jest w tym momencie ważne czego wykresy są generowane, gdyż sprawa, o której piszę jest tak kuriozalna, że przyćmi niewątpliwie każdą tematykę strony. Mianowicie chodzi o problem (bug?), który powoduje następujący rodzaj błędów:

exception 'ImagickException' with message 'Non-conforming drawing primitive definition `rectangle''

Podobne błędy pojawiają się praktycznie przy każdej operacji wykonywanej przy pomocy ImagickDraw. Co więcej - jeśli nawet nie będziemy z niej korzystać to i tak najprawdopodobniej otrzymamy przekłamanie kolorów (na przykład biały zostanie zastąpiony przez żółty).

Dziwny bug

Nie uwierzycie co powoduje ten błąd… ustawienia lokalizacyjne (sic!). Na początku swojego skryptu zazwyczaj ustawiam język aplikacji wykorzystując setlocale():

setlocale(LC_ALL, 'pl_PL.UTF-8');

I tu się pojawiają problemy - w naszym kraju, jak i w wielu innych, separatorem dziesiętnym (oddzielającym część całkowitą liczby od ułamkowej) jest przecinek, a nie jak to ma miejsce w USA (a to tamtejsze standardy są podstawą informatyki) kropka. ImageMagick generuje grafikę wektorowo, zapisując polecenia. Oznacza to, że na przykład wywołanie:

$draw = new ImagickDraw();
$draw->rectangle(20, 20, 20 + $width, 220);

Spowoduje zapisanie instrukcji:

rectangle 20,20 400,220

O dziwo mimo iż generowanie komend odbywa się w standardowy sposób, to do generowania na ich podstawie grafiki jakimś cudem zaprzęga się parsowanie bazujące na ludzkiej reprezentacji liczb. W takim wypadku mimo iż przy tych samych localsach, rozkaz wygenerowany zostanie odczytany jako prostokąt z podanymi jedynie dwoma współrzędnymi - x1=20,20 i y1=400,220 co nie wystarcza do jego realizacji, bo brakuje punktu "końcowego" (x2/y2). Tak jak wspomniałem podobnie rzecz ma się z kolorami, jeśli wykorzystamy na przykład notację RGB:

$background = new ImagickPixel('rgb(255,255,255)');

Zostanie to zinterpretowane jako kolor o składowej czerwonej 255,255, zielonej 255 i niebieskiej 0 (jakimś cudem brak trzeciego parametru nie przeszkadza przy tworzeniu koloru), co w rezultacie da kolor żółty.

Rozwiązanie

Na szczęście rozwiązanie jest banalnie proste. Przed użyciem ImageMagick wystarczy dodać linijkę:

setlocale(LC_ALL, 'C');

Fixed?!

Oczywiście, jak to zawsze w przypadku open source bywa, wszyscy o tym doskonale wiedzą - bug został zamknięty z komentarzem This bug has been fixed in CVS., co ciekawe, w październiku 2007 - korzystam z wersji aktualnie (o ironio) stabilnej, czyli 2.2.2, która została wypuszczona w lutym 2009. Jak to możliwe, że ten naprawiony błąd wciąż mnie prześladuje?

Tags: , , , ,