关于文本对象模型

文本对象模型 (TOM) 定义了一组文本操作界面,包括 Rich Edit 控件在内的多个 Microsoft 文本解决方案在不同程度上都支持这些界面。 本主题提供 TOM 的高级概述。 讨论以下主题。

TOM 版本 2 对象

TOM 版本 2 (TOM 2) 扩展了原始文本对象模型;新接口是从旧接口派生而来的。 更新的 TOM API 包括对新字符和段落格式属性、表格模型、多选和对 math 和 ruby 的内联对象支持。

顶级 TOM 2 对象由 ITextDocument2 接口定义,该接口具有用于创建和检索对象层次结构中较低级别的对象的方法。 对于简单的纯文本处理,可以从ITextDocument2 对象获取 ITextRange2 对象,并使用该对象执行大部分操作。 如果需要添加富文本格式,可以从 ITextRange2 对象获取 ITextFont2ITextPara2 对象。 ITextFont2 提供与 Microsoft Word 格式字体对话框等效的编程功能; ITextPara2 提供 Word 格式段落对话框等效的项。

除了这三个较低级别的对象外,TOM 2 还有一个选择对象 (ITextSelection2),它是一个 ITextRange2 对象,其中突出显示了选定内容和一些面向 UI 的方法。

范围和选择对象包括面向屏幕的方法,这些方法使程序能够检查屏幕上的文本或可滚动到屏幕上的文本。 例如,这些功能有助于使视力受损的人能够访问文本。

每个具有 2 后缀的接口都继承自没有 2 后缀的相应接口。 例如,ITextDocument2 继承自 ITextDocument

TOM 2 对象具有以下层次结构。

ITextDocument2         Top-level editing object
    ITextRange2        Primary text interface: a range of text
        ITextFont2     Character-attribute interface
        ITextPara2     Paragraph-attribute interface
        ITextRow       Table interface
    ITextSelection2    Screen highlighted text range
        ITextRange2    Selection inherits all range methods
    ITextDisplays      Displays collection (not yet defined)
    ITextStrings       Rich-text strings collection
    ITextStoryRanges2  Enumerator for stories in document

ITextDocument2 对象描述一个或多个连续的文本范围,称为情景。 情景表示文档的各个部分,例如文档的主文本、页眉和页脚、脚注、批注和富文本暂存板。 在线性格式的数学表达式和内置窗体之间进行转换时,将使用暂存板情景。 当内容即将更改时,在保存作为当前复制源的范围的内容时,也会使用暂存板情景。

ITextRange2 对象由其开始和结束字符位置偏移量和情景对象定义。 它不与其父情景对象独立存在,尽管其文本可以复制到剪贴板或其他目标。 文本范围对象不同于电子表格和其他范围对象,这些对象由其他类型的偏移量定义:例如,行/列或图形位置 (x, y)。 文本范围对象可以以各种方式修改自身,可以返回自身的重复项,并且可以命令复制其开始和结束字符位置以及指向当前所选内容的情景指针。

不需要显式情节对象,因为始终可以创建 ITextRange 对象来表示任何给定的情景。 具体而言,ITextDocument 对象可以创建一个 ITextStoryRanges 对象,以范围来枚举文档中的情景,其中包含描述完整情景(如 0 和 tomForward)的起始和结束字符位置值。

使用 ITextStoryRanges2 对象时,不需要显式情景对象,因为每个情景都由 ITextRange2 对象描述。 具体而言,ITextDocument2 对象可以创建一个 ITextStoryRanges2 对象,以范围来枚举文档中的情景,其中包含描述完整情景(如 0 和 tomForward)的起始和结束字符位置值。

ITextRow 接口与 ITextRange::MoveITextRange::Expand 方法一起提供插入、查询和更改表的功能。

TOM 接口约定

所有 TOM 方法都返回 HRESULT 值。 通常,TOM 方法返回以下标准值。

  • E_OUTOFMEMORY
  • E_INVALIDARG
  • E_NOTIMPL
  • E_FILENOTFOUND
  • E_ACCESSDENIED
  • E_FAIL
  • CO_E_RELEASED
  • NOERROR(与 S_OK 相同)
  • S_FALSE

请注意,如果删除与 TOM 对象(如 ITextRange)关联的编辑实例,TOM 对象将变得无用,并且其所有方法都将返回 CO_E_RELEASED。

