作者:李智敏,华清远见嵌入式学院上海中心讲师。
在嵌入式系统开发中,某些产品可能会需要跨区域销售,因此,通常会有多语言的需求。对于这一类多语言需求的解决,在嵌入式产品中有其特殊的地方。以下,给出一种可能的解决方案。
该方案的核心思想是为所有文本建立索引,通过索引可以得到特定语言的文字编码,随后通过该编码获得字库资源,并进行输出。在这过程中,唯一需要注意的是对于特殊的某些语言,如阿拉伯语等的处理。(阿拉伯语字符在连写时,其形状会发生变化。)
1. 字库的建立:
文本最终都将输出给用户,因此,必须为文本内容指定字库。本方案中采用UNICODE编码字库。字库文件采用二进制存储,按UNICODE编码顺序排列存储点阵数据,点阵大小为24*24。
2. 文本资源文件:
文本资源文件描述了特定语言的文本内容,以及相关的字符编码。例如对于Chinese.cfg文件来说,就保留了一个索引为1的文本,该文本内容为“确认”;相应对于English.cfg文件来说,必然会同样有一个索引为1的文本,该文本内容为“Confirm”。通过对所有的文本建立索引并生成文本资源文件,就为最终的解决扫清了障碍。
文本资源文件采用二进制存储。文件头部16个字节为描述性信息,之后是文本映射表,紧跟映射表之后为文本的实际Unicode编码。
3. 对文本资源文件进行描述的数据结构
typedef struct _txtres_fileheader {
LONG lFileType; //文件类型,0x2E434647='.CFG'
LONG lVersionNum; //适用版本,0x56313032='V102'
LONG lMapOffset; //偏移量,文件头到文本映射区的偏移量
LONG lDataOffset; //偏移量,文件头到文本数据区的偏移量
} APPTEXT_FILEHEADER;
4. 文本映射表结构
typedef struct _txtres_txtmap {
WORD wTextIndes; //当前文本的索引值
WORD wTextSize; //当前文本的Unicode编码所占用的字节数
LONG lUnicodeOffset; //从文件头到当前文本Unicode编码存储位置的偏移量
} TXTRES_TXTMAP;
5. 特殊语言(阿拉伯语等)的解决
特殊语言在连写时可能发生变化,因此采用固定字库可能无法解决该问题。针对这种状况可以直接新增一个自定义字库。以阿拉伯语为例,该字库的处理过程如下:
a. 首先将阿拉伯的文本内容按预定格式(例如24*24)在windows系统上显示输出,并将内容保存为图片格式。此时图片中便为连写内容。
b. 随后,对图片进行分割。如按照24*24进行分割便可得到特定的24*24大小的字库内容。
c. 最后,将原先的UNICODE编码转为按照之前生成的字库来编码。
d. 之后在程序代码中就可利用自定义字库与自定义编码来显示阿拉伯语。
最后附上部分示例代码。
//定义文本配置文件路径
#define TXT_FILE_ENGLISH "config/English.cfg"
#define TXT_FILE_CHINASIM "config/ChinaSim.cfg"
#define TXT_FILE_CHINATRA "config/ChinaTra.cfg"
#define TXT_FILE_KOREAN "config/Korean.cfg"
#define TXT_FILE_JAPANESE "config/Japanese.cfg"
#define TXT_FILE_SPANISH "config/Spanish.cfg"
#define TXT_FILE_RUSSIAN "config/Russian.cfg"
#define TXT_FILE_THAI "config/Thai.cfg"
#define TXT_FILE_GERMAN "config/German.cfg"
#define TXT_FILE_FRANCE "config/France.cfg"
#define TXT_FILE_ITALY "config/Italy.cfg"
#define TXT_FILE_ARABIA "config/Arabia.cfg"
#define TXT_FILE_PORTUGAL "config/Portugal.cfg"
#define TXT_FILE_HINDI "config/Hindi.cfg"
#define TXT_FILE_TURKISH "config/Turkish.cfg"
#define TXT_FILE_VIETNAM "config/Vietnam.cfg"
#define TXT_FILE_SWIDISH "config/Swedish.cfg"
#define TXT_FILE_POLISH "config/Polish.cfg"
//根据文本索引及文本语言,读取相应的文本配置文件,以得到该文本,成功返回有效指针
GUISTRING * GetTextResource(LONG lIndex, LONG lLanguage)
{
GUISTRING * pTxt;
APPTEXT_FILEHEADER fh;
APPTEXT_MAPPING map;
STRING strFile;
WORD * pBuf;
int fd, iOff;
//确定要读取的配置文件
switch (lLanguage)
{
case TXT_LANG_ENGLISH:
strFile = TXT_FILE_ENGLISH;
break;
case TXT_LANG_CHINASIM:
strFile = TXT_FILE_CHINASIM;
break;
case TXT_LANG_CHINATRA:
strFile = TXT_FILE_CHINATRA;
break;
case TXT_LANG_KOREAN:
strFile = TXT_FILE_KOREAN;
break;
case TXT_LANG_JAPANESE:
strFile = TXT_FILE_JAPANESE;
break;
case TXT_LANG_SPANISH:
strFile = TXT_FILE_SPANISH;
break;
case TXT_LANG_RUSSIAN:
strFile = TXT_FILE_RUSSIAN;
break;
case TXT_LANG_THAI:
strFile = TXT_FILE_THAI;
break;
case TXT_LANG_GERMAN:
strFile = TXT_FILE_GERMAN;
break;
case TXT_LANG_FRANCE:
strFile = TXT_FILE_FRANCE;
break;
case TXT_LANG_ITALY:
strFile = TXT_FILE_ITALY;
break;
case TXT_LANG_ARABIA:
strFile = TXT_FILE_ARABIA;
break;
case TXT_LANG_PORTUGAL:
strFile = TXT_FILE_PORTUGAL;
break;
case TXT_LANG_HINDI:
strFile = TXT_FILE_HINDI;
break;
case TXT_LANG_TURKISH:
strFile = TXT_FILE_TURKISH;
break;
case TXT_LANG_VIETNAM:
strFile = TXT_FILE_VIETNAM;
break;
case TXT_LANG_SWIDISH:
strFile = TXT_FILE_SWIDISH;
break;
case TXT_LANG_POLISH:
strFile = TXT_FILE_POLISH;
break;
default:
return NULL;
}
//打开配置文件并检查其格式
if ((fd = open(strFile, O_RDONLY)) == -1)
{
return NULL;
}
if (read(fd, &fh, 16) != 16)
{
close(fd);
return NULL;
}
if (fh.lFileType != 0x4746432E || fh.lVersionNum != 0x32303156)
{
close(fd);
return NULL;
}
//在文本映射区内查找匹配的文本索引
for (iOff = fh.lMapOffset; iOff < fh.lDataOffset; iOff += 8)
{
if (read(fd, &map, 8) != 8)
{
close(fd);
return NULL;
}
if (map.wTextIndex == lIndex)
{
break;
}
}
if (iOff >= fh.lDataOffset)
{
close(fd);
return NULL;
}
//根据找到的文本映射来读取文本内容
if (!(pBuf = GuiMemAlloc(map.wTextSize + 2)))
{
close(fd);
return NULL;
}
lseek(fd, fh.lDataOffset + map.lTextOffset, SEEK_SET);
if (read(fd, pBuf, map.wTextSize) != map.wTextSize)
{
GuiMemFree(pBuf);
close(fd);
return NULL;
}
pBuf[map.wTextSize >> 1] = 0;
//建立字符串对象
pTxt = CreateStringDirect(pBuf);
GuiMemFree(pBuf);
close(fd);
return pTxt;
}
//定义与字符串相关的数据结构
#ifndef GUI_STRING_STRUCT
typedef struct _string
{
WORD wWidth; //字符串宽度,字符串输出时的总点阵宽度
WORD wLength; //字符串长度,不包括'\0'
WORD * pContent; //字符串内容,以'\0'结尾
} GUISTRING;
//结束与字符串相关的数据结构的定义
#define GUI_STRING_STRUCT
#endif
/***
* 功能:
根据指定的信息直接建立字符串,函数返回字符串指针
* 参数:
1.WORD * pContent: 字符串内容
* 返回:
成功返回字符串指针
失败返回NULL
* 备注:
***/
GUISTRING * CreateStringDirect(WORD * pContent)
{
GUISTRING * pStr;
//尝试为字符串分配内存
if (!(pStr = GuiMemAlloc(sizeof(GUISTRING))))
{
PRINT_INF(CreateStringDirect Err0!);
return NULL;
}
//字符串内容为空,建立一个空字符串对象
if (!pContent)
{
pStr->wWidth = 0;
pStr->wLength = 0;
pStr->pContent = NULL;
return pStr;
}
//统计字符串长度
pStr->wLength = 0;
pStr->pContent = pContent;
while (*pStr->pContent++)
{
pStr->wLength++;
}
//尝试为字符串内容分配内存
if (!(pStr->pContent = GuiMemAlloc((pStr->wLength + 1) << 1)))
{
GuiMemFree(pStr);
PRINT_INF(CreateStringDirect Err1!);
return NULL;
}
//写入字符串内容
memcpy(pStr->pContent, pContent, (pStr->wLength + 1) << 1);
//计算字符串宽度
if (_StringObjectFill(pStr))
{
GuiMemFree(pStr->pContent);
GuiMemFree(pStr);
PRINT_INF(CreateStringDirect Err2!);
return NULL;
}
return pStr;
}
文章评论(0条评论)
登录后参与讨论