Arabské klikyháky snadno a rychle
Dostal jsem poměrně exotický úkol: naučit naše automaty mluvit arabsky a hebrejsky. To by nemuselo být zas tak těžké, myslel jsem si, jenže jde o embedded zařízení optimalizovaná pro velmi nízkou spotřebu a to s sebou nese spoustu omezení.
Konkrétně se jedná o starší automotive platformu M32C od Renesasu. Vývoj probíhá v mírně osekané verzi jazyka C bez dynamické alokace paměti, což znamená, že nelze použít oblíbené knihovny pro podporu Unicode, vektorových fontů a tak dále.
Ve světle těchto skutečností už to tak easy nevypadá, nicméně možnosti tu sou, pojďme se na ně tedy podívat.
Jazyky a písma
Věta mluvit arabsky a hebrejsky z prvního odstavce je trochu nepřesná. Při zobrazení nezáleží na tom, jakým jazykem se kde mluví, ale jakým písmem se píše.
Po nějakém tom googlování se mi podařilo zjistit následující:
Arabské písmo je komplexní skript s následujícími pravidly:
- Vizuální forma vychází z psaného písma
- 28 základních znaků (konsonant), vokály se značí pomocí vokalizačních značek a nejsou povinné
- Každý znak lze zobrazit v několika podobách, v závislosti na pozici - na začátku slova, uprostřed, na konci nebo samostatně (tzv. kontextové formy)
- Některé dvojice nebo trojice písmen mají zvlášť předepsané zobrazení (tzv. ligatury)
- Číslice: západní standard, užívaný v severní Africe (0123456789) nebo východní standard, užívaný na Blízkém východě (٠١٢٣٤٥٦٧٨٩)
- Text se píše zprava doleva, vyjma čísel
Hebrejské písmo je oproti tomu poměrně jednoduché:
- 22 písmen, 5 z nich má odlišnou koncovou formu
- Text se píše zprava doleva, vyjma čísel
Unicode
My, co používáme latinku, bychom si vystačili i s kódovými stránkami z Windows a ISO-8859, ale co takoví Číňané s tisíci znaky nebo Arabové se spoustou pravidel pro zápis textu?
Unicode standard řeší všechny tyto problémy tím, že zavádí šestnácti- a vícebitové znaky, definuje řídicí znaky pro obousměrný text a kromě samostatných znaků obsahuje i ligatury a kontextové formy. Každému znaku je přiřazen hexadecimální Unicode kód, například znak Á = U+00C1. Jednotlivé skripty a skupiny znaků (latinka, azbuka, interpunkce, symboly měny atd.) jsou sdruženy do bloků, přičemž první blok nazvaný Basic Latin (U+0000 až U+007F) odpovídá klasické ASCII tabulce.
Protože tenhle systém je dost všeobjímající, chtěl jsem si práci s ním co nejvíce zjednodušit. Proto jsem začal texty ukládat v kompatibilním kódování UCS-2, kde jsou všechny znaky pouze šestnáctibitové, a na základě znalosti jednotlivých bloků jsem si vytvořil následující seznam Unicode "stránek" potřebných pro některá rozšířená písma:
Latinka (U+00, U+01)
- Západoevropské jazyky: afrikánština (af), albánština (sq), baskičtina (eu), katalánština (ca), dánština (da), holandština (nl), angličtina (en), faerština (fo), finština (fi), francouzština (fr), gaelština (gl), němčina (de), islandština (is), irština (ga), italština (it), norština (no), portugalština (pt), skotština (gd), španělština (es), švédština (sv)
- Středoevropské jazyky: chorvatština (hr), čeština (cs), maďarština (hu), polština (pl), rumunština (ro), srbština (sr-Latn), slovenština (sk), slovinština (sl)
- Pobaltské jazyky: estonština (et), litevština (lv), lotyština (lt)
- Další jazyky: turečtina (tr), vietnamština (vi) - je navíc potřeba U+1E pro plnou podporu
Řecké písmo (U+03)
- Jazyky: řečtina (el)
Azbuka (U+04)
- Jazyky: bulharština (bg), běloruština (be), makedonština (mk), ruština (ru), srbština (sr-Cyrl), ukrajinština (uk)
Hebrejské písmo (U+05)
- Jazyky: hebrejština (he)
Arabské písmo (U+06)
- Jazyky: arabština (ar)
Katakana (U+30)
- Jazyky: japonština (ja)
Seznam samozřejmě není kompletní. Cílem bylo pokrýt země, u nichž je reálný obchodní potenciál pro naše produkty.
Kompletní není ani výčet Unicode stránek. Pro arabské písmo je namísto samostatných znaků (U+06) potřeba použít kontextové formy (U+FE), u hebrejského písma lze dodatečné formy (U+FB) zvážit. A připočtěme i základní interpunkci, číslice a symboly (U+00), používané ve všech jazycích.
Zobrazení
Na embedded zařízení s monochromatickým displejem by vykreslování textu pomocí vektorového fontu bylo v mnoha případech slušný overkill. Jednodušší je použít rastrový font nebo přímo monochromatickou bitmapu s maticí znaků pro určitou Unicode stránku. Renderování textu pak spočívá v pouhém vykusování jednotlivých znaků z bitmapy a vykreslování na displej (v řádku za sebe).
Při tvoření fontu pozor na mezery: zatímco u latinky a dalších jednoduchých písem je nějaký ten pixel mezi písmeny žádoucí, v arabském písmu jsou znaky ve slovech spojeny (tzv. shaping).
Protože každý znak potřebuje ke své reprezentaci aspoň dva bajty, měla by platforma podporovat typ wchar. Jinak nezbude než si nadeklarovat něco vlastního typu word
.
Obousměrný text
Člověk by řekl, vykreslíme znaky zprava doleva a je to. Tak jednoduché to ale bohužel není, neboť často se vizuální a logická reprezentace textu podstatně liší.
Základem je vědět výchozí směr textu:
- zleva doprava (LTR): latinka, azbuka, řecké písmo a další
- zprava doleva (RTL): arabské písmo, hebrejské písmo a další
Sekvence textu v jednom směru se nazývá běh, nebo též directional run. Za normálních okolností je na řádku pouze jeden běh. Pokud se ale například v arabském textu vyskytne český název nebo naopak, znamená to více běhů. Ty najdeme na základě znalosti následujících skupin znaků:
- silné znaky: písmena, značky LTR a RTL, ...
- slabé znaky: číslice, oddělovače, operátory, znaky měny, ...
- neutrální znaky: mezera a další znaky
Zatímco silné znaky mají svůj pevně definovaný směr, neutrální znaky přebírají směr dle okolí. Slabé znaky jsou něco mezi: mají sice svůj směr (například čísla se i v arabském textu píší zleva doprava), netvoří ale samostatný běh.
Algoritmus pro převod logické reprezentace obousměrného textu na odpovídající vizuální formu říká zhruba toto:
- Pokud je výchozí směr RTL, reverzuj celý text
- Rozpoznej silné, slabé a neutrální znaky
- Rozděl text na běhy
- Reverzuj běhy, které mají jiný směr než výchozí
- Nalezni a reverzuj čísla v RTL bězích
Jak ukazuje článek Understanding Bidirectional (BIDI) Text in Unicode, pro nalezení běhů lze s výhodou použít explicitních značek pro run embedding - ovšem pouze pokud je umí vložit příslušný textový editor.
Mně však nezbylo než nalézt si své běhy sám. Nejprve jsem pomocí krátkého skriptu v Pythonu analyzoval volně dostupná Unicode data a vytvořil si seznam slabých, neutrálních a silných RTL znaků. Všechny ostatní znaky jsem automaticky považoval za silné LTR. Na základě této znalosti jsem pak mohl projít celý řetězec a každý silný znak s opačným směrem označit jako začátek nového běhu.
Nutno poznamenat, že jsem se zabýval pouze jednořádkovými texty. U celých odstavců je situace komplikovanější, neboť nejprve je zapotřebí text zalomit a poté aplikovat pravidla na každý řádek zvlášť.
Pěkné shrnutí celé problematiky je k nalezení na stránkách W3C, samotný algoritmus je popsaný na webu Unicode konzorcia.
Kontextové formy
Poslední věcí nutnou pro správné zobrazení arabského textu je výběr kontextových forem jednotlivých znaků a nahrazení specifických kombinací znaků ligaturami.
Připravit celý seznam kontextových forem z tabulky může být celkem únavné, naštěstí existují již hotové implementace. Z ligatur je povinná pouze kombinace lám + alif (U+FEF5 až U+FEFC), mezi další často používané patří alif + hamza nebo alif + madda. Nezapomeňte, že arabský text se prochází zprava doleva.
Nakonec nezbývá než výsledný text zobrazit na displeji a kochat se. Tak tedy حظا سعيدا,מתכנתים.
comments powered by Disqus
Groovy: řekni prostě, co chceš
My vývojáři už jsme si na nějakou tu nesrozumitelnost v kódu za ty roky zvykli. Víme, co dělá ta která funkce, a taky nám dlouho trvalo, než jsme jim vymysleli ta dlouhá a zcela nepochopitelná jména. Rovněž jsme dřív psali mnohastránkové elaboráty s popisem rozhraní, ale teď jedeme agilně, a tak pro jistotu nepíšeme vůbec nic.
Web na zelené louce IV.
Výborně, sajta nám jede, stěrače stírají a stránky v oblíbeném prohlížeči najíždějí jako po másle. Nezbývá, než se přihlásit do administrace a sepsat nějaké ty duchaplné texty pro ctihodné čtenáře.
Manuál, kam se podíváš
Při psaní jakéhosi user manuálu mě napadlo: budou to ti uživatelé vůbec chtít číst v papírové podobě, nebo si výsledné PDF zkrátka otevřou na počítači? A co by říkali na možnost mít celou dokumentaci neustále při ruce, třeba v mobilu nebo v e-ink čtečce? Neoplatí se připravit několik výsledných formátů pro různé použití?
WebExpo, kedlubny a flanelové košile
Z letošního WebExpa jsem si opět odnesl pár nových poznatků, spoustu inspirace a díky veselé hře s QR kódy a povedené sobotní party i několik nových kontaktů. Nebudu ale zdlouhavě přežvýkávat obsah jednotlivých přednášek, to už učinili mnozí jiní. Jde mi totiž o něco z mého pohledu mnohem podstatnějšího. O přetékání.