除了 HRESULT 返回值之外,许多方法还包括用于返回值的指针。 对于所有接口,应检查所有指针参数,以确保它们在使用前是非零的。 如果将 null 值传递给需要有效指针的方法,该方法将返回 E_INVALIDARG。 带有 null 值的可选输出指针将被忽略。

使用具有 Get 和 Set 前缀的方法来获取和设置属性。 布尔变量将 tomFalse (0) 用于 FALSE,将 tomTrue (-1) 用于 TRUE

TOM 常量在 tomConstants 枚举类型中定义,以前缀 tom 开头,例如 tomWord

tomBool 类型

许多 TOM 方法对具有二进制状态的富文本属性使用名为“tomBool”的特殊类型的变量。 tomBool 类型不同于 布尔值类型,因为它可以采用四个值:tomTruetomFalsetomToggletomUndefinedtomTruetomFalse 值指示 true 和 false。 tomToggle 值用于切换属性。 tomUndefined 值(更传统的称为 NINCH)是一种特殊的无输入、无更改值,适用于 longs、floats 和 COLORREF。 对于字符串,tomUndefined(或 NINCH)由 null 字符串表示。 对于属性设置操作,使用 tomUndefined 不会更改目标属性。 对于属性获取操作,tomUndefined 表示区域中的字符具有不同的值(它在属性对话框中显示灰色复选框)。

Math BuildUp 和 Build Down

可以使用 ITextRange2::BuildUpMath 方法将线性格式的数学表达式转换为内置版本。 ITextRange2::Linearize 方法执行相反的转换(称为线性化或向下生成)将数学表达式的内置版本转换回线性格式。 需要导出纯文本或启用某些类型的编辑时,数学生成功能非常有用。

TOM RTF

在 TOM 中,富文本交换可以通过一组显式方法调用或 RTF 中的富文本传输来实现。 本节为段落属性和字符属性提供 RTF 控制字的表格。

TOM RTF 段落控制字

控制字 含义
\ fi n 第一行缩进(默认值为零)。
\ keep 保持段落不变。
\ keepn 继续下一段。
\ li n 左缩进(默认值为零)。
\ noline 没有行编号。
\ nowidctlpar 关闭末行/孤立行控件。
\ pagebb 在段落前换页。
\ par 新段落。
\ pard 重置为默认段落属性。
\ ql 左对齐(默认值)。
\ qr 右对齐。
\ qj 对齐。
\ qc 居中对齐。
\ ri n 右缩进(默认值为零)。
\ s n 样式 n
\ sa n 后面有空格(默认值为零)。
\ sb n 前面有空格(默认值为零)。
\ sl n 如果缺少或 n=1000,则行距由行中最高的字符确定(单行间距);如果为 n> 为零,则至少使用此大小;如果 n < 零,则完全使用 |n|。 如果 \ slmult 1 紧随其后,则行距为多行间距。
\ slmult m 紧跟 \ sl. m = 零:至少或完全符合 \ sl n. m = 1: 行间距 = n/240 倍单行间距所规定的的间距。
\ tb n 栏制表符位置(以缇为单位),从左边距开始。
\ tldot 制表符前导点。
\ tleq 制表符前导等号。
\ tlhyph 制表符前导连字符。
\ tlth 制表符前导粗线。
\ tlul 制表符前导符下划线。
\ tqc “居中对齐”选项卡。
\ tqdec 十进制选项卡。
\ tqr 右对齐选项卡。
\ tx n 制表符位置(以缇为单位),从左边距开始。

 

TOM RTF 字符格式控制字

控制字 含义
\ animation n 将动画类型设置为 n
\b 粗体。
\ caps 全部大写。
\ cf n 前景色(默认值为 tomAutocolor)。
\ cs n 字符样式 n
\ dn n 以半点为单位的下标位置(默认值为 6)。
\ embo 浮雕。
\ f n 字号,n 表示字体表中的条目。
\ fs n 以半点为单位的字体大小(默认值为 24)。
\ highlight n 背景色(默认值为 tomAutocolor)。
\ i 斜体。
\ impr 印记。
\ lang n 将语言应用于字符。 n 是对应于语言的数字。 \ plain 控制字将语言属性重置为文档属性中 deflang n 定义的语言。
\ nosupersub 关闭上标或下标。
\ outl 轮廓。
\ plain 将字符格式属性重置为应用程序定义的默认值。 关联的字符格式属性(RTF 规范中的关联字符属性部分中所述)也会重置。
\ scaps 小写字母。
\ shad 阴影。
\ strike 删除线。
\ sub 将下标应用于文本,并根据字体信息减小点大小。
\ super 将上标应用于文本,并根据字体信息减小点大小。
\ ul 连续下划线。 \ ul0 关闭所有下划线。
\ uld 虚线下划线。
\ uldb 双下划线。
\ ulnone 停止所有下划线。
\ ulw 词下划线。
\ up n 以半点为单位的上标位置(默认值为 6)。
\v 隐藏文本。

 

