📄 minigui 体系结构之三逻辑字体以及多字体和多字符集实现.htm
字号:
ISO8859 的字符,而“中文”是属于 GB2312 的字符。通过调用这两个字符集操作集中的函数,我们就可以了解该字符串中哪些字符是属于
ISO8859 的字符,哪些字符是属于 GB2312 的字符,甚至可以进行更加复杂的分析。比如,MiniGUI 中的 GetFirstWord
函数可以从这种字符串中获得第一个单词。比如“ABC DEF
中文”字符串中的第一个单词是“ABC”,而第二个单词是“DEF”,第三个单词和第四个单词分别是“中”和“文”。该函数的实现如下:</P>
<TABLE class=code-sample cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><PRE>int GUIAPI GetFirstWord (PLOGFONT log_font, const char* mstr, int len,
WORDINFO* word_info)
{
DEVFONT* sbc_devfont = log_font->sbc_devfont;
DEVFONT* mbc_devfont = log_font->mbc_devfont;
if (mbc_devfont) {
int mbc_pos;
mbc_pos = (*mbc_devfont->charset_ops->pos_first_char) (mstr, len);
if (mbc_pos == 0) {
len = (*mbc_devfont->charset_ops->len_first_substr) (mstr, len);
(*mbc_devfont->charset_ops->get_next_word) (mstr, len, word_info);
return word_info->len + word_info->nr_delimiters;
}
else if (mbc_pos > 0)
len = mbc_pos;
}
(*sbc_devfont->charset_ops->get_next_word) (mstr, len, word_info);
return word_info->len + word_info->nr_delimiters;
}
</PRE></TD></TR></TBODY></TABLE>
<P>该函数首先判断该逻辑字体是否包含多字节设备字体(mbc_devfont是否为空),如果是,则调用多字节字符集对应的操作函数
pos_first_char、len_first_substr、get_next_word 等函数获得第一个单词信息,并填充 word_info
结构。如果该逻辑字体只包含单字节设备字体,则直接调用单字节字符集对应的操作函数 get_next_word。一般而言,在 GetFirstWord
等函数中,我们首先要进行多字节字符集的某些判断,比如 pos_first_char
返回的是字符串中属于该字符集的第一个字符的位置。如果返回值不为零,表明第一个字符是单字节字符;如果为零,才会调用其他函数进行操作。</P>
<P>有了这样的逻辑字体、设备字体和字符集结构定义,当我们需要新添加一种字符集或者字体支持时,只需按照我们的字体操作集和字符集操作集定义对应的新操作集结构即可,而对上层程序没有任何影响。</P><A
id=3 name=3></A>
<P><STRONG class=subhead>3 MiniGUI 中的字符集支持</STRONG> <STRONG>3.1
字符集操作集</STRONG></P>
<P>在 MiniGUI
中,每个特定的字符集由对应的字符集操作集来表示。字符集操作集的定义如下(include/gdi.h。前面的数字表示在该文件中的行数,下同):</P>
<TABLE class=code-sample cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><PRE> 250 typedef struct _CHARSETOPS
251 {
252 int nr_chars; // 该字符集中字符的个数
253 int bytes_per_char; // 每个字符的平均字节数
254 int bytes_maxlen_char; // 字符的最大字节数
255 const char* name; // 字符集名称
256 char def_char [MAX_LEN_MCHAR]; // 默认字符
257
258 int (*len_first_char) (const unsigned char* mstr, int mstrlen);
259 int (*char_offset) (const unsigned char* mchar);
260
261 int (*nr_chars_in_str) (const unsigned char* mstr, int mstrlen);
262
263 int (*is_this_charset) (const unsigned char* charset);
264
265 int (*len_first_substr) (const unsigned char* mstr, int mstrlen);
266 const unsigned char* (*get_next_word) (const unsigned char* mstr,
267 int strlen, WORDINFO* word_info);
268
269 int (*pos_first_char) (const unsigned char* mstr, int mstrlen);
270
271 #ifndef _LITE_VERSION
272 unsigned short (*conv_to_uc16) (const unsigned char* mchar, int len);
273 #endif /* !LITE_VERSION */
274 } CHARSETOPS;
</PRE></TD></TR></TBODY></TABLE>
<P>其中,前几个字段(nr_chars、bytes_per_char、bytes_maxlen_char、name、def_char
等)表示了该字符集的一些基本信息,具体含义参见注释。这里需要对 bytes_maxlen_char 和 def_chat 作进一步解释:</P>
<UL>
<LI>bytes_maxlen_char
用来表示该字符集中字符的最长字节数。通常情况下,一个字符集中的每个字符的长度一般是定长的,但是也有许多例外,比如在
GB18303、UNICODE 等字符集中,字符的最长字节数可能超过 4 字节。
<LI>def_char
用来表示该字符集中的默认字符。该字段主要和字体配合使用。当某个针对该字符集的字体中缺少一些字符的定义时,就需要用默认字体替代这些缺少的字符。
</LI></UL>
<P>在上述字符集的操作集定义中,后几个字段定义为函数指针,它们均由逻辑字体接口用来进行文本分析:</P>
<UL>
<LI>len_first_char 返回多字节字符串中第一个属于该字符集的字符的长度。若不属于该字符集,则返回 0。
<LI>char_offset 返回某个字符在该字符集中的位置。该信息可以由设备字体使用,用来从一个字体文件中获取该字符对应的宽度或点阵。
<LI>nr_chars_in_str 计算字符串中属于该字符集的字符个数并返回。注意,传入的字符串必须均为该字符集字符。
<LI>is_this_charset 判断给定的用来表示字符集的名称是否指该字符集。因为对某种特定的字符集,其名称不一定和 name
字段所定义的名称匹配。比如,对 GB2312 字符集,就可能有 gb2312-1980.0、GB2312_80
等各种不同的名称。该函数可以帮助正确判断一个名称是否指该字符集。
<LI>len_first_substr 返回某个多字节字符串中属于该字符集的子字符串长度。如果第一个字符不属于该字符集,则返回为 0。
<LI>get_next_word
返回多字节字符串中属于该字符集的字符串中下一个单词的信息。对欧美语言来说,单词之间由空格、标点符号、制表符等相隔;对亚洲语言来说,单词通常定义为字符。
<LI>pos_first_char 该函数返回多字节字符串中属于该字符集的第一个字符的位置。
<LI>conv_to_uc16 该函数将某个属于该字符集的字符,转换为 UNICODE 的 16 位内码。该函数主要用来从 TrueType
字体中获得字符的轮廓信息。因为 TrueType 字体使用 UNICODE 定位字符,所以需要这个函数完成特定字符集内码到 UNICODE
内码的转换。由于 MiniGUI-Lite 版本尚不支持 TrueType 字体,所以该函数在 MiniGUI-Lite 版本中无需定义。
</LI></UL>
<P>在 src/font/charset.c 中,定义了系统支持的所有字符集操作集,并由函数 GetCharsetOps
返回某个字符集名称对应的字符集操作集(src/font/charset.c):</P>
<TABLE class=code-sample cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><PRE> 716 static CHARSETOPS* Charsets [] =
717 {
718 &CharsetOps_iso8859_1,
719 &CharsetOps_iso8859_5,
720 #ifdef _GB_SUPPORT
721 &CharsetOps_gb2312,
722 #endif
723 #ifdef _BIG5_SUPPORT
724 &CharsetOps_big5,
725 #endif
726 #ifdef _EUCKR_SUPPORT
727 &CharsetOps_euckr,
728 #endif
729 #ifdef _UJIS_SUPPORT
730 &CharsetOps_ujis
731 #endif
732 };
733
734 #define NR_CHARSETS (sizeof(Charsets)/sizeof(CHARSETOPS*))
735
736 CHARSETOPS* GetCharsetOps (const char* charset_name)
737 {
738 int i;
739
740 for (i = 0; i &lt; NR_CHARSETS; i++) {
741 if ((*Charsets [i]-&gt;is_this_charset) (charset_name) == 0)
742 return Charsets [i];
743 }
744
745 return NULL;
746 }
747
</PRE></TD></TR></TBODY></TABLE><STRONG>3.2 新字符集的实现举例</STRONG>
<P>如果我们需要定义一种新的字符集支持时,只需在该文件中添加相应的操作集函数以及对应的操作集结构定义即可,比如,对 EUCKR
字符集的支持定义如下(src/font/charset.c):</P>
<TABLE class=code-sample cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><PRE> 468 #ifdef _EUCKR_SUPPORT
469 /************************* EUCKR Specific Operations ************************/
470 static int euckr_len_first_char (const unsigned char* mstr, int len)
471 {
472 unsigned char ch1;
473 unsigned char ch2;
474
475 if (len < 2) return 0;
476
477 ch1 = mstr [0];
478 if (ch1 == '\0')
479 return 0;
480
481 ch2 = mstr [1];
482 if (ch1 >= 0xA1 && ch1 <= 0xFE && ch2 >= 0xA1 && ch2 <= 0xFE)
483 return 2;
484
485 return 0;
486 }
487
488 static int euckr_char_offset (const unsigned char* mchar)
489 {
490 if(mchar [0] > 0xAD)
491 return ((mchar [0] - 0xA4) * 94 + mchar [1] - 0xA1 - 0x8E);
492 else
493 return ((mchar [0] - 0xA1) * 94 + mchar [1] - 0xA1 - 0x8E);
494 }
495
496 static int euckr_is_this_charset (const unsigned char* charset)
497 {
498 int i;
499 char name [LEN_FONT_NAME + 1];
500
501 for (i = 0; i < LEN_FONT_NAME + 1; i++) {
502 if (charset [i] == '\0')
503 break;
504 name [i] = toupper (charset [i]);
505 }
506 name [i] = '\0';
507
508 if (strstr (name, "EUCKR") )
509 return 0;
510
511 return 1;
512 }
513
514 static int euckr_len_first_substr (const unsigned char* mstr, int mstrlen)
515 {
516 unsigned char ch1;
517 unsigned char ch2;
518 int i, left;
519 int sub_len = 0;
520
521 left = mstrlen;
522 for (i = 0; i < mstrlen; i += 2) {
523 if (left < 2) return sub_len;
524
525 ch1 = mstr [i];
526 if (ch1 == '\0') return sub_len;
527
528 ch2 = mstr [i + 1];
529 if (ch1 >= 0xA1 && ch1 <= 0xFE && ch2 >= 0xA1 && ch2 <= 0xFE)
530 sub_len += 2;
531 else
532 return sub_len;
533
534 left -= 2;
535 }
536
537 return sub_len;
538 }
539
540 static int euckr_pos_first_char (const unsigned char* mstr, int mstrlen)
541 {
542 unsigned char ch1;
543 unsigned char ch2;
544 int i, left;
545
546 i = 0;
547 left = mstrlen;
548 while (left) {
549 if (left < 2) return -1;
550
551 ch1 = mstr [i];
552 if (ch1 == '\0') return -1;
553
554 ch2 = mstr [i + 1];
555 if (ch1 >= 0xA1 && ch1 <= 0xFE && ch2 >= 0xA1 && ch2 <= 0xFE)
556 return i;
557
558 i += 1;
559 left -= 1;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -