65.9K
CodeProject 正在变化。 阅读更多。
Home

Windows CE 的阿拉伯语支持

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.90/5 (56投票s)

2003 年 1 月 20 日

CPOL

3分钟阅读

viewsIcon

409441

本文解释了如何在为 Windows CE 编写的应用程序中支持阿拉伯语。

Sample Image - arabicsupport.gif

引言

当 Microsoft Windows CE 首次问世,并且基于它的 PDA 数量迅速增加时,软件市场为一种新型的应用程序广泛开放,我们都想编写新的应用程序或将我们已经为 Win32 编写的应用程序移植到 Windows CE。不幸的是,对于阿拉伯语市场,WinCE 2.11 和 WinCE 3.0 中没有阿拉伯语支持。

很长一段时间后,一些第三方推出了解决方案,并且每个都声称“我们拥有第一个也是最好的 Windows CE(或 PocketPC)阿拉伯语支持”。 他们为所谓的“钱”提供了大量的解决方案。 对于一个贫穷的自由程序员来说,这太多了。 并且,在提供该解决方案的公司之一向我索要 5000 美元来获取他们的解决方案(不要告诉他们我告诉过你它不好),我开始自己编写第一个也是最好的 Windows CE 阿拉伯语支持。 大约是 14 个月前。 这就是我所做的。

问题识别

主要问题是 WinCE 2.11 和 WinCE 3.0 没有对 BiDi(双向)字体和 RTL(从右到左)窗口控件的内在支持。 我不知道为什么。 我认为这不太难,并且我认为它也不会占用太多内存或处理时间来支持这些功能。

在深入探讨之前,我希望您注意到阿拉伯语支持解决方案对字符串的作用,以便以正确的方式显示在窗口上。

阿拉伯语字符串从左到右存储,就像在英语中一样。 但是当它要显示时,字符串中的第一个字符应该是最右边的字符。 此外,在阿拉伯语中,字符可以根据其在单词中的位置(开始、中间、结尾和单独)采用多个字形(图形或形状)。 在支持阿拉伯语的 Windows 和 Unicode 就绪的 Windows(Windows 2000 及更高版本)中,所有这些都在 GDI 中内在实现。 在 WinCE 中,我们必须自己完成这项工作。

解决方案

首先,您必须安装一个阿拉伯语 TrueType 字体。 阿拉伯语支持 Windows 的 Tahoma 非常棒。

阿拉伯语化涉及反转字符串,考虑到数字不应该被反转,并手动执行字形替换。

为了反转字符串,我使用

//////////////////////////////////////////////////////////////////////
void ArabicReverse(CString &s)
{
    CString out, rev;
    s.MakeReverse();

    int i=0;
    while(i<s.GetLength())
    {
        if((s[i]>='0' && s[i]<='9'))    // isDigit(s[i]) ?
        {
            rev.Empty();
            while((s[i]>='0' && s[i]<='9'))    // isDigit(s[i]) ?
            {
                rev = rev + s[i];
                ++i;
            }
            rev.MakeReverse();
            out = out + rev;
        }
        else
        {
            out = out + s[i];
            ++i;
        }
    }
    s=out;
}
//////////////////////////////////////////////////////////////////////

第二步稍微棘手,所以请准备好。 我们知道内存中的阿拉伯语字符串包含 Unicode 字符,这些字符采用字母的独立字形的相同形式。 我们必须将字符映射到要显示的正确字形,考虑到字母的所有状态。

我设法构造了一个包含所有阿拉伯字母状态的数组。 它没有按字母顺序排序。 表中的第一行是我可以收集到的关于一个字母的第一条数据,它是字母“thal”。

据我所知.. 字形替换的规则位于 TTF 文件中的某个位置,但我发现它非常复杂。 为了帮助构造字形替换的规则,我根据它是否可能需要链接到前一个或下一个字符,制作了两组字符。