查找 Rich Text

可以使用 TOM 方法查找由一系列文本定义的富文本。 在文字处理中经常需要精确地找到这样的富文本,尽管在“所见即所得”(WYSIWYG) 的文字处理程序中从来没有实现过。 显然,有一个较大的格式文本匹配域,允许忽略某些字符格式属性(或包括段落格式和/或对象内容),但这种通用化超出了本部分的范围。

此功能的一个用途是使用富文本查找对话框来定义要在文档中查找的富文本。 对话框将使用丰富的编辑控件实现,TOM 方法将用于通过文档执行搜索。 可以将所需格式文本从文档复制到查找对话框中,也可以直接在查找对话框中输入格式。

以下示例演示如何使用 TOM 方法查找包含精确字符格式组合的文本。 该算法在名为 pr1 的匹配范围内搜索纯文本。 如果找到纯文本,则会通过名为 pr2 的试用范围指向它。 然后,使用两个插入点范围(prip1prip2)遍历试验范围,将其字符格式与 pr1 的字符格式进行比较。 如果它们完全匹配,则输入范围(由 ppr 给定)更新为指向试用范围的文本,并且该函数返回匹配区域中的字符计数。 字符格式比较中使用两个 ITextFont 对象 pf1pf2。 它们附加到插入点范围 prip1prip2

LONG FindRichText (
    ITextRange **ppr,             // Ptr to range to search
    ITextRange *pr1)              // Range with rich text to find
{
    BSTR        bstr;             // pr1 plain-text to search for
    LONG        cch;              // Text string count
    LONG        cch1, cch2;       // tomCharFormat run char counts
    LONG        cchMatch = 0;     // Nothing matched yet
    LONG        cp;               // Handy char position
    LONG        cpFirst1;         // pr1 cpFirst
    LONG        cpFirst2;         // pr2 cpFirst
    ITextFont  *    pf1, *pf      // Fonts corresponding to IPs prip1 and prip2
    ITextRange *pr2;              // Range duplicate to search with
    ITextRange *prip1, *prip      // Insertion points to walk pr1, pr2

    if (!ppr || !*ppr || !pr1)
        return E_INVALIDARG;

    // Initialize range and font objects used in search
    if ((*ppr)->GetDuplicate(&pr2)    != NOERROR ||
        pr1->GetDuplicate(&prip1)     != NOERROR ||
        pr2->GetDuplicate(&prip2)     != NOERROR ||
        prip1->GetFont(&pf1)          != NOERROR ||
        prip2->GetFont(&pf2)          != NOERROR ||
        pr1->GetText(&bstr)           != NOERROR )
    {
        return E_OUTOFMEMORY;
    }

    pr1->GetStart(&cpFirst1);

    // Keep searching till rich text is matched or no more plain-text hits
    while(!cchMatch && pr2->FindText(bstr, tomForward, 0, &cch) == NOERROR)
    {
        pr2->GetStart(&cpFirst2);                 // pr2 is a new trial range
        prip1->SetRange(cpFirst1, cpFirst1);      // Set up IPs to scan match
        prip2->SetRange(cpFirst2, cpFirst2);      //  and trial ranges

        while(cch > 0 &&
            pf1->IsEqual(pf2, NULL) == NOERROR)   // Walk match & trial ranges
        {                                         //  together comparing font
            prip1->GetStart(&cch1);               //  properties
            prip1->Move(tomCharFormat, 1, NULL);
            prip1->GetStart(&cp);
            cch1 = cp - cch1;                     // cch of next match font run

            prip2->GetStart(&cch2);
            prip2->Move(tomCharFormat, 1, NULL);
            prip2->GetStart(&cp);
            cch2 = cp - cch2;                      // cch of next trial font run

            if(cch1 < cch)                         // There is more to compare
            {
                if(cch1 != cch2)                   // Different run lengths:
                    break;                         //  no formatting match
                cch = cch - cch1;                  // Matched format run
            }
            else if(cch2 < cch)                    // Trial range format run too
                break;                             //  short

            else                                   // Both match and trial runs
            {                                      //  reach at least to match
                pr2->GetEnd(&cp);                  //  text end: rich-text match
                (*ppr)->SetRange(cpFirst2, cp)     // Set input range to hit
                cchMatch = cp - cpFirst2;          //  coordinates and return
                break;                             //  length of matched string
            }
        }
    }
    pr2->Release();
    prip1->Release();
    prip2->Release();
    pf1->Release();
    pf2->Release();
    SysFreeString(bstr);

    return cchMatch;
}

