Unicode双向算法(二)
注:本翻译使用符号「」来突出某些可能会产生歧义的名词。
目前状态:勘误中。
Unicode®标准附录#9
UNICODE双向算法#####
版本 | Unicode 8.0.0 |
编者 | Mark Davis, Aharon Lanin, and Andrew Glass |
日期 | 2015-05-29 |
当前版本 | http://www.unicode.org/reports/tr9/tr9-33.html |
当前版本 | http://www.unicode.org/reports/tr9/tr9-31.html |
以前版本 | http://www.unicode.org/reports/tr9/tr9-31.html |
最新版本 | http://www.unicode.org/reports/tr9/ |
提出的更新 | http://www.unicode.org/reports/tr9/proposed.html |
修正 | 33 |
目录#####
├ 1 引言
├ 2 定向格式化字符
│├ 2.1 显式定向嵌入
│├ 2.2 显式定向重写
│├ 2.3 终止显式定向嵌入和重写
│├ 2.4 显式定向隔离
│├ 2.5 终止显式定向隔离
│├ 2.6 隐式定向标记
│└ 2.7 标记和格式化字符
├ 3 基本显示算法
│├ 3.1 定义
││├ 3.1.1 基础:BD1,BD2,BD3,BD4,BD5,BD6,BD7
││├ 3.1.2 匹配显式定向格式化字符:BD8,BD9,BD10,
│││ BD11,BD12,BD13
││├ 3.1.3 成对括号:BD14,BD15,BD16
││└ 3.1.4 其他缩略语缩略语翻译
│├ 3.2 双向字符类型
│├ 3.3 解析嵌入层次level是层次还是等级?
││├ 3.3.1 分段层次: P1,P2,P3
││├ 3.3.2 显式层次和方向:X1,X2,X3,X4,X5,X5A,
│││ X5b,X5c,X6,X6A,X7,X8
││├ 3.3.3 隐式处理的准备: X9,X10
││├ 3.3.4 分析弱类型: W1,W2,W3,W4,W5,W6,W7
││├ 3.3.5 分析中性类型和隔离格式化类型: N0,N1,N2隔离格式化?
││└ 3.3.6 分析隐式层次:I1,I2
│├ 3.4 分析重排序层次: L1,L2,L3,L4
│└ 3.5 成型定型?翻译
├ 4 双向一致性
│├ 4.1 中性边界
│├ 4.2 显式格式化字符
│├ 4.3 高层协议:HL1,HL2,HL3,HL4,HL5,HL6高等级协议
│└ 4.4 双向一致性测试
├ 5 实现说明
│├ 5.1 参考代码
│└ 5.2 保留的BN和显式格式化字符保留还是确定
├ 6 用法
│├ 6.1 结合
│├ 6.2 竖排文本
│├ 6.3 格式化
│├ 6.4 分离标点符号
│└ 6.5 转换为纯文本
├ 7 镜像
├ 迁移问题
│└部分重组
├ 致谢
├ 参考
└ 修改
3.3.3 隐式处理的准备
通过之前的规则,显式嵌入水平已被分配给字符,在字符的隐式双向类型的基础上,显式嵌入将作进一步调整。对于一个给定字符的调整,将依赖于它周围的字符。然而,这种依赖性会被如下规则界定: 在逻辑上将分段分成子单元,独立地对每个单元进行后续的隐式处理限制。
X9. 移除所有的RLE,LRE,RLO,LRO,PDF和BN字符。
注意:这个实现并没有真正删除那些字符,只是在后面的算法中,字符表现得不存在一样。只要其他字符正确排序,一致性不对字符作特殊的规定。
参阅章节5,执行注释,有关执行该算法而不需要删除格式化字符的资料。
零宽度连接器(zero width joiner)和非连接器(non-joiner)影响相邻字符(即使这些字符在双向算法中重排序后可能变得不相邻,字符在最初存储器中的顺序相邻即可)的成型,具体可参阅章节6.1,连接符。
注意:FSI,LRI,RLI和PDI 字符没有被移除。正如下列规则指出的,在某种程度上,它们被用于确定分段的隔离运行序列,其中,它们会被看作是中性字符。当然,它们是像LRM和RLM这类的零宽度字符,在最后输出中不可见。
X10. 执行以下步骤:
按BD13指定的计算隔离运行序列的集合,基于字符的双向类型和上面规则(X1-X9)赋值的嵌入等级。
对每个隔离运行序列,确定分段的起点(start-of-sequence,简称sos)和分段的终点(end-of-sequence,简称eos)类型,L或R其中之一。这取决于在序列边界两侧的两个等级更高的那个:
对于sos,序列中第一个字符的等级与分段内sos前面的字符(不包括被X9移除的字符)等级比较,如果找不到sos前面的字符,则是与分段嵌入等级比较。
对于eos,序列中最后字符的等级与分段内soe后面的字符(不包括被X9移除的字符)等级比较,如果找不到soe后面的字符或序列的最后字符是隔离启动器(缺少一个匹配的PDI),则是与分段嵌入等级比较。
如果最高的级别是奇数,sos或eos是R,否则是L。
注意这个计算必须用到上面规则所赋值的嵌入等级,下面的步骤执行产生的变化。
应用规则W1-W7,N0-N2和I1-I2,对每个隔离运行序列,按照序列出现的顺序,对序列中的所有字符,按照字符在序列中的顺序,且对序列的任意部分应用其他规则之前,应用一条规则。对每个隔离运行序列顺序的处理都很类似。当对一个隔离运行序列应用一条规则时,隔离运行序列中每个运行等级的最后一个字符均被看作字符的后面会紧接着序列中下一运行等级的第一个字符(无论是否存在这个字符)。
这里有几个例子,其中每个texti被假定为是一个基础等级为0和内部无字符序列的分段,texti包含了显式定向格式化字符或分段分隔符。例子中的点号是为了视觉清晰把元素分开,它们不是文本的一部分。
例1:
text1·RLE·text2·LRE·text3·PDF·text4·PDF·RLE·text5·PDF·text6
隔离运行序列 | 嵌入等级 | sos | eos |
---|---|---|---|
text1 | 0 | L | R |
text2 | 1 | R | L |
text3 | 2 | L | L |
text4·text5 | 1 | L | R |
text6 | 0 | R | L |
例2: text1·RLI·text2·LRI·text3·PDI·text4·PDI·RLI·text5·PDI·text6
隔离运行序列 | 嵌入等级 | sos | eos |
---|---|---|---|
text1·RLI·PDI·RLI·PDI·text6 | 0 | L | L |
text2·LRI·PDI·text4 | 1 | R | R |
text3 | 2 | L | L |
text5 | 1 | R | R |
例3: text1·RLE·text2·LRI·text3·RLE·text4·PDI·text5·PDF·text6
隔离运行序列 | 嵌入等级 | sos | eos |
---|---|---|---|
text1 | 0 | L | R |
text2·LRI·PDI·text5 | 1 | R | R |
text3 | 2 | L | R |
text4 | 3 | R | R |
text6 | 0 | R | L |
3.3.4 解析弱类型
首先,每个无间距标记(nonspacing mark,简称NSM)的解析是基于它后面的字符。
W1.在每个隔离运行序列中检查每个NSM,如果前一个字符是隔离启动器或PDI,将NSM的类型更改为其他中性的,否则类型更改为与前一个字符相同。如果NSM是隔离运行序列的第一个字符,NSM的类型将是sos。(注意,在一个隔离运行序列中,一个隔离启动器后面是一个NSM或任何非PDI类型,那么这个隔离启动器就是一个溢出隔离启动器。)
这个例子中,假设SOS是R:<pre><b>AL NSM NSM → AL AL AL
sos NSM → sos R
LRI NSM → LRI ON
PDI NSM → PDI ON</b></pre>下一步是解析数字。这阶段会将双向类型欧洲数字分隔符、欧洲数字终止符和普通数字分隔符变更为欧洲数字文本,或者其他中性文本。对于已经被扫描的文本,它的类型可能已经被定向重写改变。如果是这样,那么就不会解析为数字。
W2.从每个欧洲数字的实例反向搜索直到找到第一个强类型(R,L或sos)。如果找到AL,将欧洲数字的类型变更为阿拉伯数字。<pre><b>AL EN → AL AN
AL NI EN → AL NI AN
sos NI EN → sos NI EN
L NI EN → L NI EN
R NI EN → R NI EN</b></pre>W3.强所有AL类型变更为R。
W4.在两个欧洲数字之间的唯一的欧洲数字分隔符变更为欧洲数字。两个同一类型的数字之间的唯一普通分隔符变更为该类型。<pre>EN ES EN → EN EN EN
EN CS EN → EN EN EN
AN CS AN → AN AN AN</pre>W5.与欧洲数字相邻的欧洲数字分隔符变更为欧洲数字。<pre>
ET ET EN → EN EN EN
EN ET ET → EN EN EN
AN ET EN → AN EN EN
</pre>W6.否则,分隔符和终止符变更为其他中性类型。<pre>AN ET → AN ON
L ES EN → L ON EN
EN CS AN → EN ON AN
ET AN → ON AN</pre>W7.从每个欧洲数字开始,反向搜索直到找到第一个强类型(R,L或sos)。如果找到L,则将欧洲数字的类型变更为L。<pre>L NI EN → L NI L
R NI EN → R NI EN</pre>3.3.5 解析中性类型和隔离格式化类型
在下一阶段,在一个隔离运行序列中,进行一次中性和隔离格式化(即 NI)字符的解析。这将导致所有NI变更为R或L。一般来说,NI类型由其周围的文本决定。万一发生冲突,NI类型由定向嵌入决定。 At isolating run sequence boundaries where the type of the character on the other side of the boundary is required, the type assigned to sos or eos is used.在隔离运行序列中的一对括号被当作单元来处理,这样开括号和闭括号均被解析为同一方向。注意这条规则是基于每对括号的当前定向字符类型而不是原始类型,同时这可能在规则X6下发生了改变。The current bidirectional character type may also have changed under a previous iteration of the for loop in N0 in the case of nested bracket pairs.
N0.
使用下面给出的逻辑处理在开括号的文本位置的逻辑顺序的一个隔离运行序列中的对括号。在范围内,双向类型EN和AN看作R。
根据BD16,识别在当前隔离运行序列中的对括号
对于每个在文本位置的列表中的每个对括号元素:
a.检查在对括号内所包含的字符的双向类型。
b.如果发现任何匹配了嵌入方向的强类型(L或R),将对括号的类型设置为嵌入方向。<pre>o [ e ] o → o e e e o
o [ o e ] → o e o e e
o [ NI e ] → o e NI e e</pre>c.否则,如果这里有一个强类型,它必须是与嵌入方向相反。因此,带着上述的强类型,测试一个确定的上下文,通过在开括号的前面开始反向检查,直到发现第一个强类型(L,R或sos)。
1.如果上述的强类型也是与嵌入方向相反,上下文被确定,因此,将对括号设置成该方向。<pre>o [ o ] e → o o o o e
o [ o NI ] o → o o o NI o o</pre>2.否则将对括号设置成嵌入方向。<pre>e [ o ] o → e e o e o
e [ o ] e → e e o e e</pre>d.否则,在对括号里没有强类型。因此,对括号不设置类型。<pre>e ( NI ) o → e ( NI ) o</pre>注意,如果封闭的文本里没有强字符,括号会逐一使用规则N1和N2来解析到相同的等级。
无论有多少个字符,只要字符含有原始双向字符类型NSM,该NSM优先应用规则W1,且NSM后面紧跟在规则N0下变更为L或R对括号,那么字符应该变更为匹配它们之前括号的类型。
例1.从逻辑顺序的开括号位置开始,依次解析对括号。
(RTL分段方向)
Storage | AB | ( | CD | [ | & | ef | ] | ! | ) | gh |
Bidi_Class | R | ON | R | ON | ON | L | ON | ON | ON | L |
N0 applied (first pair) | N0b: ON→R | N0b: ON→R | ||||||||
N0 applied (second pair) | N0c2: ON→R | N0c2: ON→R | ||||||||
Display | gh(![ef&]DC)BA |
例2.括号对内包含混合强类型则选取分段方向。
(RTL分段方向)
Storage | smith | ( | fabrikam | ARABIC | ) | HEBREW | |||
Bidi_Class | L | WS | ON | L | WS | R | ON | WS | R |
N0 applied | N0b: ON→R | N0b: ON→R | |||||||
Display | WERBEH (CIBARA fabrikam) smith |
注意,在上面例子中,如果smith与HEBREW或fabrikam与ARABIC的顺序调换,对括号的解析仍然不变。
例3.对括号内包含与嵌入方向相反的强类型with additional strong-type context,选取与嵌入方向相反的方向。
(RTL分段方向)
||||||||
|:--|:--|:--|:--|:--|:--|:--|:--|:--|:--|:--|
|Storage|ARABIC||book|(|s|)|
|Bidi_Class|R|WS|L|ON|L|ON|
|N0 applied||||N0c1: ON→L||N0c1: ON→L|
|Display|book(s) CIBARA||||||
N1.如果NI两边的文本具有相同的方向,那么NI选取周围的强文本方向。在对NI的影响方面,欧洲数字和阿拉伯数字表现得像R类型一样。sos和eos类型用于隔离运行序列边界上。<pre> L NI L → L L L
R NI R → R R R
R NI AN → R R AN
R NI EN → R R EN
AN NI R → AN R R
AN NI AN → AN R AN
AN NI EN → AN R EN
EN NI R → EN R R
EN NI AN → EN R AN
EN NI EN → EN R EN</pre>
N2.剩下的NI选取嵌入方向。<pre>NI → e</pre>对于给定的NI字符的嵌入方向,是来源于它的嵌入等级:如果字符被设置偶数等级,则为L,如果是奇数等级,则为R。(参阅BD3.)
在以下例子中,假定eos是L类型,sos是R类型。应用N1和N2后如下:<pre>L NI eos → L L eos
R NI eos → R e eos
sos NI L → sos e L
sos NI R → sos R R</pre>
一列数字被中性字符隔开和嵌入在定向运行中,它会出现在运行的顺序中。<pre>Storage: he said "THE VALUES ARE 123, 456, 789, OK".
Display: he said "KO ,789 ,456 ,123 ERA SEULAV EHT".</pre>
在这个例子中,逗号和空格均在数字之间,它们忽略数字并获取周围文本的方向(大写字符即right-to-left)。逗号不用考虑数字部分,因为它们的两侧不是被数字包围(参阅章节3.3.4,解析弱类型)。然而,如果前面有一个left-to-right序列,欧洲数字会采取如下方向:<pre>Storage: IT IS A bmw 500, OK.
Display: .KO ,bmw 500 A SI TI</pre>
3.3.6 解析隐式层次
在最后阶段,基于已被解析的字符类型,文本的嵌入等级可能增加。Right-to-left文本总是以奇数等级结束,left-to-right和中性的文本总是以偶数等级结束。此外,数字的文本总是以比分段等级高的等级结束。(注意,在这过程中,文本以max_depth+1等级结束是可能的。)这将导致以下规则:
I1.对于所有在偶数(left-to-right)嵌入等级的字符,那些类型为R的提升一个等级,那些类型为AN或EN的提升两个等级。
I2.对于所有在奇数(right-to-left)嵌入等级的字符,那些类型为L,EN或AN的提升一个级别。
表格5归纳了隐式算法的结果。
|类型|嵌入水平|嵌入水平|
|:--|:--|:--|:--|:--|
||偶数|奇数|
|L|EL|EL+1|
|R|EL+1|EL|
|AN|EL+2|EL+1|
|EN|EL+2|EL+1|
3.4 重排序已被解析的等级
以下规则描述了寻找正确的显示顺序的逻辑过程。相对于解析阶段,these rules act on a per-line basis and are applied after any line wrapping is applied to the paragraph.
逻辑上有以下几个步骤:
文本等级由前面的规则确定。
字符根据它们的上下文(对镜像考虑嵌入等级)成型。
这些字形(按逻辑顺序)的累计宽度被用与确定换行。
对于每一行,规则L1-L4被用于重排序该行上的字符。
对应行中字符的字形的显示。
L1.在每一行中,将以下字符的嵌入等级重置成分段嵌入等级:
1.段分隔符,
2.分段分隔符,
3.任何在段分隔符或分段分隔符前面的空白字符和/或隔离格式化字符(FSI,LRI,RLI和PDI)序列,和
4.任何在行的结尾的空白字符和/或隔离格式化字符(FSI,LRI,RLI和PDI)序列。
这里所使用的字符类型是原始类型,不是被以前阶段修改过的类型。
因为分段分隔符会中断行,在每行中最多有一个在该行的结尾。
结合以下规则,这意味着尾随的空白字符将出现该行的视觉结束位置(分段方向)。分段中的制表符总有一个连贯的方向。
L2.在文本中发现的最高等级到每行中最低的奇数等级,包括中间等级,这些等级实际上并不存在于文本中,颠倒字符在该等级或更高的所有相邻序列
这条规则反向逐步增大的子串。
以下例子说明重排序,展示应用规则L2的连续步骤。原始文本展示在表格里行“存储器(Storage)”中。无形的,零宽度格式化字符LRI,RLI和PDI分别使用>,<,=号来代替。章节3.3,解析嵌入等级和规则L1的应用所生成的已被解析等级展示在行“已被解析等级(Resolved Levels)”中。(由于这些例子只是利用隔离格式化字符,规则X9不会移除任何字符。注意,如果使用嵌入来替代,例3将不起作用,因为两个right-to-left短语会连同中性标点合并成一个right-to-left运行。)。此后,每一个连续的行按规则L2展示一遍逆转,例如“反向等级1-2”.在每次反向中,下划线表示该段文本已经被反向。
例1,例2,例3的分段嵌入等级为0(left-to-right方向),例4为1(right-to-left方向)。
例1.(嵌入等级=0)<pre><b>Storage: car means CAR.
Resolved levels: 00000000001110
Reverse level 1: car means <u>RAC</u>.
Display: car means RAC</b></pre>
例2.(嵌入等级=0)<pre><b>
Storage: <car MEANS CAR.=
Resolved levels: 0222111111111110
Reverse level 2: <<u>rac</u> MEANS CAR.=
Reverse levels 1-2: <<u>.RAC SNAEM car</u>=
Display: .RAC SNAEM car
</b></pre>
例3.(嵌入等级=0)<pre><b>
Storage: he said “<car MEANS CAR=.” “<IT DOES=,” she agreed.
Resolved levels: 000000000022211111111110000001111111000000000000000
Reverse level 2: he said “<<u>rac</u> MEANS CAR=.” “<IT DOES=,” she agreed.
Reverse levels 1-2: he said “<<u>RAC SNAEM car</u>=.” “<<u>SEOD TI</u>=,” she agreed.
Display: he said “RAC SNAEM car.” “SEOD TI,” she agreed.
</b></pre>
例4.(嵌入等级=1)<pre><b>
Storage: DID YOU SAY ’>he said “<car MEANS CAR=”=‘?
Resolved levels: 111111111111112222222222444333333333322111
Reverse level 4: DID YOU SAY ’>he said “<rac MEANS CAR=”=‘?
Reverse levels 3-4: DID YOU SAY ’>he said “<RAC SNAEM car=”=‘?
Reverse levels 2-4: DID YOU SAY ’>”=rac MEANS CAR<“ dias eh=‘?
Reverse levels 1-4: ?‘=he said “<RAC SNAEM car=”>’ YAS UOY DID
Display: ?‘he said “RAC SNAEM car”’ YAS UOY DID
</b></pre>
L3.在这点上,应用在right-toleft基本字符(base character)的组合标记(Combining marks)优于它们的基本字符。如果渲染引擎希望它们在最后展示流程中跟随基本字符,则标记的顺序和基本字符必须颠倒。Many font designers provide default metrics for combining marks that support rendering by simple overhang. Because of the reordering for right-to-left characters, it is common practice to make the glyphs for most combining characters overhang to the left (thus assuming the characters will be applied to left-to-right base characters) and make the glyphs for combining characters in right-to-left scripts overhang to the right (thus assuming that the characters will be applied to right-to-left base characters). With such fonts, the display ordering of the marks and base glyphs may need to be adjusted when combining marks are applied to “unmatching” base characters. See Section 5.13, Rendering Nonspacing Marks of [Unicode], for more information.
L4.当且仅当已被解析的字符方向是R、字符的Bidi_Mirrored属性值是Yes时,该字符由一个镜像字形描述。
BIdi_Mirrored属性在[Unicode]的章节4.7,Bidi Mirrored中定义;属性值在[UCD]中详细说明。
这条规则可以在某些情况下被覆盖;参阅HL6.
例如,U+0028左圆括号(LEFT PARENTHESIS),它在Unicode Standard中翻译为开放的圆括号,当它解析后的等级为偶数,显示为“(”,当它解析后的等级为奇数,显示为“)”。注意,为了向后兼容字符U+FD3E“﴾”装饰的左括号(ORNATE LEFT PARENTHESIS)和U+FD3F“﴿”装饰的右括号(ORNATE RIGHT PARENTHESIS,它们没有镜像。
3.5 成型
手写连接的语言,例如阿拉伯语或叙利亚语,需要选择位置的字符形状,会依赖于邻近的字符(参阅[Unicode]的章节8.2,Arabic)。在双向算法的规则I2后应用成型,成型受到相同运行等级内的字符限制。(注意,限制的整形对一个运行等级和一个隔离运行序列没有实际的差异,是因为隔离启动器和PDI字符被定义去加入类型U,即非链接(non-joining)。因此,定向隔离前后的字符不会连接隔离,即使隔离是空的或溢出深度限制。)参考以下例子,阿拉伯字符串,在内存中作为字符1,2,3和4表示,并且其中前两个字符是LTR方形的重写。同时显示分段方向,后两个字符是RTL方向的嵌入。
|1|2|3|4|
|:--:|:--:|:--:|:--:|:--:|
|<b>ج</b>|<b>ع</b>|<b>ل</b>|<b>م</b>|
|062C|0639|0644|0645|
|JEEM|AIN|LAM|MEEM|
|L|L|R|R|
在HTML中的文本或使用,
在纯文本中使用显式定向格式化字符,或者在HTML中使用标记,均可达到这个效果,例子如下。(粗体文本是right-to-left的分段方向。)
LRM/RLM LRO JEEM AIN PDF RLO LAM MEEM PDF
<p dir="ltr"/"rtl">LRO JEEM AIN PDF RLO LAM MEEM PDF</p>
<p dir="ltr"/"rtl"><bdo dir="ltr">JEEM AIN</bdo>
<bdo dir="rtl">LAM MEEM</bdo></p>
根据分段方向,所得的形状如下:
Left-Right Paragraph
|||||
|:--|:--|:--|:--|:--|:--|:--|:--|:--|:--|:--|
|1|2|4|3|
|<b>ﺞ</b>|<b>ﻋ</b>|<b>ﻢ</b>|<b>ﻟ</b>|
|JEEM-F|AIN-I|MEEM-F|LAM-I|
Right-Left Paragraph
|||||
|:--|:--|:--|:--|:--|:--|:--|:--|:--|:--|:--|
|4|3|1|2|
|<b>ﻢ</b>|<b>ﻟ</b>|<b>ﺞ</b>|<b>ﻋ</b>|
|MEEM-F|LAM-I|JEEM-F|AIN-I|
3.5.1 成型和断行
将分段分成符合特定边界的一行或多行的过程超出了双向算法的范围。当字符成型参与时,宽度计算必须基于字形的形状。
注意,软连字符(soft-hyphen,简称SHY)插入到手写连接语言时与插入到其他语言一样。也就是说,它指出了一点,在该点上单词的中间可以中断该行。如果渲染系统在该点上中断,对于给定的语言,显示(包括成型)应该是恰当的。关于这些和其他断行问题的更多信息,见附件# Unicode标准14,“断行特性”[UAX14]。
双向一致性
双向算法指定部分right-to-left字符的内在语义,因此需要与Unicode Standard中显示的任何字符保持一致性。
与本规范保持一致性的过程应满足下列条款:
UAX9-C1.在没有允许的更高级别的协议下,渲染文本的过程应该在顺序描述中显示出所有列出的可见的字符(不包括格式化字符),参阅章节3,Basic Display Algorithm。此外,这包括定义BD1-BD16和步骤P1-P3,X1-X10,W1-W7,N0-N2,I1-I2,和L1-L4。
如同所有其他的Unicode算法,只要他们产生相同的结果,这一逻辑描述在特定实现下可以有更高效的机制。参[Unicode]第三章Conformance,注意事项如下。
UAX9-C2.唯一允许更高级别的协议在4.3节,Higher-Level Protocols中列出。他们是HL1、HL2、3,4,5,和HL6。
注意:不鼓励使用更高级别的协议是因为它引入了交换问题,并可能导致安全问题。有关更多信息,参见Unicode的技术报告# 36、“Unicode Security Considerations”[UTR36]。
4.1 中性边界
标记一个格式化字符或控制字符为BN的目的它对算法的其余部分不产生影响。(ZWJ和ZWNJ是例外;参阅X9)。相对于其它字符,一致性并不要求对格式化字符进行精确地排序,只要它们保留其他字符的顺序时,可以用不同的方式来处理它们。
4.2 显式格式化字符
就Unicode字符来说,系统不必支持所有的显式定向格式化字符(虽然一般只含有一个终止字符而不含启动器是没用的)。
一般情况下,一致性系统将分为四类:
无双向性格式化。这意味着系统并不能直观地解释right-to-left语言的字符。
隐式双向性。这意味着支持双向算法和定向标记ALM,RLM和LRM。
无隔离双向性。这意味着支持双向算法,隐式定向标记,显式非隔离定向格式化字符ALM,RLM,LRM,LRE,RLE,LRO,RLO,PDF。
完整的双向性。这意味着支持双向算法,隐式定向标记,显式定向格式化字符ALM,RLM,LRM,LRE,RLE,LRO,RLO,PDF,FSI,LRI,RLI,PDI。
4.3 高级协议
下列条款是系统对双向文本的排序,应用更高级别的协议唯一允许的方式。部分条款适用于结构化文本段。这指的是文本被解析为结构化时,无论是带有显式标记的例如XML或HTML,或是不带显式标记的内部结构化如文字处理器或电子表格。在这种情况下,一个段的范围是通过结构用某种方式来区分。
HL1。重写P3,并显式地设置分段嵌入等级。当在规则X5c中决定怎样对待FSI时,本规则不会被应用。
更高级别的协议可能设置所有分段等级。这可以在上下文的基础上进行,例如在一个表格单元格,分段,文档或系统等级。(如果P3被重写,P2可能会被跳过)。注意,这不允许更高级别的协议来重写BD2中指定的限制。
一个更高级别的协议可能应用相当于P2和P3的规则,但默认为1级(RTL)而不是0级(LTR),以适应整体的RTL环境。
更高级别的协议可能会使用完全不同的算法,基于分段文本和内容上,启发式地自动检测分段嵌入等级。例如,它可以将它基于是否在文本中RTL字符比LTR多。作为另一个例子,当分段不含有强字符,其方向可以由分段之前或之后的等级确定。
HL2.重写W2,显式地设置EN或AN。
更高级别的协议可能将字符的EN类型重置为AN类型,反之亦然,并忽略W2。例如,样式表或标记信息可以用在文本范围内,来将设置为EN的文本重写为AN,反之亦然。
HL3。模拟显式定向格式化字符。
更高级别的协议可以对在结构化段落施加定向嵌入,隔离和重写的作用。该行为必须通过参考,如果同等的在算法中定义的显式定向格式化字符被插入到文本会发生什么来定义。例如,样式表或标记信息可以修改文本范围内的嵌入等级。
HL4。对段落应用双向算法。
双向算法可以被单独地应用在一个或多个结构化段落中。例如,当在编辑器中显示一个包含文本数据和可见标记的文档时,更高级别的协议可以从文本数据中,分别处理标记中的语法元素。
HL5。提供人为的上下文。
文本可以通过双向算法进行处理,就好像文本的前面或后面是一个给定类型的字符。这允许从文本中的长段落提取出一片文本来表现出该片文本在更大的上下文中。
HL6。额外的镜像。
没有Bidi_Mirrored属性的某些字符也可以通过在专门上下文的镜像图像字符描述。这些上下文包括但不限于,历史上的语言和相关的标点,私人使用的字符和数学表达式的字符。
这些字符至少符合下列一个条件:
1.字符带有已被解析的方向是R
2.字符带有已被解析的方向是L,它的双向类型是R或AL。
条款HL1和HL3是专业的应用,条款HL4和HL5更常规。它们在这里明确规定是因为它们直接对应着常用操作。
对HL4应用的一个例子,假设一个XML文档包含以下片段。(注意,这是一个简化的例子:元素名,属性名和属性值都可能参与进来。)<pre>ARABICenglishARABIC<e1 type='ab'>ARABICenglish<e2 type='cd'>english</pre>这可以被分析为五个不同的片段:<ol><li>ARABICenglishARABIC</li><li><e1 type='ab'></li><li>ARABICenglish</li><li><e2 type='cd'></li><li>english</li></ol>为了使作为源文本的XML文件可读,在编辑器中显示可以命令这些元素都在统一方向(例如,所有都是left-to-right)和分别对每部分应用双向算法。它也可以选择去命定元素名,属性名和属性值在同一方向上统一(例如,所有都是left-to-right)。对最终的显示,标记可以被忽略,允许所有的文本(片段a,c和e)一起进行重排序。
4.4 双向一致性测试
Unicode字符数据库[UCD]包含两个文件来为双向算法[Tests9]的实现提供一致性测试。其中一个文件,BidiTest.txt
,
包含了双向类型详细的测试序列直到一个给定的长度,一般是4。另一个文件,BidiCharacterTest.txt
,包含显式代码点的测试序列,包括例如:括号对。每个测试文件的格式在该文件的头部均被说明。
5 实现注意事项
5.1 参考代码
可获得用Java编写双向算法的参考实现。源代码可以从[Code9]下载。鼓励实施者使用这一资源来测试它们的实现。可在[Demo9]找到显示双向算法的结果以及嵌入等级和调用每个字符的规则的在线演示。
参考代码被设计成遵循该算法的步骤,而没有应用任何优化。一个有效优化的例子是仅当right-to-left字符存在时,首先测试它们并调用双向算法。另一个优化是对匹配括号对。双向括号对(字符的Bidi_Paired_Bracket_Type属性值Open或Close)构成双向类型为ON的字符的一个子集。相反,双向类型非ON的字符的Bidi_Paired_Bracket_Type属性为None。因此, 被 通过限制对双向类型为ON的字符处理,可以优化查找Bidi_Paired_Bracket_Type属性值来识别括号对的过程。
5.2 保留BN和显式格式化字符
一些实现可能希望在算法运行时,能保留显式定向嵌入、重写格式字符和BN。事实上,保留这些格式化字符和BN对用户是很重要的。例如有些用户需要显示隐藏字符的图示,因此为了显示而需要获取格式化字符和BN。
以下描述如何通过算法的步骤来保留这些字符的具体实现。注意,本描述是信息的实施指南;它应该提供与上述显式算法一样的结果,但万一与显式算法存在偏差,显式算法对于一致性来说更规范。
在规则X2-X5,给双向状态栈中最后一个条目的嵌入等级,插入一个初始步骤来设置显式嵌入或重写字符的嵌入等级。这适用于RLE,LRE,RLO和LRO。
在规则X6,移除BN字符外的字符。换句话说,对除了B,RLE,LRE,RLO,LRO,PDF,RLI,LRI,FSI和PDI的所有类型应用规则。
在规则X7,在所有情况下,添加最后一步来将PDF的嵌入等级设置成定向状态栈中最后一个条目的嵌入等级。
在规则X9,不移除所有字符,但将所有RLE,LRE,RLO,LRO和PDF字符转换成BN。
在规则X10,在确定隔离运行序列的sos和eos过程中,当寻找隔离运行序列的首字符前的字符和末字符后的字符时,跳过任何BN。
在规则W1,从每个NSM开始反向搜索直到找到隔离运行序列中首个双向类型为非BN的字符,如果NSM是一个隔离启动器或PDI并将NSM设置成ON,否则不变。如果NSM是第一个非BN字符,NSM的类型转换为sos。
在规则W4,扫描越过邻近是ES或CS的BN类型。
在规则W5,转换所有适当的ET和BN序列,而不只是ET。
在规则W6,同样将所有邻近ET,ES或CS的BN类型转换成ON。
在规则W7,扫描越过BN。
在规则N0-N2,将邻近中性字符的BN字符看作与中性字符一样。
在规则I1和I2,忽略BN。
在规则L1,在序列中,包括嵌入和重写格式化字符和BN连同空格字符和隔离格式化字符,它们的等级在分隔符或换行符之前被重置。如果LRE,RLE,LRO,RLO,PDF或BN的前一个字符等级是1,则将它们的等级解析为1,否则解析为基础等级。
6 用法
6.1 结合
正如X9中描述的,零宽度连接符和非连接符影响邻近(指在原始储存备份(backing-store)中的邻近顺序,即使这些字符经过双向算法重新排列后最终变得不邻近)字符的成型。为了确定特定字符在应用双向算法后的连接行为,这里有两种主要的策略:
在成型时,实现可以回查储存备份,看是否有相邻的ZWNJ或ZWJ字符。
另外,实现可以通过一个与相邻字符关联的带外(out-of-band)字符属性代替ZWJ和ZWNJ,这样信息不会与双向算法和保存在字符重新排列的信息相冲突。只要双向算法被应用,带外信息可以用于适当的成型。
6.2 竖排文本
在垂直方向上,双向算法仍然用于确定文本的等级。然而,这些等级不是用来对文本重排序,因为字符通常是统一地由上至下。相反,等级是用于确定文本的旋转。有时垂直线遵循的垂直基线,基线上每个字符方向确定且正常(非旋转),字符排列从上到下,无论是希伯来语,数字或拉丁语。当在垂直线上,将文本设置成阿拉伯语时,更普遍的是使水平线逆时针旋转90°,那么字符就从上到下排列。拉丁文字和拉丁数字可能顺时针旋转90°,那么字符也从上到下排列。
双向算法也可以用于字符从下往上排列。例如,混合了阿拉伯语和拉丁符号,此时所有图像字符顺时针旋转90°。Unicode标准不会指定文本是否水平的或垂直的,或是旋转的。那是留给更高级别的协议。
6.3 格式化
由于隐式字符类型和对解析中性和数字的启发式定向行为,隐式双向排序一般会在没有进一步工作就可以产生正确的显示。但是,当一个right-to-left分段首字符是left-to-right时,或有不同方向文本的嵌套片段,或是弱字符在定向的边界时,就可能会出现问题。在这些情况下,为得到正确的显示,可能需要嵌入或定向标记。部分数字可能还需要定向重写。
最常见的问题是中性字符在嵌入式语言的边界。这可以通过正确设置嵌入式文本的等级来解决。例如:以下所有的文本在等级0中:
<pre>Memory: he said "I NEED WATER!", and expired.
Display: he said "RETAW DEEN I!", and expired.</pre>
如果感叹号是阿拉伯语括号的一部分,那么用户可以选择文本I NEED WATER!并明确将它标记为嵌入式阿拉伯语,并产生以下结果:<pre>Memory: he said "RLII NEED WATER!PDI", and expired.
Display: he said "!RETAW DEEN I", and expired.</pre>
然而,一个更简单和更好的方法是在感叹号后面放置一个RLM。因为这样感叹号标记不是在定向边界,这会产生正确的结果。当人为地编辑文本或程序化生成需要编辑的文本,或处理根本不支持显式格式化字符的应用程序,这是最好的方法。
<pre>Memory: he said "I NEED WATER!RLM", and expired.
Display: he said "!RETAW DEEN I", and expired.</pre>
首选后者的方法因为它不使用显式格式化字符,在不完全支持编辑器和其他字符处理的情况下,它可以轻松摆脱同步。然而,显式格式化字符是绝对必要的,当一个文本包含与之相反方向的文本,相反方向的文本包含着与原始方向相同的文本。这种情况并不像人们想象的那样罕见,因为拉丁语的品牌名称、技术术语和缩写常书写在非拉丁语文本的原始拉丁语字符中,包括right-to-left文本,如下所示:
<pre>Memory: it is called "RLIAN INTRODUCTION TO javaPDI" - $19.95 in hardcover.
Display: it is called "java OT NOITCUDORTNI NA" - $19.95 in hardcover.</pre>
因此,当文本通过插入数据到模板中而程序化生成,不打算以后手动编辑,而且特定的插入恰好是与模板文本方向相反,这种情况使用显式格式化字符(或等同它们的标记)包裹插入的数据是最简单的,而不用分析是否真的有必要这样做或可以使用无状态定向标记完成。
此外,在这种常见的情景中,强烈建议使用定向隔离格式化字符而不是定向嵌入格式化字符(一旦知道目标展示平台支持隔离)。这是因为嵌入会像强字符一样影响周围文本。嵌入的强影响会产生不可预料效果,强影响很少产生有益的效果。为了证明,这里使用嵌入代替隔离:
<pre>Memory: it is called "RLEAN INTRODUCTION TO javaPDF" - $19.95 in hardcover.
Display: it is called "$19.95 - "java OT NOITCUDORTNI NA in hardcover.</pre>
这当然不是预期中的显示,这是因为数字(连同里面所有的中性字符)“粘(sticking)”在前面的RTL嵌入上,就表现得“粘”上了前面的RTL字符了。
定向隔离也提供了一个解决方案,在普通情况下,程序化插入的文本方向是不知道的。与分析插入文本的字符并决定是否使用LRE或RLE(或LRI或RLI或什么都不使用)不同的是,软件可以采取简单的方式,即使用FSI和PDI包裹插每个未知方向。因此,FSI而不是RLI在上述例子中会产生相同的显式。FSI的第一个强启发式并不是可靠的,但它能在大多数情况下甚至在混个文本中起作用。
虽然用隔离包裹插入是一种有用的计数,但更好的方式是,在已知内部没有被隔离包裹的反方向字符的文本时,不包裹文本。不必要的包裹层次不单增大体积和复杂度;它们还可能最终超过了深度限制和渲染无效的内部隔离,这会导致文本错误显示。一个普遍的情况是,不需要包裹的插入是一个已知的本地化的上下文环境,即一个带着插入值的转换消息要不本地化,要不包裹在隔离中。
6.4 分离标点符号
一个常见的问题是其中文本确实表示着一系列具有分隔标点符号的片段,并且通常是串联的。这些分隔符通常是中性字符的字符串。例如,一个网页的底部可能如下:
<pre>advertising programs - business solutions - privacy policy - help - about</pre>
例如,这可能通过分隔符“-”来连接数量不定的字符串建立在服务器上。如果所有的文本转换成阿拉伯语或希伯来语,整体的页面方向设置成RTL,正确的显示结果如下:
<pre>TUOBA - PLEH - YCILOP YCAVIRP - SNOITULOS SSENISUB - SMARGORP GNISITREVDA</pre>
然而,假定在转换中,保留一些LTR字符。这种情况对公司名、产品名、技术术语等来说很常见。如果其中一个分隔符两边是LTR字符,那么会导致页面产生严重的混乱。例如,假如“programs”和“business”以英语术语留下而不转换。结果如下:
<pre>TUOBA - PLEH - YCILOP YCAVIRP - SNOITULOS programs - business GNISITREVDA</pre>
显而易见,第一个术语“advertising business”和第二个“programs solutions”就产生了混乱。对这个问题最简单的解决方式是在每个分隔符字符串中包含一个RLM字符。这将导致各分隔符采用right-to-left的方向,并产生正确的展示:
<pre>TUOBA - PLEH - YCILOP YCAVIRP - SNOITULOS business - programs GNISITREVDA</pre>
显式格式化字符(LRE,RLE,和PDF或LRI,RLI,FSI和PDI)可以用于取得相同的效果;网页会使用带有属性dir="ltr"
或dir="rtl"
的span
标签。嵌入到每个单独字段中,不包括分隔符。总的来说,首选的显式格式化字符LRM和RLM,因为它们的影响范围更本地化,当文本被复制时比使用dir
属性更稳健(理论上程序在将文字转换成纯文本时,会将dir
属性转变成相应的显式格式化字符,但通常并不支持这个功能)。
6.5 转换为纯文本
为了外观一致,当双向文本受制于高级协议来转换成Unicode纯文本时,应该插入格式化字符来确保展示顺序,展示顺序由Unicode双向算法应用程序匹配,通过高级协议指定。每当文本使用高级协议来转换成标记(marked-up)文本时that is unaware of the higher-level protocol.,应该遵守同样的原则。例如,如果高级协议基于L比R/AL字符的数量来设置分段方向为1(R),当转换为纯文本,文本里的分段会被嵌入到括号对RLE..PDF格式化字符中。如果同一文本转换成HTML4.0,属性dir="rtl"
会被添加到分段元素。
7 镜像
镜像属性对确保正确的字符用于所需的语义来说,非常重要。这是特别重要,当字符的名字与所需的语义不符时,如U+0028“(”左括号(LEFT PARENTHESIS)。虽然字符的名字表明它是个左括号,字符确实表现出开括号(在括号短语开头的字符,而不是尾随的那个。)
某些没有Bidi_Mirrored属性的字符在被渲染成镜像图像字符,是由高级协议添加镜像:参阅章节4.3,Higher-Level Protocols,尤其是HL6。除了这种情况,镜像必须根据规则L4,去确保正确的字符被用于表达所需的语义,并避免互相干扰和安全问题。
实行规则L4需要镜像图像字符。这些图像字符可能不会是精确的图形镜像。例如,一个斜体圆括号不是另一个的镜像(“(”不是“)”的图形镜像)。Instead, mirror glyphs are those acceptable as mirrors within the normal parameters of the font in which they are represented.
在实现中,有时候字符对是互相接受的镜像,例如,U+0028“(” 左括号(LEFT PARENTHESIS)和U+0029“)”右括号(RIGHT PARENTHESIS)或U+22E0 “⋠”不领先与或等于(DOES NOT PRECEDE OR EQUAL)和and U+22E1 “⋡”不继承或等于(DOES NOT SUCCEED OR EQUAL)。其他字符例如U+2231 “∱”顺时针积分(CLOCKWISE INTEGRAL)没有相应的可接受的镜像字符。文件BidiMirroring.txt data file[Data9],列出互为镜像字形的字符对。这一数据在[UCD]正式的属性名是Bidi_Mirroring_Glyph。文件中的注释指出镜像对“合适的才是最好的”:虽然理论的镜像字形的形状不同,但在渲染中它们也是可以接受的。
迁移问题
在UBA的Unicode 6.3版本有两个主要的新增功能:
定向隔离
括号对
新的定向隔离的实现应该存在很少的兼容问题;UBA经过谨慎的修改以求缩小与以前的文本的差异。等级数量的限制可能会有些不同,但这在实际中很少碰到。
对于括号对,可能会有更多不同。在不知道(没有良好的用户界面)定向标记或嵌入的情况下,人们构建的文本虽然有正确的视觉呈现但内部结构不正确。(即...[...[...,表现为...[...]...)。新算法捕获这类型的问题,因为这样畸形的括号序列不会被匹配。
然而,某些情况如缺少规则N0的旧实现会产生预期的表现,而新实现不会。用户对实现的反馈对决定添加规则N0具有积极的影响。
对于那些未能正确更新到以前的Unicode版本的实现,也存在一些兼容问题,特别对处理不当的斜线“T 1/2”(T是一个阿拉伯字符)表现出错误的“2/1 T”。
为了减轻兼容问题的影响,强烈建议实现采用以下步骤:
在任何括号的两边,增加适当的定向格式化字符让其能被规则N0解析,这样也能在旧系统正确地表示。也可以在括号两边使用定向标记(RLM或LRM)。为了向前兼容,在旧系统中撰写的文本应该使用语义正确的括号(必要时使用定向格式化字符),以确保Unicode的6.3后的系统实现得到正确的显示。
在围绕数字+斜线+数字(如3/2)的序列添加适合的显式嵌入。
部分重组
在Unicode 6.3,对其文本进行了重要的重组。下表显示了新旧章节变化。
|Unicode 6.3|Unicode 6.2|
|:--|:--|:--|
|2.4 Explicit Directional Isolates|n/a|
|2.5 Terminating Explicit Directional Isolates|n/a|
|2.6 Implicit Directional Marks|2.4|
|3.3.3 Preparations for Implicit Processing|n/a|
|3.3.4 Resolving Weak Types …3.3.6 Resolving Implicit Levels|3.3.3…3.3.5|
|6.1 Joiners|5.3|
|6.2 Vertical Text|5.4|
|6.3 Formatting|5.5|
|6.4 Separating Punctuation Marks|5.6|
|6.5 Conversion to Plain Text|n/a|
|Migration Issues|5.7|
致谢
Mark Davis created the initial version of this annex and maintains the text. Aharon Lanin and Andrew Glass made substantial additions to Revision 29 (Unicode 6.3.0).
Thanks to the following people for their contributions to the Bidirectional Algorithm or for their feedback on earlier versions of this annex: Ahmed Talaat (أحمد طلعت), Alaa Ghoneim (علاء غنيم), Asmus Freytag, Avery Bishop, Ayman Aldahleh (أيمن الدحلة), Behdad Esfahbod (بهداد اسفهبد), Doug Felt, Dwayne Robinson, Eric Mader, Ernest Cline, Gidi Shalom-Bendor (גידי שלום-בן דור), Gilead Almosnino (גלעד אלמוסנינו), Isai Scheinberg, Israel Gidali (ישראל גידלי), Joe Becker, John McConnell, Jonathan Kew, Jonathan Rosenne (יונתן רוזן), Kamal Mansour (كمال منصور), Kenneth Whistler, Khaled Sherif (خالد شريف), Laurențiu Iancu, Maha Hassan (مها حسن), Markus Scherer, Martin Dürst, Mati Allouche (מתתיהו אלוש), Michel Suignard, Mike Ksar (ميشيل قصار), Murray Sargent, Paul Nelson, Peter Constable, Rick McGowan, Robert Steen, Roozbeh Pournader (روزبه پورنادر), Steve Atkin, and Thomas Milo (تُومَاسْ مِيلُو).
参考
修改
以下总结了本附件先前的修订。
完