//////////////////////////////////////////////////////////////////////
CString Arabize (LPCTSTR in)
{
    static struct
    {
        WCHAR character;
        WCHAR endGlyph;
        WCHAR iniGlyph;
        WCHAR midGlyph;
        WCHAR isoGlyph;
    }a[N_DISTINCT_CHARACTERS]=
    {
        {0x630, 0xfeac, 0xfeab, 0xfeac, 0xfeab},
        {0x62f, 0xfeaa, 0xfea9, 0xfeaa, 0xfea9},
        {0x62c, 0xfe9e, 0xfe9f, 0xfea0, 0xfe9d},
        {0x62d, 0xfea2, 0xfea3, 0xfea4, 0xfea1},
        {0x62e, 0xfea6, 0xfea7, 0xfea8, 0xfea5},
        {0x647, 0xfeea, 0xfeeb, 0xfeec, 0xfee9},
        {0x639, 0xfeca, 0xfecb, 0xfecc, 0xfec9},
        {0x63a, 0xfece, 0xfecf, 0xfed0, 0xfecd},
        {0x641, 0xfed2, 0xfed3, 0xfed4, 0xfed1},
        {0x642, 0xfed6, 0xfed7, 0xfed8, 0xfed5},
        {0x62b, 0xfe9a, 0xfe9b, 0xfe9c, 0xfe99},
        {0x635, 0xfeba, 0xfebb, 0xfebc, 0xfeb9},
        {0x636, 0xfebe, 0xfebf, 0xfec0, 0xfebd},
        {0x637, 0xfec2, 0xfec3, 0xfec4, 0xfec1},
        {0x643, 0xfeda, 0xfedb, 0xfedc, 0xfed9},
        {0x645, 0xfee2, 0xfee3, 0xfee4, 0xfee1},
        {0x646, 0xfee6, 0xfee7, 0xfee8, 0xfee5},
        {0x62a, 0xfe96, 0xfe97, 0xfe98, 0xfe95},
        {0x627, 0xfe8e, 0xfe8d, 0xfe8e, 0xfe8d},
        {0x644, 0xfede, 0xfedf, 0xfee0, 0xfedd},
        {0x628, 0xfe90, 0xfe91, 0xfe92, 0xfe8f},
        {0x64a, 0xfef2, 0xfef3, 0xfef4, 0xfef1},
        {0x633, 0xfeb2, 0xfeb3, 0xfeb4, 0xfeb1},
        {0x634, 0xfeb6, 0xfeb7, 0xfeb8, 0xfeb5},
        {0x638, 0xfec6, 0xfec7, 0xfec8, 0xfec5},
        {0x632, 0xfeb0, 0xfeaf, 0xfeb0, 0xfeaf},
        {0x648, 0xfeee, 0xfeed, 0xfeee, 0xfeed},
        {0x629, 0xfe94, 0xfe93, 0xfe93, 0xfe93},
        {0x649, 0xfef0, 0xfeef, 0xfef0, 0xfeef},
        {0x631, 0xfeae, 0xfead, 0xfeae, 0xfead},
        {0x624, 0xfe86, 0xfe85, 0xfe86, 0xfe85},
        {0x621, 0xfe80, 0xfe80, 0xfe80, 0xfe7f},
        {0x626, 0xfe8a, 0xfe8b, 0xfe8c, 0xfe89},
        {0x623, 0xfe84, 0xfe83, 0xfe84, 0xfe83},
        {0x622, 0xfe82, 0xfe81, 0xfe82, 0xfe81},
        {0x625, 0xfe88, 0xfe87, 0xfe88, 0xfe87}
    };
    BOOL linkBefore, linkAfter;
    CString out;
    out=in;
    for(UINT i=0; i<_tcslen(in); i++)
    {
        WCHAR ch=in[i];
        if(((ch>=0x0621 && ch<=0x064a)) // is an Arabic character?
        {
            int idx = 0;
            while (idx < N_DISTINCT_CHARACTERS)
            {
                if (a[idx].character == in[i])
                    break;
                ++idx;
            }
            
            if(i == _tcslen(in) - 1)
                linkAfter=0;
            else
                linkAfter = (isFromTheSet1(in[i+1]) || 
                                   isFromTheSet2(in[i+1]));
            if(i == 0)
                linkBefore=0;
            else
                linkBefore=isFromTheSet1(in[i-1]);
    
            if(linkBefore && linkAfter)
                out.SetAt(i, a[idx].midGlyph);
            if(linkBefore && !linkAfter)
                out.SetAt(i, a[idx].endGlyph);
            if(!linkBefore && linkAfter)
                out.SetAt(i, a[idx].iniGlyph);
            if(!linkBefore && !linkAfter)
                out.SetAt(i, a[idx].glyph);
        }
    }
    ArabicReverse (out);
    return out;
}
//////////////////////////////////////////////////////////////////////
BOOL BzArabicRender::isFromTheSet1(WCHAR ch)
{
    static WCHAR theSet1[22]={
        0x62c, 0x62d, 0x62e, 0x647, 0x639, 0x63a, 0x641, 0x642,
        0x62b, 0x635, 0x636, 0x637, 0x643, 0x645, 0x646, 0x62a,
        0x644, 0x628, 0x64a, 0x633, 0x634, 0x638};
    int i = 0;
    while (i < 22)
    {
        if(ch == theSet1[i])
            return TRUE;
        ++i;
    }
    return FALSE;
}
//////////////////////////////////////////////////////////////////////
BOOL BzArabicRender::isFromTheSet2(WCHAR ch)
{
    static WCHAR theSet2[12]={
        0x627, 0x623, 0x625, 0x622, 0x62f, 0x630, 0x631, 0x632,
        0x648, 0x624, 0x629, 0x649};
    int i = 0;
    while (i < 12)
    {
        if(ch == theSet2[i])
            return TRUE;
        ++i;
    }
    return FALSE;
}
//////////////////////////////////////////////////////////////////////

要使用它.. 只需将您想要的窗口的 SetWindowText 设置为 Arabize() 的输出。

window.SetWindowText(str); //before
window.SetWindowText(Arabize(str)); //after

or

dc.DrawText(str,……); //before
dc.DrawText(Arabize(str),……); //after

… etc

因此,如果您已经为英语编写了旧代码,那么代码中没有太大变化。

当然,这不是理想的解决方案。 我认为最佳的解决方案是钩住 DrawText 函数并添加所有这些内容。 但是,当我编写此代码时,没有在线材料显示如何钩住 Windows CE API。

我将更新文档以显示如何从字形中检索字符以尽快处理它们。 或者,它可能是另一篇文章。

© . All rights reserved.