TOM 辅助功能

TOM 通过 ITextSelectionITextRange 接口提供辅助功能支持。 本部分介绍对辅助功能有用的方法,以及程序如何确定对象的 xy 屏幕位置。

由于基于 UI 的辅助功能程序通常适用于屏幕和鼠标,因此一个共同的问题是查找当前鼠标位置的相应 ITextDocument 接口(在屏幕坐标中)。 以下部分提供了两种方法来确定正确的接口:

有关详细信息,请参阅 Microsoft Active Accessibility 规范。 从屏幕位置获取对象后,可以用于 ITextDocument 接口,并调用 RangeFromPoint 方法以在与屏幕位置对应的 cp 处获取空范围对象。

来自运行对象表的接口

运行对象表 (ROT) 指示哪些对象实例处于活动状态。 通过查询此表,可以加速在对象正在运行时将客户端连接到对象的过程。 在程序可以通过正在运行的对象表访问 TOM 接口之前,具有窗口的 TOM 实例需要使用名字对象在 ROT 中注册。 可以从包含其 HWND 十六进制值的字符串构造名字对象。 以下代码示例显示如何执行此操作。

// This TOM implementation code is executed when a new windowed 
// instance starts up. 
// Variables with leading underscores are members of this class.

HRESULT hr;
OLECHAR szBuf[10];            // Place to put moniker
MONIKER *pmk;

hr = StringCchPrintf(szBuff, 10, "%x", _hwnd);
if (FAILED(hr))
{
    //
    // TODO: write error handler
    //
}
CreateFileMoniker(szBuf, &pmk);
OleStdRegisterAsRunning(this, pmk, &_dwROTcookie);
....................
 
// Accessibility Client: 
//    Find hwnd for window pointed to by mouse cursor.

GetCursorPos(&pt);
hwnd = WindowFromPoint(pt);

// Look in ROT (running object table) for an object attached to hwnd

hr = StringCchPrintf(szBuff, 10, "%x", hwnd);
if (FAILED(hr))
{
    //
    // TODO: write error handler
    //
}
CreateFileMoniker(szBuf, &pmk);
CreateBindContext(0, &pbc);
pmk->BindToObject(pbc, NULL, IID_ITextDocument, &pDoc);
pbc->Release();

if( pDoc )
{
    pDoc->RangeFromPoint(pt.x, pt.y, &pRange);
    // ...now do whatever with the range pRange
}

来自窗口消息的接口

EM_GETOLEINTERFACE 消息提供了另一种方法,用于获取位于给定屏幕位置的对象的 IUnknown 接口。 如来自运行对象表的接口中所述,你将获得屏幕位置的 HWND,然后将此消息发送到该 HWNDEM_GETOLEINTERFACE 消息特定于 Rich Edit,并返回指向 lParam 寻址的变量中的 IRichEditOle 接口的指针。

提示 如果返回指针(请确保在发送消息之前将 lParam 指向该对象设置为 null),则可以调用其 IUnknown::QueryInterface 方法以获取 ITextDocument 接口。 以下代码示例演示了此方法。

    HWND    hwnd;
    ITextDocument *pDoc;
    ITextRange *pRange;
    POINT    pt;
    IUnknown *pUnk = NULL;
    
    GetCursorPos(&pt);
    hwnd = WindowFromPoint(pt);
    SendMessage(hwnd, EM_GETOLEINTERFACE, 0, (LPARAM)&pUnk);
    if(pUnk && 
        pUnk->QueryInterface(IID_ITextDocument, &pDoc) == NOERROR)
    {
        pDoc->RangeFromPoint(pt.x, pt.y, &pRange);
        //  ... continue with rest of program
    }

面向辅助功能的方法

一些 TOM 方法对于在屏幕上导航特别有用,而其他 TOM 方法则可以增强到达感兴趣的地方时的功能。 下表介绍了最有用的方法。

方法 如何提升辅助功能
GetSelection 此方法获取可用于各种面向视图目的的活动选择,例如突出显示文本和滚动。
RangeFromPoint 在活动选择上使用时,此方法可以保证获取与特定视图关联的范围。
展开 放大文本范围,使其包含的任何部分单位完全包含。 例如,Expand(tomWindow) 展开范围以包含区域故事的可见部分。
GetDuplicate 在活动选择上使用时,此方法可以保证获取与特定视图关联的范围。 请参阅 RangeFromPoint 的说明。
GetPoint 获取文本范围中开始或结束字符位置的屏幕坐标。
ScrollIntoView 将文本范围滚动到视图中。
SetPoint 在指定点或向上选择文本。

 

字符匹配集

ITextRange 中各种 Move* 方法的 variant 参数(如 MoveWhileMoveUntil)可以采用显式字符串或字符匹配集 32 位索引。 索引由 Unicode 范围或 GetStringTypeEx 字符集定义。 从 n 开始,长度为 l (< 32768) 的 Unicode 范围由索引 n + (l << 16) + 0x80000000 给出。 例如,基本希腊字母由 CR_Greek = 0x805f0370 定义,可打印 ASCII 字符由 CR_ASCIIPrint = 0x805e0020 定义。 此外,MoveWhileMoveUntil 方法允许快速绕过任何 GetStringTypeEx 字符集中的字符范围,或者不在这些字符集中的字符跨度。

GetStringTypeEx 集由 Ctype1Ctype2Ctype3 的值指定,定义如下。

Cset 含义
Ctype1 CT_CTYPE1 类型的组合。
Ctype2 + tomCType2 任何 CT_CTYPE2 类型。
Ctype3 + tomCType3 CT_CTYPE3 类型的组合。

 

具体而言,Ctype1 可以是以下任意组合。

Ctype1 名称 含义
C1_UPPER 0x0001 大写。
C1_LOWER 0x0002 小写。
C1_DIGIT 0x0004 十进制数字。
C1_SPACE 0x0008 空格字符。
C1_PUNCT 0x0010 标点。
C1_CNTRL 0x0020 控制字符。
C1_BLANK 0x0040 空白字符。
C1_XDIGIT 0x0080 十六进制数字。
C1_ALPHA 0x0100 任何语言字符(字母、音节或表意文字)。
C1_DEFINED 0x0200 已定义的字符,但不是其他 C1_* 类型之一。

 

Ctype2 类型支持 Unicode 文本的正确布局。 指定方向属性,以便 Unicode 标准化的双向布局算法产生准确的结果。 这些类型互相排斥。 有关使用这些属性的详细信息,请参阅 Unicode 标准:全球字符编码,第 1 卷和第 2 卷,Addison-Wesley 出版公司:1991 年、1992 年。

CType2 名称 含义
强一致性:
C2_LEFTTORIGHT 0x1 从左向右。
C2_RIGHTTOLEFT 0x2 从右到左。
弱:
C2_EUROPENUMBER 0x3 欧洲编号,欧洲数字。
C2_EUROPESEPARATOR 0x4 欧洲数字分隔符。
C2_EUROPETERMINATOR 0x5 欧洲数字终止符。
C2_ARABICNUMBER 0x6 阿拉伯数字。
C2_COMMONSEPARATOR 0x7 通用数字分隔符。
中立:
C2_BLOCKSEPARATOR 0x8 块分隔符。
C2_SEGMENTSEPARATOR 0x9 段分隔符。
C2_WHITESPACE 0xA 空格。
C2_OTHERNEUTRAL 0xB 其他中立者。
不适用:
C2_NOTAPPLICABLE 0x0 没有隐含的指示。

 

Ctype3 类型旨在用作常规文本处理或标准 C 库函数所需的 POSIX 类型的扩展的占位符。

CType3 名称 含义
C3_NONSPACING 0x1 非空格标记。
C3_DIACRITIC 0x2 音调符号非空格标记。
C3_VOWELMARK 0x4 元音非空格标记。
C3_SYMBOL 0x8 符号。
C3_KATAKANA 0x10 片假名字符。
C3_HIRAGANA 0x20 平假名字符。
C3_HALFWIDTH 0x40 半角字符。
C3_FULLWIDTH 0x80 全角字符。
C3_IDEOGRAPH 0x100 表意字符。
C3_KASHIDA 0x200 阿拉伯文 Kashida 字符。
C3_ALPHA 0x8000 所有语言文字(字母、音节和表意文字)。
C3_NOTAPPLICABLE 0x0 不适用。

 

编辑开发工具包 (EDK) 可以包括 Unicode 标准中介绍的以下范围的 pVar 索引定义。

字符集 Unicode 范围 字符集 Unicode 范围
ASCII 0x0—0x7f ANSI 0x0—0xff
ASCIIPrint 0x20—0x7e Latin1 0x20—0xff
Latin1Supp 0xa0—0xff LatinXA 0x100—0x17f
LatinXB 0x180—0x24f IPAX 0x250—0x2af
SpaceMod 0x2b0—0x2ff 合并 0x300—0x36f
希腊语 0x370—0x3ff BasicGreek 0x370—0x3cf
GreekSymbols 0x3d0—0x3ff 西里尔语 0x400—0x4ff
亚美尼亚语 0x530—0x58f 希伯来语 0x590—0x5ff
BasicHebrew 0x5d0—0x5ea HebrewXA 0x590—0x5cf
HebrewXB 0x5eb—0x5ff 阿拉伯语 0x600—0x6ff
BasicArabic 0x600—0x652 ArabicX 0x653—0x6ff
Devangari 0x900—0x97f 孟加拉语(前孟加拉语) 0x980—0x9ff
果鲁穆奇语 0xa00—0xa7f 古吉拉特语 0xa80—0xaff
奥里亚语(前奥里亚) 0xb00—0xb7f 泰米尔语 0xb80—0xbff
Teluga 0xc00—0xc7f 卡纳达语 0xc80—0xcff
马拉雅拉姆语 0xd00—0xd7f 泰语 0xe00—0xe7f
老挝语 0xe80—0xeff GeorgianX 0x10a0—0xa0cf
BascGeorgian 0x10d0—0x10ff Jamo 0x1100—0x11ff
LatinXAdd 0x1e00—0x1eff GreekX 0x1f00—0x1fff
GenPunct 0x2000—0x206f 上标 0x2070—0x207f
下标 0x2080—0x208f SuperSubscript 0x2070—0x209f
货币 0x20a0—0x20cf CombMarkSym 0x20d0—0x20ff
LetterLike 0x2100—0x214f NumberForms 0x2150—0x218f
箭头 0x2190—0x21ff MathOps 0x2200—0x22ff
MiscTech 0x2300—0x23ff CtrlPictures 0x2400—0x243f
OptCharRecog 0x2440—0x245f EnclAlphaNum 0x2460—x24ff
BoxDrawing 0x2500—0x257f BlockElement 0x2580—0x259f
GeometShapes 0x25a0—0x25ff MiscSymbols 0x2600—0x26ff
Dingbats 0x2700—0x27bf CJKSymPunct 0x3000—0x303f
平假名 0x3040—0x309f 片假名 0x30a0—0x30ff
注音符号 0x3100—0x312f HangulJamo 0x3130—0x318f
CJLMisc 0x3190—0x319f EnclCJK 0x3200—0x32ff
CJKCompatibl 0x3300—0x33ff Han 0x3400—0xabff
朝鲜文字 0xac00—0xd7ff UTF16Lead 0xd800—0xdbff
UTF16Trail 0xdc00—0xdfff PrivateUse 0xe000—0xf800
CJKCompIdeog 0xf900—0xfaff AlphaPres 0xfb00—0xfb4f
ArabicPresA 0xfb50—0xfdff CombHalfMark 0xfe20—0xfe2f
CJKCompForm 0xfe30—0xfe4f SmallFormVar 0xfe50—0xfe6f
ArabicPresB 0xfe70—0xfefe HalfFullForm 0xff00—0xffef
特殊字符 0xfff0—0xfffd