BIDI算法 (Unicode Bidirectional Algorithm)

Unicode Standard Annex #

 Unicode Bidirectional Algorithm

原文地址:http://www.unicode.org/reports/tr9/

原文翻译文章,转载请注明文章出处  http://blog.csdn.net/minibeargui/article/details/24888797

1导读

Unicode标准规定内存表示顺序称为逻辑顺序。当文本排布在水平行线上时,大多数脚本从左往右显示字符。然而,也有一些脚本(如阿拉伯语或希伯来语)水平显示文本的自然顺序是从右往左。如果所有文本具有单纯一致的水平方向,那么显示文本的顺序就不会不清楚了。

 

但是,由于这些RTL脚本中使用了从左往右书写的数字,这些文本实际上是双向的: 即RTL和LTR文本的混合。除了数字外,还有来自英语和其它脚本的LTR嵌入其中的单词,也会产生双向文本。如果没有一个清晰的规范,当文本的水平方向不统一的时候,在决定显示字符顺序的时候含糊不清的问题将会出现。

 

这个附件描述了用于决定双向Unicode文本方向的算法。这个算法扩展了目前已采用的一些现有实现中的隐式模型,并为特殊情况添加了显式的格式码。在大多数情况下,没有必要再包含额外的文本信息来获取正确的显示顺序。

 

然而,在双向文本的情况下,对于有些情况,一个隐式的双向排序不足以产生可以理解的文本。为了处理这些情况,则定义一个方向格式码的最小的集合,来控制渲染时字符的顺序。这样则可以对显示顺序进行精确的控制以完成清晰的互换,并可以确保用于像文件名或者标签这样的简单项目的纯文本总是可以被正确的排序来显示。

 

方向格式码仅仅被用来改变文本的显示顺序。在其他所有方面它们应该被忽——它们不影响文本的比较或者断字,解析,或数值分析。

 

每个字符都有一个隐式双向类型。双向类型left-to-right right-to-left被称为强类型,具有这些类型的字符被称为强方向字符。与数字相关的双向类型被称为弱类型,具有这些类型的字符被称为弱方向字符。除了方向格式码,剩下的双向类型和字符被称为中性。此算法在文本中依据字符的隐式双向类型以达到对文本的合理的排序显示。

 

当使用双向文本的时候,字符仍然解释为逻辑顺序——只有显示时会被影响。双向文本的显示顺序依赖于文本中字符的方向属性。注意,有一些重要的安全问题与双向文本相关联:请参考[UTR36]来获取更多信息。

 

2方向格式码

三种类型的显式方向格式码被用于修改标准隐式Unicode双向算法(UBA)。此外,还有隐式方向格式码,right-to-left和left-to-right标记。所有这些格式码的效果被限制在当前段落;因此,他们的影响将被一个段落分割符终止。

这些格式码都具有Bidi_Control属性,它们被细分为三组:

 

隐式方向格式码

LRM, RLM, ALM

显式方向嵌入和重写格式码

LRE, RLE, LRO, RLO, PDF

显式方向隔断格式码

LRI, RLI, FSI, PDI

 

在网页上,显式格式码(所有的类型--嵌入,重写和隔断)应该使用dir属性和BDI,BDO元素替代。这并不适用于隐式方向格式码。更多信息,见[UTR20]。

 

虽然术语嵌入用于一些显式格式码,但在嵌入格式码范围内的文本并不独立于周围的文本。嵌入的字符会影响外围字符的顺序,反之亦然。但这又不同于隔断格式码的情况。被隔断的字符不会影响它外围字符的顺序,反之也一样。一个隔断的作用相当于在周围字符的排序中

作为一个整体和中性字符的作用差不多。而一个嵌入或者重写大概类似于一个强字符的效果。

 

方向隔断格式码是在Unicode6.3后推出的,很显然,方向嵌入对它们周围的文本通常有过于强的影响,因而很难于成功使用。新的格式码被推出不是为了改变现有的行为,因为这样做对那些依赖旧的行为的现有的文档可能有不良影响。然而,在新的文档中鼓励使用方向隔断而不是方向嵌入—一旦目标平台都支持它们。

 

2.1显式方向嵌入

下面的格式码是一块文本被视为嵌入的信号。例如,一段英文引用嵌入在阿拉伯文之中将被标记为嵌入的left-to-right文本。如果有一段希伯来文在英文之中,这段希伯来文字将被标记为嵌入的right-to-left文本。嵌入可以和另一个嵌入,隔断和重写之间相互嵌套。

 

Abbr.

Code Point

Chart

Name

Description

LRE

U+202A

LEFT-TO-RIGHT EMBEDDING

将以下文本作为嵌入式left-to-right。

RLE

U+202B

RIGHT-TO-LEFT EMBEDDING

将以下文本作为嵌入式right-to-left。

对right-left行方向进行影响,例如,可以通过在文本中嵌入RLE…PDF。(PDF将在2.3节终止显式方向嵌入和重写一节中描述)

 

2.2显式方向重写

在特别需要的情况下,下面的格式码允许双向字符类型被重写,比如部分数字。出于安全性的考虑,只要有可能应尽量避免它们。更多信息, 见 [UTR36]。方向重写能够与另一个方向重写,嵌入和隔断之间相互嵌套。

 

Abbr.

Code Point

Chart

Name

Description

LRO

U+202D

LEFT-TO-RIGHT OVERRIDE

后面的字符被视为强left-to-right字符

RLO

U+202E

RIGHT-TO-LEFT OVERRIDE

后面的字符被视为强right-to-left字符

这些格式码的确切含义将在算法的不断讨论中变得明晰。Right-to-left 重写,例如,能够被使用于英文,数字和希伯来字母混合制成的某个部分强制从右往左书写。

 

2.3终止显式方向嵌入和重写

下面的格式码结束最后一个LRE,RLE,LRO,或RLO的作用范围,当其范围尚未终止时。

 

Abbr.

Code Point

Chart

Name

Description

PDF

U+202C

POP DIRECTIONAL FORMATTING

结束最后一个LRE,RLE,LRO,或RLO的作用范围

这个格式码的确切含义将在算法的不断讨论中变得明晰。

 

2.4显式方向隔断

下面的格式码是一块文本被视为和他周围文本进行方向隔断的信号。它们和显式嵌入格式码非常相似。但是,一个嵌入在对周围文本上具有一个强字符的影响效果。一个隔断像U+FFFC OBJECT REPLACEMENT CHARACTER具有一个中性影响效果,并在周围文本中分配相应的显示位置。此外,文本内的隔断不会影响到它外围文本的顺序,反之亦然。

 

 

除了允许嵌入强方向文本,而不会过度影响它周围文本的双向顺序,一个隔断格式码也通过了一个额外功能:当从其组成字符中试探性地推断它的方向的时候同时嵌入文本。

 

 

隔断能够与另一个隔断,嵌入和重写之间相互嵌套。

Abbr.

Code Point

Chart

Name

Description

LRI

U+2066

LEFT-TO-RIGHT ISOLATE

将下面的文本视为孤立的left-to-right。

RLI

U+2067

RIGHT-TO-LEFT ISOLATE

将下面的文本视为孤立的right-to-left。

FSI

U+2068

FIRST STRONG ISOLATE

将下面的文本视为孤立的,它的方向是是第一个强字符的方向,并且不是内部的一个嵌套隔断。

 

这些格式码的确切含义将在算法的不断讨论中变得明晰。

 

2.5终止显式方向隔断

下面的格式码结束最后一个LRI,RLI,或FSI的作用范围,当其范围尚未终止时。

 

Abbr.

Code Point

Chart

Name

Description

PDI

U+2069

POP DIRECTIONAL ISOLATE

结束最后一个LRI,RLI,或FSI的作用范围

 

这个格式码的确切含义将在算法的不断讨论中变得明晰。

 

2.6隐式方向标记码

这些字符是非常轻量级的格式。他们的行为完全与right-to-left或left-to-right格式码一样,只是他们不显示出来或有任何其他语义效果。它们的使用比使用显式的嵌入或重写更方便,因为它们的影响范围非常多的本地化。

 

Abbr.

Code Point

Chart

Name

Description

LRM

U+200E

LEFT-TO-RIGHT MARK

Left-to-right 0宽字符

RLM

U+200F

RIGHT-TO-LEFT MARK

Right-to-left 0宽非阿拉伯字符

ALM

U+061C

ARABIC LETTER MARK

Right-to-left 0宽阿拉伯字符

在接下来的算法中也没有特别提及隐式方向标记。这是因为它们在双向排序中的效果和强方向字符的效果相对应;唯一的区别就是他们不会被显示出来。

 

2.7标记和格式化码

显式格式码状态引入到纯文本中,必须保持在编辑或显示文本中。流程中没有意识到这种状态而修改了文本,可能会无意中影响到大部分渲染中的文本,例如,通过移除一个PDF。

 

Unicode双向算法的设计为通过out-of-line信息,显式格式码能够被如样式表信息或标记等效表示地使用。如果标记和显式格式码同时使用在同一段中,可能会发生冲突。当可行时,应使用标记而不是显式格式码。然而,在这个算法中,任何替代表示仅是已被定义的以确保符合Unicode标准中相应的显式格式码行为为参考。

 

HTML 5 提供了对bidi标记的支持,如下所示:

 

Unicode

Equivalent markup

Comment

RLI

<bdi dir = "rtl">

 

LRI

<bdi dir = "ltr">

 

FSI

<bdi>

或者 <bdi dir = "auto">

RLO

<bdo dir = "rtl">

 

LRO

<bdo dir = "ltr">

 

RLE

dir = "rtl"

属性在block或inline元素中

LRE

dir = "ltr"

属性在block或inline元素中

PDF, PDI

终止标记

 

 

每当从一个文档纯文本中产生包含标记,就应该引入等效格式码,这样正确的排序是不会丢失的。例如,每当在纯文本中剪切和粘贴结果,这些处理就应该发生。

 

3基本显示算法

Unicode双向算法(UBA)采用文本流作为输入并有四个主要阶段:

 

  • 分割成段落。 该算法的其余部分将分别应用于各个段落中的文本。
  • 初始化。一个双向字符类型表将被初始化,原始文本中每个字符对应此表中一个条目。每个条目的值是各个字符的Bidi_Class属性值。一个嵌入层级表,每个字符将对应此表中一个层级,然后初始化。注意,原始字符在3.3.5节,解决中性和隔断格式码类型中被引用。
  • 解析嵌入层级。一系列的规则被应用到嵌入层级表和双向字符类型表。每个规则都操作这些列表的当前值并能够修改这些值。原始字符和它们的Bidi_Paired_Bracket以及Bidi_Paired_Bracket_Type属性值在某些规则的应用程序中被引用。这个阶段的结果是一个修改的嵌入层级表。双向字符类型表将不再被需要。
  • 重排。每个段落中的文本被重排显示:首先,段落文本打断成行,然后求解嵌入层级用于显示每行文本。

 

该算法重排仅仅作用在一个段落内。一个段落的字符不会影响另一个不同段落的字符。段落的划分是通过段落分隔符或者适当的换行函数进行的(关于CR,LF,和CRLF的处理指导见4.4节,方向性,和5.8节,Unicode的换行指导)。段落还可以通过更高级别的协议来确定:例如,在一个表格的两个不同单元格中的文本将被分在不同的段落中。

 

在内存表示中,组合字符总是附着在前面的基本字符上。即使重排显示和执行字符整形之后,在内存中,一个复合字符的代表字形将附着在它的基本字符代表的字形上。根据行方向和基本字体字形的放置方向,它可能,如,附着在字形的左边,或者右边,又或者上方。

 

本附件使用的规范定义和规则的编号约定在表格1中。

表格1 规范定义和规则

编号

Section

BDn

释义

Pn

段落

Xn

显式层级和方向

Wn

弱类型

Nn

中性类型

In

隐式层级

Ln

解析的层级

 

3.1释义

3.1.1基础知识

BD1. 双向字符类型是分配给每个Unicode字符的值,包括未分配的字符。在Unicode字符数据库[UCD]它的正式属性名称是Bidi_Class。

 

BD2. 嵌入层级是表示文本嵌套的深入程度的数字和在此层级上文本的默认方向。最小的嵌入层级为零,显式的最大深度是125,一个被称为MAX_DEPTH的值在该文档的其余部分被说明。

 

作为规则通过X1到X8将指定,通过显式格式码(嵌入,隔断和重写)来设置嵌入层级;更高的数字意味着更深层次的嵌套文本。在保证实现相同结果的前提下,有限制嵌套深度的原因是因为提供了一个精确的有极限的堆栈。总共125层但远远超出了排序的要求,即使机械地生成格式也足够了;更多的嵌入层级会比少量的嵌入会使得显示的时候变得更加模糊。

 

BD3. 当前嵌入层级(问题是针对于字符的)的默认方向叫嵌入方向。如果嵌入层级是偶数它被称为L,如果嵌入层级为奇数,它被称为R。

 

例如,在一特定的一段文本中,层级0是纯英文文本。层级1是纯阿拉伯文本,它可能是嵌入在层级0的英文文本之中。层级2是英文文本,它可能是嵌入在层级1的阿拉伯文本之中,以此类推。除非它们的方向被覆盖,否则英文文本和数字将总是一个偶数层级;阿拉伯文(排除数字)将总是一个奇数层级。嵌入层级的确切含义在后续算法的讨论中将变得明晰,但下面将先提供一个算法如何工作的例子。

BD4. 段落嵌入层级是在段落中嵌入层级用于确认文本的默认双向方向。

BD5. 段落嵌入层级的方向被称为段落方向。

  • 在某些情况下段落方向也被称为基本方向。

BD6. 方向重写状态决定了字符的双向类型将被重置。方向重写状态通过使用显式格式码来设置。这个状态有三种状态值,如表2所示。

表2 方向重写状态

状态

Interpretation

Neutral

不重写当前活动的

Right-to-left

字符被是设置为 R

Left-to-right

字符被是设置为L

 

BD7. 一个层级片是在一个相同层级中一组字符的最大字串。在具有相同层级的这组字串中它的前或后都不再有相同层级的字符(一个层级片也被称为一个方向片)。

 

正如下面指出,层级片在双向算法的两个不同阶段都是很重要的。第一个阶段发生在规则X1至X9在段落方向和显式方向格式码的基础上已经为每个字符分配了显式嵌入层级。在此阶段,在规则X10,层级片被使用于建立后续规则所应用到的单元。这些规则在隐式双向类型的基础上进一步调整每个字符的嵌入层级,并且调整这个单元中的其它字符—不是单元的外围。层级片的结果来自这些已解析的嵌入层级,通过规则L2使用在实际的文本重排中。下面的示例阐述了层级片在该算法的后期阶段的情况。

例子

 

在下面的例子,案例是用于那些不熟悉的righit-to-letf字母表示不同的隐式字符类型。大写字母代表right-to-left字符(如阿拉伯语和希伯来语),小写字母代表left-to-right字符(如英语或俄罗斯语)。

 

存储:                    car is THE CAR in arabic

字符类型:           LLL-LL-RRR-RRR-LL-LLLLLL

段落层级:           0

已解析的层级:  000000011111110000000000

 

注意,中性字符(空格)在THE和CAR之间,它使用了周围字符的层级。中性字符的层级可以通过在中性字符周围插入适当的方向标记来改变,或者使用显式的方向格式码来改变它的层级。

3.1.2匹配显式方向格式码

BD8. 一个隔断的引发码是一个类型为LRI,RLI,或FSI的字符。

 

由规则X5a至X5c将指出,一个隔断引发码,它执行的规则规则深度限制允许它的字符嵌入层级。

 

BD9. 对于一个给定的隔断引发码匹配的PDI是由下面的算法来确定的:

  • 初始化一个计算器为一。
  •  扫描这个隔断引发码之后的文本到段落结束,在每个隔断引发码处计数器递增,并在每个PDI处计数器递减。
  • 停在第一个PDI,如果有的话,那么该计数器将会递减到零。
  • 如果发现这种PDI,它是这个给定的隔断引发码所匹配的PDI。否则,没有能够为它匹配的PDI。

 

需要注意的是,当找到匹配的PDI时,除了隔断引发码和PDIs外,所有其它格式码都将被忽略。

 

注意,该算法为一个隔断引发码分配一个匹配的PDI(或缺乏之),而不管隔断引发码是增加了嵌入层级,还是通过限制深度的规则来阻止这样做。

 

由规则X6a将指出,一个匹配的PDI返回嵌入层级的值为它前面相匹配的隔断引发码嵌入层级的值。这个PDI自身被分配到新的嵌入层级。如果它不能匹配任何隔断引发码,或者如果隔断引发码没有增加嵌入层级,它离开这个嵌入层级将不会改变层级。因此,一个隔断引发码和它的匹配PDI总是被分配到相同的显式嵌入层级。在双向算法的后期阶段,一个隔断引发码和它的匹配PDI功能上相当于无形的中性字符,并且它们的嵌入层级有助于确保隔断码在它外围文本的显示顺序上具有一个中性字符的影响效果,并在周围文本中分配到相应的显示位置。

 

BD10. 一个嵌入引发码是一个类型为LRE,RLE, LRO, 或者 RLO的字符。

 

注意,一个嵌入引发码是一个方向嵌入或一个方向重写的任意之一作为开始;它的名称省略重写只是为了简明性。

 

由规则X2到X5将指出,一个嵌入引发码增加嵌入层级,强制执行深度限制规则允许它的字符嵌入级别。

 

BD11. 对于一个给定的嵌入引发码匹配的PDF是由下面的算法来确定的:

  •  初始化一个计数器为一。
  •  扫描嵌入引发码后面的文本:
  •  在一个隔断引发码处,跳过匹配的PDI,或者如果没有匹配的PDI,到段落的结尾处。
  • 在一个段落结尾处,或在这个给定嵌入引发码前面的一个隔断引发码匹配的一个PDI处,停止:这个给定嵌入引发码没有匹配的PDF。
  •  在嵌入引发码处,计数器递增。
  •  在一个PDF处,计数器递减。如果它的新值为零,停止:这就是给定嵌入引发码被匹配的PDF。

注意,该算法为一个嵌入引发码分配一个匹配的PDF(或缺乏之),而不管嵌入引发码是增加了嵌入层级,还是通过限制深度的规则来阻止这样做。

 

虽然上面的算法给出了术语“匹配PDF”的确切含义,但请注意整体双向算法从来没有实际要求寻找一个匹配嵌入引发码的PDF。相反,规则X1到X7指出了一种机制来确定嵌入引发码的范围,如果存在,将通过一个PDF终止,即有效嵌入引发码匹配的一个PDF。

 

由规则X7将指出,a matching PDF returns the embedding level to the value it hadbefore the embedding initiator that the PDF matches. 如果它不匹配任何嵌入引发码,或者如果嵌入引发码没有增加嵌入层级,一个PDF离开时嵌入层级将不被改变。

 

由规则X9将指出,一旦在一个段落中显式方向格式码已被使用于为字符分配嵌入层级,嵌入引发码和PDFs将从这个段落中移除(或实质性移除)。因此,嵌入层级分配给嵌入引发码和PDFs自身是无关紧要的。在这方面,嵌入引发码和PDFs不同于隔断引发码和PDIs,会继续在如上所述的确定段落的显示顺序方面发挥作用。

 

BD12. 方向隔断状态 是一个布尔值,通过使用隔断格式码设置此值:当当前嵌入层级是以一个隔断引发码作为起始的,此状态值置为真。

 

BD13. 一个隔断片序列就是一组层级片的最大序列,对于这样的,在这个序列中所有层级片除了最后一个片,这个片的最后一个字符是一个隔断引发码,其匹配的PDI是这个序列中下一个层级片的第一个字符。这个最大意义上说明,如果这个序列中的第一个层级片的第一个字符是一个PDI,它必定没有任何可匹配的隔断引发码,如果在这个序列中的最后一个层级片的最后一个字符是隔断引发码,它必定没有一个与之匹配的PDI。

 

可由下面的算法计算一个段落中的隔断片序列集:

  •  开始于一个隔断片序列的空集。
  •  对于段落中每个层级片,它的第一个字符不为PDI,或者可以是不能匹配层级片中任何隔断引发码的PDI:
  •  创建一个新的层级片序列,并将其初始化为包含刚好所在的层级片。
  • 当在这个序列结尾的层级片当前的最后位置有一个隔断引发码,则添加一个含有与之相匹配的PDI的层级片到这个序列。(注意这个相匹配的PDI必须是这个层级片的第一个字符。)
  • 添加层级片组的序列结果到隔断片序列集中。

注意:

  •  在一个段落中每个层级片完全属于一个隔断片序列。
  • 一个段落中,在没有隔断引发码的情况,每个隔断片序列完全包含一个层级片,每个层级片构成一个单独的隔断片序列。
  •  在一个隔断片序列中对于两个相邻的层级片,一旦一个结尾为隔断引发码,那么其匹配的PDIs必在另一个的起始位置。因此,在一个隔断片中的所有层级片都具有相同的嵌入层级。
  •  当一个隔断引发码提升了嵌入层级,这个隔断引发码和它相匹配的PDI两者,如果存在,将获得原始的嵌入层级,不会再提升一个级别。因此,在这个段落中,如果匹配的PDI没有立即跟随这个隔断引发码,那么这个隔断引发码会是这个层级片的最后一个字符,但这个匹配的PDI,如果存在,在同样的隔断片序列中,它是层级片的第一个字符并立即跟随这个隔断引发码。另一方面,在这个段落中,层级片跟随隔断引发码开始一个新的隔断片序列,在这个段落中,层级片前面匹配的PDI(如果存在)结束它的隔断片序列。

 

在下面的例子中,假定:

  • 段落的嵌入层级为0。
  •  字符序列文本没有包含任何显式格式码或段落分隔符。
  • 点的使用仅是为了改进例子的视觉清晰度;它们不是文本的一部分。
  •  段落文本中的字符被分配了嵌入层级如上面描述的一样松散,在每个例子中它们来之层级片的给予。

 

例子1

段落文本: text1·RLE·text2·PDF·RLE·text3·PDF·text4

层级片:

  • text1 – level 0
  • text2·text3 – level 1
  • text4 – level 0

已解析的隔断片序列:

  • text1 – level 0
  • text2·text3 – level 1
  • text4 – level 0

例子2

段落文本: text1·RLI·text2·PDI·RLI·text3·PDI·text4

层级片:

  • text1·RLI – level 0
  • text2 – level 1
  • PDI·RLI – level 0
  • text3 – level 1
  • PDI·text4 – level 0

已解析的隔断片序列:

  • text1·RLI PDI·RLI PDI·text4 – level 0
  • text2 – level 1
  • text3 – level 1

 

例子3

段落文本:text1·RLI·text2·LRI·text3·RLE·text4·PDF·text5·PDI·text6·PDI·text7

层级片:

  • text1·RLI – level 0
  • text2·LRI – level 1
  • text3 – level 2
  • text4 – level 3
  • text5 – level 2
  • PDI·text6 – level 1
  • PDI·text7 – level 0

已解析的隔断片序列:

  • text1·RLI PDI·text7 – level 0
  • text2·LRI PDI·text6 – level 1
  • text3 – level 2
  • text4 – level 3
  • text5 – level 2

 

由规则X10将指出,一个隔断片序列单元将有以下规则被应用,并且在这个算法阶段的时候,序列中一个层级片的最后一个字符被认为是紧随序列中下一个层级片的第一个字符。由于这些规则都是基于字符的隐式双向类型,一个隔断在对周围文本顺序上确有与一个中性字符相同的影响效果 — 或者,更精确的说,中性字符,隔断引发码和PDI,这一对,在这些规则中的行为就如中性字符一般。

 

3.1.3配对的括号

以下定义利用规范属性Bidi_Paired_Bracket 和 Bidi_Paired_Bracket_Type,它们被定义在Unicode字符数据库[UCD]的BidiBrackets.txt文件中。

 

BD14. 一个打开中的配对的括号是其Bidi_Paired_Bracket_Type属性值为Open的一个字符。

 

BD15. 一个关闭中的配对的括号是其Bidi_Paired_Bracket_Type属性值为Close的一个字符。

 

BD16. 一对括号是一对由一个打开中的配对的括号和一个关闭中的配对的括号组成的字符,旧的Bidi_Paired_Bracket属性值或者它是标准等效于后者或者在一个隔断片序列中它是标准等效于算法识别在指定的文本位置。下面的算法在一个给定的隔断片序列中识别所有的括号对:

  •  创建一个堆栈,其中每个元素由一个括号字符和一个文本位置组成。初始为空。
  •  创建一个列表,其中每个元素由两个文本位置,一个打开中的配对的括号和另一个相应的关闭中的配对的括号组成。初始化为空。
  •  以逻辑顺序在隔断片序列中检查每个字符。
  •  如果一个打开中的配对的括号被找到,压入它的Bidi_Paired_Bracket属性值和它的文本位置到堆栈中。
  •  如果一个关闭中的配对的括号被找到,按下面做:
  1.     声明一个变量用于保存当前堆栈元素的一个引用并用堆栈的顶部   元素初始化它。
  2.    在当前堆栈元素中,比较关闭中的配对的括号被检查或它是标准等效于这个括号。
  3.    如果值相匹配,意思就是两个字符形成括号对,那么,
  •  添加当前堆栈元素的文本位置和关闭中的配对的括号的文本位置到列表中。
  •  把内含的当前堆栈元素弹出堆栈。

        4.      否则,如果当前堆栈元素不是堆栈的底部,移动到堆栈中较深的下一个元素位置并返回步骤2。

        5.      否则,继续检查下一个字符不再弹出堆栈。

  •   基于打开中的配对的括号的文本位置,按升序排列文本位置对的列表。

 

注意括号配对仅可以发生在一个隔断片序列内,因为它们被处理在规则N0,显式层级求解之后。见3.3.2节,显式层级和方向。

 

括号配对的例子

 

        文本        配对中

        1 2  3 4 5 6 7 8

        a )  b  (  c                  None

        a (  b  ]  c                  None

        a (  b  )  c                  2-4

        a (  b  [  c  )  d  ]        2-6

        a (  b  ]  c  )  d           2-6

        a (  b  )  c  )  d           2-4

        a (  b  (  c  )  d           4-6

        a (  b  (  c  )  d  )        2-8, 4-6

        a (  b  {  c  }  d  )        2-8, 4-6

3.1.4其它缩写

表3列出了使用于例子中的其它缩写和使用于算法的内部字符类型。

表3  用于例子和内部类型的缩写


符号

描述

NI

中性字符或隔断格式码 (B,S,WS,ON,FSI,LRI,RLI,PDI).

e

文本顺序类型 (L orR),它与嵌入层级的方向 (even or odd)一致.

o

文本顺序类型(L orR),它的方向与嵌入层级的方向 (even or odd)的反方向一致.

注意这个oe的反向。

sos

文本顺序类型 (L orR)在一个隔断片序列前面分配到的虚拟位置.

eos

文本顺序类型(L orR)在一个隔断片序列后面分配到的虚拟位置.



3.2双向字符类型

每个字符的规范性双向字符类型被指定的Unicode字符数据库[UCD]中并在表4中汇总。这里仅是一个摘要:也有常用范围例外的情况。例如,某些字符,如U+0CBF卡纳达语元音,我给定的是类型L(而不是NSM)来维护标准等效。

  • 术语西欧数字是被用来指常见于欧洲和其它地方的十进制形式,阿拉伯—印度数字是指本地阿拉伯语形式。(见8.2节,[Unicode]的阿拉伯语,关于命名数字的更多细节)
  •  算法中未赋值的字符被给予强字符类型。通常对于未赋值的字符Unicode一致性要求一个显式的异常。随着字符将来被赋值,这些双向类型可能会改变。分配字符类型,见[UCD]中的DerivedBidiClass.txt文件[DerivedBIDI]。
  •  私有使用的字符可以通过一个一致性实现被赋予不同的值。
  • 对于双向字符的目的,内联对象(如图形)被视为如果它们是一个 U+FFFC对象替换字符。
  •  由Unicode4.0,几个印度字符的双向字符类型被改变,这样双向算法保持了标准等效。也就是说,两个标准等效字符串在应用过算法之后将导致等效排序。这在将来将保持不变。

 

注意:双向算法不保持兼容性等价。

 表4 双向字符类型

类别

类型

描述

常用范围

强类型

L

Left-to-Right

LRM,大部分字母、音节,汉字象形文字,非欧洲或非阿拉伯数字, ...

R

Right-to-Left

RLM,希伯来字母,以及相关的标点符号

AL

Right-to-Left Arabic

ALM,阿拉伯语,Thaana,和叙利亚字母,大多数特殊脚本的标点符号, ...

弱类型

EN

European Number

西欧数字,东亚阿拉伯 - 印度数字, ...

ES

European Number Separator

加号,减号

ET

European Number Terminator

度的符号, 货币符号, ...

AN

Arabic Number

阿拉伯 - 印度数字,阿拉伯语小数点和千位分隔符, ...

CS

Common Number Separator

冒号, 逗号, 句号, 无中断空格 ...

NSM

Nonspacing Mark

字符标记Mn(Nonspacing_Mark)和Me(Enclosing_Mark)在Unicode字符数据库中

BN

Boundary Neutral

默认 ignorables,非字符和控制字符,除了那些显式授予的其他类型。

中性类型

B

Paragraph Separator

段落分隔符,合适的换行函数,更高级别确定段落的协议

S

Segment Separator

制表符

WS

Whitespace

空格,图空格,行分隔符,换页,常用标点符号的空格, ...

ON

Other Neutrals

所有其他字符,包括对象替换字符

显式格式码

LRE

Left-to-Right Embedding

LRE

LRO

Left-to-Right Override

LRO

RLE

Right-to-Left Embedding

RLE

RLO

Right-to-Left Override

RLO

PDF

Pop Directional Format

PDF

LRI

Left-to-Right Isolate

LRI

RLI

Right-to-Left Isolate

RLI

FSI

First Strong Isolate

FSI

PDI

Pop Directional Isolate

PDI

 

3.3解析嵌入层级

双向算法的主体使用双向字符类型,显示格式码,和括号对来产生一个解析的层级列表。这个解析过程包括了以下步骤:

  •   应用规则P1将文本分割成段落,并为每个这些段落:
  •   应用规则P2和规则P3来确定段落层级。
  •  应用规则X1(它调用规则X2- X8),来确定显式嵌入层级和方向。
  •  应用规则X9从而进一步考虑消除很多控制字符。
  •   应用规则X10分割段落成隔断片序列并为每片这些隔断片序列:
  •  应用规则W1-W7解析弱类型。
  •   应用规则N0-N2解析中性类型。
  •  应用规则I1-I2解析隐式嵌入层级。

 

3.3.1段落层级

P1. 分割文本成单独的段落。一个段落分隔符是用来和前一个段落保持分隔。在每个段落内,应用所有其它算法规则。

 

P2. 在每个段落中,找到类型为L,AL,或R的第一个字符,同时跳过任何字符之间的隔断引发码到它匹配的PDI之后,或者它没有匹配的PDI,直至段落的结尾。

 

注意:

 

  •  因为该算法中段落分割符分隔文本,通过这条规则发现的这个字符通常是在一个段落分隔符之后的首个强字符或者在文本的开始处。
  • 该规则指出在隔断引发码和其匹配的PDI之间的字符将被忽略,因为一个方向隔断码应该具有一个中性字符对周围文本顺序相同的影响效果,该规则忽略中性字符。
  • 该规则指出在隔断引发码和其匹配的PDI之间的字符将被忽略,即使深度限制(在规则X5a到X5c中定义)可以防止隔断引发码提升嵌入层级。这是为了使规则更容易实现。
  • 该规则忽略嵌入引发码(但不是嵌入其中的字符)。

 

P3. 如果在P2中找到类型为AL或R的一个字符,那么设置段落嵌入层级为一,否则,设置它为零。

 

每当较高级别的协议指定了段落层级,可能会覆盖规则P2 P3:见 HL1

 

3.3.2显式层级和方向

所有显式嵌入层级由显式方向格式码(嵌入,重写,和隔断)通过应用显式层级规则X1来确定。这个执行逻辑传递过段落时,依次为每个字符应用规则X2-X8。在这个传递期间,下面的变量被使用:

 

  •   一个最大深度+2条目的方向状态,其中每个条目包含:
  • 一个嵌入层级,至少是零,最多是Max_depth。
  •  一个方向重写状态。
  • 一个方向隔断状态。

         除了支持常用的破坏性“弹出”操作,堆栈也允许只读它的最后(即顶部)条目而不弹出它。为了提高效率,最后的条目能够被保存在一个单独变量中以取代在方向状态堆栈中。但没有优化的算法将更容易理解。传递开始,方向状态堆栈会参照段落嵌入层级,和值为中性的方向重写状态,以及值为假的方向隔断状态,来初始化一个条目;这个条目不会被弹出直到段落的结尾。传递期间,方向状态堆栈在当前所处位置总是包含所有方向嵌入,重写,和隔断– 排除那些将溢出的深度限制– 除了在堆栈开始处的段落层级条目。最后的条目反映了在传递的当前位置处最深的有效范围。实现者可能发现在每个堆栈条目中包含更多的信息会非常有用。例如,在一个隔断条目,隔断引发码的位置能被用于生成一个从每个有效隔断引发码到相匹配的PDI位置之间映射图,反之亦然。但是,这样的优化超出了这个规范的范围。

  •  一个计数器用于溢出隔断计数。

这反映了传递到目前为止遇到的没有能够相遇它们匹配PDIs的隔断引发码的数量,但通过深度限制是无效的,因而这个情况不会反映在方向状态堆栈上。它们是一个嵌套一个,并是堆栈的最后超出的范围。这个计数被用于确定是否一个新遇到的PDI匹配和终止了一个溢出隔断引发码的范围。因而递减计数,而不是可能去匹配和终止一个有效的隔断引发码的范围,这样做会导致在方向堆栈中弹出其条目而丢失。它也被用于确定在这个溢出隔断码的范围内是否有一个新遇到的PDF落下,并因此可以完全忽略(无论是否在相同的溢出隔断内它匹配一个嵌入引发码又或者什么也没有)。

  •  一个计数器用于溢出嵌入计数。

这反映了传递到目前为止遇到的没有能够相遇它们匹配PDF的嵌入引发码的数量,或者遇到一个嵌套其中的隔断的PDI,但通过深度限制是无效的,因而这种情况不会反映在方向状态堆栈上。它们是一个嵌套一个,并是堆栈的最后超出范围。这个计数被用于确定是否一个新遇到的PDF匹配和终止了一个溢出嵌入引发码的范围。因而递减计数,而不是可能去匹配和终止一个有效的嵌入引发码的范围,这样做会导致在方向堆栈中弹出其条目而丢失。但是,这个计数不包含在一个溢出隔断范围内遇到的嵌入引发码(即当上面的溢出隔断计数大于零时遇到的)。当溢出隔断计数变为零时,那些落在一个溢出隔断范围内的溢出嵌入引发码的范围会被终止。因此,它们不需要被计数。事实上,如果它们在溢出嵌入计数中是计数的,当匹配一个溢出隔断引发码的一个PDI被遇到时,就没有办法正常更新数量:因为没有一个溢出范围堆栈,就没有办法知道有多少(如果存在)落在溢出隔断范围内的溢出嵌入引发码。

  • 一个计数器用于有效隔断计数。

这反映了传递到目前为止遇到的没有能够相遇它们匹配PDIs的隔断引发码的数量,并且根据深度限制判定为有效,即堆栈的所有条目有一个为true的方向隔断状态。它忽略了所有嵌入和重写,并被用于无需通过方向状态堆栈来确定通过传递当溢出隔断计数为零时,是否有一个匹配一些有效隔断的PDI被遇到或什么都没有。

 

注意,不需要一个有效的嵌入计数来告诉是否遇到了一个PDF匹配于一个有效的嵌入引发码或什么都没有。可以通过检查方向状态堆栈上的尾部条目的方向隔断状态和堆栈中条目的数量来确定。如果尾部条目有一个true值的方向隔断状态,这个PDF是落在了一个方向隔断内。由于这个PDF不能匹配一个隔断外围的嵌入引发码,并且没有一个嵌入条目在这个隔断内部,这个PDF将没有任何匹配。如果结尾条目有一个flase值的方向隔断状态,但又是堆栈中的仅有的一个条目,这个条目属于段落层级,因而这个PDF再次无法有任何匹配。

 

处理每个字符时,这些变量的值被修改,字符的显式嵌入层级的修改是通过规则 X2到X8来定义的,这些规则以字符的双向类型和变量的当前值为基础。

 

X1. 在一个段落的开始,执行下面步骤:

 

  • 设置堆栈为空。
  • 把一个包含段落嵌入层级,一个值为中性的方向重写状态,和一个值为假的方向隔断状态的条目压入堆栈。
  • 设置溢出隔断计数为零。
  • 设置溢出嵌入计数为零。
  • 设置有效隔断计数为零。
  •  应用规则X2到X8,迭代处理每个字符。在这个阶段嵌入层级从0到max_depth是有效的。(注意,在规则I1和I2内,层级的解析中,最大能够达到max_depth+1的嵌入层级。)

 

显式嵌入

 

X2. 对于每个RLE,执行下面步骤:

  • 计算至少奇数嵌入层级大于方向状态堆栈中的尾部条目的嵌入层级。
  • 如果这个新的层级是有效的,并且溢出隔断计数和溢出嵌入计数同时是零,那么这个RLE是有效的。往方向状态堆栈中压入一个包含新嵌入层级,值为neutral的方向重写状态,和值为false的方向隔断状态的条目。
  • 否则,这是一个溢出的RLE。如果溢出隔断计数为零,那么溢出嵌入计数增加一。保持所有其它变量不变。

例如,假设溢出计数都为零,level 0 → 1; levels 1, 2 → 3; levels 3, 4 → 5;等。在达到MAX_DEPTH或者如果溢出计数不为零,层级将保持不变(溢出RLE) 。

 

X3. 对于每个LRE,执行下面步骤:

  • 计算至少偶数嵌入层级大于方向状态堆栈中的尾部条目的嵌入层级。
  •  如果这个新的层级是有效的,并且溢出隔断计数和溢出嵌入计数同时是零,那么这个LRE是有效的。往方向状态堆栈中压入一个包含新嵌入层级,值为neutral的方向重写状态,和值为false的方向隔断状态的条目。
  •   否则,这是一个溢出的RLE。如果溢出隔断计数为零,那么溢出嵌入计数增加一。保持所有其它变量不变。

例如,假设溢出计数均为零,levels 0, 1 → 2; levels 2, 3 → 4; levels 4, 5 → 6; 等。在达到MAX_DEPTH或MAX_DEPTH - 1(哪个,是偶数,就必须去MAX_DEPTH +1 )或者如果溢出计数不为零,层级将保持不变(溢出LRE ) 。

 

显式重写

 

一个显式方向重写设置嵌入层级的方法和显式嵌入格式码的做法是相同的,但也会将受影响字符的双向字符类型改为重写方向。

 

X4. 对于每个RLO,执行下面步骤:

  •  计算至少奇数嵌入层级大于方向状态堆栈中的尾部条目的嵌入层级。
  •  如果这个新的层级是有效的,并且溢出隔断计数和溢出嵌入计数同时是零,那么这个RLO是有效的。往方向状态堆栈中压入一个包含新嵌入层级,值为righit-to-left的方向重写状态,和值为false的方向隔断状态的条目。
  •  否则,这是一个溢出的RLO。如果溢出隔断计数为零,那么溢出嵌入计数增加一。保持所有其它变量不变。

 

X5. 对于每个LRO,执行下面步骤:

  • 计算至少偶数嵌入层级大于方向状态堆栈中的尾部条目的嵌入层级。
  •  如果这个新的层级是有效的,并且溢出隔断计数和溢出嵌入计数同时是零,那么这个RLO是有效的。往方向状态堆栈中压入一个包含新嵌入层级,值为left-to-right的方向重写状态,和值为false的方向隔断状态的条目。
  •   否则,这是一个溢出的RLO。如果溢出隔断计数为零,那么溢出嵌入计数增加一。保持所有其它变量不变。

 

隔断

 

X5a. 对于每个RLI,执行下面步骤:

  •  设置RLI的嵌入层级为方向状态堆栈的尾部条目的嵌入层级。
  •  计算至少奇数嵌入层级大于方向状态堆栈中的尾部条目的嵌入层级。
  • 如果这个新的层级是有效的,并且溢出隔断计数和溢出嵌入计数同时是零,那么这个RLI是有效的。有效隔断计数增加一。往方向状态堆栈中压入一个包含新嵌入层级,值为neutral的方向重写状态,和值为true的方向隔断状态的条目。
  •  否则,这是一个溢出的RLI。 溢出隔断计数增加一。保持所有其它变量不变。

 

X5b. 对于每个LRI,执行下面步骤:

  • 设置LRI的嵌入层级为方向状态堆栈的尾部条目的嵌入层级。
  • 计算至少偶数嵌入层级大于方向状态堆栈中的尾部条目的嵌入层级。
  • 如果这个新的层级是有效的,并且溢出隔断计数和溢出嵌入计数同时是零,那么这个LRI是有效的。有效隔断计数增加一。往方向状态堆栈中压入一个包含新嵌入层级,值为neutral的方向重写状态,和值为true的方向隔断状态的条目。
  •  否则,这是一个溢出的LRI。 溢出隔断计数增加一。保持所有其它变量不变。

 

X5c. 对于每个FSI,应用规则P2和P3到在FSI和其匹配的PDI之间的字符序列上,或者如果没有匹配的PDI,而是段落的结尾,就好像此字符序列是一个段落。如果这个规则确定了段落嵌入层级为1,则把FSI视为RLI使用规则X5a。否则,视为LRI使用规则X5b。

 

注意,新的嵌入层级不能通过P2和P3来确定这个段落嵌入层级。它有一个或两个层级,为LRI或RLI。

 

非格式码字符

X6. 除了B,BN,RLE,LRE,RLO,LRO,PDF,RLI,LRI,FSI, 和PDI之外的所有类型:

  •  设置当前字符的嵌入层级为方向状态堆栈上尾部条目的嵌入层级。
  •  每当在方向状态大早上的尾部条目的方向重写状态不为neutral时,根据堆栈中尾部条目的方向重写状态重设当前字符类型。

 

换句话说,如果在方向状态堆栈上的尾部条目的方向重写状态是neutral时,那么字符保留其正常的类型:阿拉伯字符保持AL,拉丁字符保持L,空格保持WS,等等。如果方向重写状态是right-to-left,那么字符类型变为R。如果方向重写状态是left-to-right,那么字符类型变为L。

 

需要注意的是当前的嵌入层级不能通过这个规则改变。

 

终止隔断

一个PDI终止和它相匹配的隔断引发码的范围。它也终止在它相匹配的隔断引发码内所有嵌入引发码的范围,这是对于没有遇到匹配的PDF的那些其中的嵌入引发码来说。如果它不能匹配任何隔断引发码,它将被忽略。

 

X6a. 对于每个PDI,执行下面步骤:

  •  如果溢出隔断计数是大于零,说明这个PDI匹配一个溢出隔断引发码。溢出隔断计数减一。
  •  否则,如果有效隔断计数是零,说明这个PDI不匹配任何隔断引发码,有效或者溢出的。什么也不做。
  •  否则,这个PDI匹配一个有效的隔断引发码。执行下面步骤:
  • 重设溢出嵌入计数为零。(这终止了在相匹配隔断引发码范围内的那些溢出的嵌入引发码的范围,这些范围不能再通过一个匹配的PDF来终止,并因此缺乏了一个匹配的PDF。)
  • 循环检测堆栈尾部条目的方向隔断状态是否为false,如果是,则从方向状态堆栈中弹出尾部条目。(这终止了在相匹配隔断引发码范围内的那些有效的嵌入引发码的范围,这些范围不能再通过一个匹配的PDF来终止,并因此缺乏了一个匹配的PDF。鉴于有效隔断计数非零,在此步之前,方向状态堆栈必须包含一个方向隔断状态为true的条目,并因而在此步之后在堆栈的尾部条目中确实有一个值为true的方向隔断状态,即表示被匹配的隔断引发码的范围。这不可能是堆栈的第一个条目,第一个条目总是属于段落层级,并具有一个值为flase的方向状态,因此在堆栈的第一个条目之前至少有一个以上的条目。)
  • 从方向状态堆栈中弹出尾部条目并让有效隔断计数减一。(这终止了匹配隔断引发码的范围。由于前一步骤至少留下两个条目在堆栈中,此弹出操作不会留下空堆栈。)
  • 在所有的情况下,离开上面步骤之后,设置PDI的层级为方向状态堆栈上尾部条目的嵌入层级。

 

注意赋予一个隔断引发码的层级总是相同于赋予给它相匹配的PDI的层级。

 

终止嵌入和重写

 

一个PDF终止他所匹配的嵌入引发码的范围。如果没有相匹配的任何嵌入引发码,忽略它。

 

X7. 对于每个PDF,执行下面步骤:

  • 如果溢出隔断计数大于零,不做任何事。(这个PDF是在一个溢出隔断引发码范围之内。它可能匹配并终止一个在溢出隔断范围内的溢出嵌入引发码的范围,或者不匹配任何嵌入引发码。)
  •  否则,如果溢出嵌入计数大于零,此计数要减一。(这个PDF匹配并终止一个溢出嵌入引发码的范围,此溢出嵌入引发码不在溢出隔断引发码的范围之内。)
  •  否则,如果在方向状态堆栈上的尾部条目的方向隔断状态是false,并且方向状态堆栈至少包含两个条目,那么,从方向状态堆栈中弹出尾部条目。(这个PDF匹配并终止一个有效嵌入引发码的范围。由于堆栈至少有两个条目,弹出操作不会留下空堆栈。)
  •   否者,不做任何事。(这个PDF不匹配任何嵌入引发码。)

 

段落的结尾

 

X8. 所有的显式方向嵌入,重写和隔断在每个段落的结尾处完全终止。段落分割符不被包含在任何的嵌入,重写和隔断中,它用来赋予段落的嵌入层级。

3.3.3隐式处理的准备工作

显式嵌入层级通过前面的规则已经被赋予给了这些字符,很快将在字符的隐式双向类型基础上,进一步调整这些嵌入层级。这种调整使得一个给定的字符将取决于它周围字符的。但是,这种依赖关系是有限制的,通过逻辑划分段落为子单元,并且在每个子单元上做后续的隐式处理。

 

X9. 移除所有的RLE,LRE,RLO,LRO,PDF,和BN字符。

  • 注意一个实现实际上不必移除这些字符,它只需要不让这些字符出现在剩余的算法中。这些字符一致性不需要任何特定的位置,只要其它字符正确排序就行了。

见第5节,实现注意,关于实现该算法而不必移除格式码的信息。

 

  • 零宽连接码和非连接码会影响到相邻字符的整形 – 那些字符在原有的后备存储顺序中是相邻的,即使通过双向算法那些字符可能最终会被重新安排为非相邻的。相关更多信息,见6.1节,连接码。
  •  需要注意的是FSI,LRI,RLI,和PDI字符不会被移除。有如下规则显示,它们被用来,在某种程度上,以确定段落的隔断片序列,在其中它们被视为中性字符。但是,它们当然也是零宽字符,并且,相LRM和RLM,在最后的输出中,都不应该可见。

 

X10. 执行下面的步骤:

  •  通过BD13计算指定的隔断序列集,这个序列集基于字符的双向类型和通过上面的规则(X1--X9)分配的嵌入层级。
  • 为每个隔断片序列,确定序列的开始(sos)和序列的结束(eos)类型,无论是L或R,这些依赖于在序列边界边上的两个层级中的较高的一方:
  •   对于sos,比较这个序列中的首字符的层级与段落中此字符的前面字符的层级(不包括通过X9移除的字符),如果前面没有字符,那么使用段落嵌入层级进行比较。
  •   对于eos,比较这个序列的结尾字符的层级与段落中此字符的跟随字符的层级(不包括通过X9移除的字符),如果后面没有字符或者这个序列的结尾字符是一个隔断引发码(缺乏一个匹配的PDI),那么使用段落的嵌入层级进行比较。
  •  如果拥有值较高的层级是奇数,sos或eos是R;否则,它是L。
  •   注意,这些计算必须使用上面的规则已分配的嵌入层级,也必须在下面的步骤做出任何改变并被使用于它们之前计算。
  • 应用规则W1—W7,N0—N2,和I1—I2,在下面它们出现的顺序上,为每个隔断片序列,在这个序列中,在应用另一个规则到这个序列的任何部分之前,按这些字符在序列中出现的顺序,应用一个规则到所有这些字符。处理一个隔断序列片的顺序相对于另一个是不相关的。当应用一个规则到一个隔断序列片时,这个隔断序列片中的每个层级片的结尾字符是被视为紧随序列中下个层级片的首字符的,如果存在的话。

 

这里有一些例子,其中每个被假定为是基于层级0的一个段落,其中的文本i序列不包含任何显式方向格式码或者段落分隔符。在例子中点被用来分割元素让视觉度更清晰;它们不是文本的一部分。

例子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解析弱类型

弱类型现在一次解决一个隔断片序列。在隔断片序列边界上需要边界另一边的字符类型,这个类型分配给sos或eos使用。

首先,每个非空格标记的解析都是基于它跟随的字符。

W1. 检查在隔断片序列中的每个非空格标记(NSM),如果前面的字符是一个隔断引发码或PDI,那么改变NSM的类行为其它中性字符(other Neutral),否则改变为前面字符相同的类型如果NSM在隔断片序列的起始处,它将获取sos的类型。()

假设在这个例子中sos是R:

AL  NSM NSM → AL AL  AL

sos NSM     → sosR

LRI NSM     → LRI ON

PDI NSM     → PDI ON

文本下一个解析的是数字。这个传递过程将改变西欧数字分割符,西欧数字终止符,和通用数字分割符的方向类型为西欧数字文本,阿拉伯数字文本,或其它中性文本。被扫描的文本可能已经由一个方向重写类型改变。如果是这样,那么它不再解析数字。

W2. 从每个西欧数字的实例向后搜寻直到首个强类型(R,L,AL,或sos)被发现。如果发现的是AL,改变这个西欧数字类型为阿拉伯数字。

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

W3. 改变所有的ALs为R。

W4. 一个单个的西欧分割符(Europeanseparator)在两个西欧数字之间时,将改变为一个西欧数字。一个单个的通用分隔符(common separator)在两个相同类型的数字之间时,将变成和它们相同的类型。

EN ES EN → EN EN EN

EN CS EN → EN EN EN

AN CS AN → AN AN AN

W5. 西欧终止符(Europeanterminators)序列毗邻西欧数字时将全部变为西欧数字类型。

ET ET EN → EN EN EN

EN ET ET → EN EN EN

AN ET EN → AN EN EN

W6. 否则,分隔符和终止符改变为其它中性字符(Other Neutral)。

AN ET    → AN ON

L  ES EN →L  ON EN

EN CS AN → EN ON AN

ET AN    → ON AN

W7. 从每个西欧数字的实例向后搜寻直到首个强类型(R,L,AL,或sos)被发现。如果发现的是L,改变这个西欧数字类型为L。

L  NI EN →L  NI L

R  NI EN →R  NI EN

3.3.5解析中性和隔断码类型

在下一阶段,中性字符和隔断格式码(即NI)一次解析一个隔断片序列。其结果是所有NIs变成RL。一般来说,NIs采用周围文本的方向。在有冲突的情况下,它们采用嵌入方向。在隔断片序列边界上需要边界另一边的字符类型,这个类型分配给sos或eos使用。

 

在隔断片序列中括号对被作为一个单元来处理,因此在同一个配对中的打开中的和关闭中的配对括号被解析为同样的方向。

N0. 在一个隔断片序列中依次以打开中的配对的括号的文本位置的逻辑顺序来处理括号配对。在此范围内,双向类型EN和AN被视为R 。

l  根据BD16识别当前隔断片序列中的括号对。

l  在存储文本位置的配对列表中为每个括号对元素

a)        在闭合的括号对范围内检查字符的双向类型。

b)        如果任何和这嵌入方向一致的强类型(L或R)被找到,在此括号对中设置两个括号的方向类型和这嵌入方向一致。

o [ e ] o → o ee e o

o [ o e ] → o eo e e

o [ NI e ] → o eNI e e

c)        否则,如果含有强类型都与嵌入方向相反。因此,通过在打开中的配对括号处向后检查直到首个强类型(L,R,或sos)被发现,来测试一个上下文前面的强类型。

1.        如果前面的强类型方向也是和嵌入方向相反,因此设置两个配对的括号的方向类型为这个强类型的方向。

o [ o ] e → o o o o e
o [ o NI ] o → o o o NI o o

2.        否则,设置两个配对的括号的方向类型为嵌入方向。

e [ o ] o → e e oe o

e [ o ] e → e e oe e

d)        否则,在这个括号对中没有强类型,因此,不设置这括号对的类型。

e ( NI ) o → e ( NI) o

注意,如果闭合文本没有包含强类型,当使单独使用规则N1和N2解析时,括号对将解析为相同的层级。

例子1:括号对被依次以打开中的配对的括号的逻辑顺序解析。

(RTL 段落方向)

存储

AB

(

CD

[

&

ef

]

!

)

gh

双向类型

R

ON

R

ON

ON

L

ON

ON

ON

L

N0规则被应用 (第一对)

 

N0b: ON→R

      

N0b: ON→R

 

N0规则被应用  (第二对)

   

N0c2: ON→R

  

N0c2: ON→R

   

显示

gh(![ef&]DC)BA

 

例子2:括号对闭合范围内是混合的强类型将采用段落方向。

 

(RTL 段落方向)

存储

smith

 

(

fabrikam

 

ARABIC

)

 

HEBREW

双向类型

L

WS

ON

L

WS

R

ON

WS

R

N0规则被应用

  

N0b: ON→R

   

N0b: ON→R

  

显示

WERBEH (CIBARA fabrikam) smith

值得注意的是就算颠倒smith和HEBREW,或fabrikam和ARABIC的顺序,该括号对的解析也是稳定的。

例子3: 括号对的闭合范围内强类型方向和嵌入方向相反。采用前文已建立的强类型方向。

(RTL 段落方向)

存储

ARABIC

 

book

(

s

)

双向类型

R

WS

L

ON

L

ON

N0规则被应用

   

N0c1: ON→L

 

N0c1: ON→L

显示

book(s) CIBARA

N1. 一个NIs序列采用周围强文本的方向,如果NIs两边的文本双方都具有相同的方向。西欧和阿拉伯数字在对NIs的影响上实际相当于R。序列开始(sos)和序列结束(eos)的类型被使用在隔断片序列的边界处。

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

 

N2. 任何剩余的NIs采用嵌入方向。

 

NI → e

 

给定的NI字符的嵌入方向来自它的嵌入层级:如果字符设置为一个偶数层级,则为L,如果层级为奇数,则为R。

 

假设下面的例子中eos是L,sos是R。则N1和N2的应用如下:

 

L   NI eos→ L   L eos

R   NI eos→ R   e eos

sos NI L   → sos e L

sos NI R   → sos R R

 

范例. 通过一个中性并且嵌入在一个方向片中的分隔符列表将会在这个方向片的顺序中出来。

 

存储:       hesaid "THE VALUES ARE 123, 456, 789, OK".

 

显示:       hesaid "KO ,789 ,456 ,123 ERA SEULAV EHT".

 

在此情况下,在数字包围间的逗号和空格将采用周围文本的方向(大写== right-to-left),而忽略其周围数字的影响。逗号不考虑数字的部分,是因为它们的两侧不是由数字完全包围的(见3.3.4节,解析弱类型)。但是,如果前面有一个left-to-right的序列,那么西欧数字将采用该方向:

 

存储:       ITIS A bmw 500, OK.

 

显示:       .KO,bmw 500 A SI TI

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 总结了隐式算法的结果

                                                                                                                                     Table 5. 解析隐式层级

类型

嵌入层级

偶数

奇数

L

EL

EL+1

R

EL+1

EL

AN

EL+2

EL+1

EN

EL+2

EL+1

 

3.4 重排已解析的层级

下面的规则描述找到正确的显示顺序的逻辑过程。而不是解析阶段,这些规则作用基于每行和应用任何一行自动换行段落之后。

 

逻辑上有以下步骤:

 

l  文本的层级根据前面的规则被确定。

l  根据上下文字符被整形成字形。

l  这些字形的累积宽度(按逻辑顺序)用于确定断行。

l  对于每行,规则L1—L4被用于重排此行上的字符。

l  对应行上的字符的字形被显示在该顺序上。

 

L1. 在每行上,重设下面字符的嵌入层级为段落嵌入层级:

 

1.   段分隔符,

2.   段落分隔符,

3.   在一个段分隔符或段落分割符前面的任何空格字符序列和/或隔断格式码(FSI,LRI,RLI,和PDI),和

4.   在行结尾处的任何空格字符序列和/或隔断格式码(FSI,LRI,RLI,和PDI)。

 

l使用在这里的字符类型是原始字符类型,而不是通过前面阶段那些修改过后的类型。

l因为一个段落分割符用来断行,在行的结尾处,每行至多将会只存在一个。

 

结合下面的规则,这意味着结尾空格将出现在行的视觉结尾处(以段落的方向)。在一个段落中,表格将总是有一个一致性的方向。

 

L2. 从的文本中每一行上找到最高的层级到最低的奇数层级,包括中间实际不存在于文本中的层级,反向这个层级或更高层级任何相邻的字符序列。

 

这条规则逐步增大反向一系列子字符串。

 

下面的例子说明了重排,示出了应用规则L2的连续步骤。原始文本显示在示例表中的“存储”行中。不显示的,零宽格式码LRI,RLI,和PDI分别用符号>,<,和 =, 代表。从3.3节,解析嵌入层级到规则L1的应用的结果在解析的层级列表中。被显示在“解析的层级”一行中。(由于示例中只利用了隔断格式码,所以应用规则X9不会移除任何字符。注意例子3如果使用反向嵌入层级将不会工作因为这两个right-to-left短句会一同和在他们之间的中性字符合并成一个单一的right-to-left片。)每个连续行显示了通过规则L2的反向结果,如“反向层级1-2”。在每个迭代中,有下划线的文本突显出已经被反向。

 

对于第一,第二,和第三个例子,段落嵌入层级是0(left-to-right方向),对于第四个例子,层级是1(right-to-left方向)。

例 1 (段落嵌入层级 = 0)

存储:

car means CAR.

解析的层级:

00000000001110

反向层级1:

car means RAC.

显示:

car means RAC.

 

例2 (段落嵌入层级= 0)

存储:

<car MEANS CAR.=

解析的层级:

0222111111111110

反向层级2:

<rac MEANS CAR.=

反向层级1-2:

<.RAC SNAEM car=

显示:

.RAC SNAEM car

 

例3 (段落嵌入层级= 0)

存储:

he said “<car MEANS CAR=.” “<IT DOES=,” she agreed.

解析的层级:

000000000022211111111110000001111111000000000000000

反向层级2:

he said “<rac MEANS CAR=.” “<IT DOES=,” she agreed.

反向层级1-2:

he said “<RAC SNAEM car=.” “<SEOD TI=,” she agreed.

显示:

he said “RAC SNAEM car.” “SEOD TI,” she agreed.

 

例4 (段落嵌入层级= 1)

存储:

DID YOU SAY ’>he said “<car MEANS CAR=”=‘?

解析的层级:

111111111111112222222222444333333333322111

反向层级4:

DID YOU SAY ’>he said “<rac MEANS CAR=”=‘?

反向层级3-4:

DID YOU SAY ’>he said “<RAC SNAEM car=”=‘?

反向层级2-4:

DID YOU SAY ’>”=rac MEANS CAR<“ dias eh=‘?

反向层级1-4:

?‘=he said “<RAC SNAEM car=”>’ YAS UOY DID

显示:

?‘he said “RAC SNAEM car”’ YAS UOY DID

 

L3.Combining marks applied to a right-to-left base character will at this pointprecede their base character. If the rendering engine expects them to followthe base characters in the final display process, then the ordering of themarks and the base character must be reversed.

Many font designers provide default metrics for combiningmarks that support rendering by simple overhang. Because of the reordering forright-to-left characters, it is common practice to make the glyphs for mostcombining characters overhang to the left (thus assuming the characters will beapplied to left-to-right base characters) and make the glyphs for combiningcharacters in right-to-left scripts overhang to the right (thus assuming thatthe characters will be applied to right-to-left base characters). With suchfonts, the display ordering of the marks and base glyphs may need to beadjusted when combining marks are applied to “unmatching” base characters. SeeSection5.13, Rendering Nonspacing Marks of [Unicode],for more information.

L4. Acharacter is depicted by a mirrored glyph if and only if (a) the resolveddirectionality of that character is R, and (b) the Bidi_Mirrored property valueof that character is Yes.

  • The Bidi_Mirrored property is defined by Section 4.7, Bidi Mirrored of [Unicode]; the property values are specified in [UCD].
  • This rule can be overridden in certain cases; see HL6.

For example, U+0028 left parenthesis—whichis interpreted in the Unicode Standard as an opening parenthesis—appears as “(”when its resolved level is even, and as the mirrored glyph “)” when itsresolved level is odd. Note that for backward compatibility the charactersU+FD3E (﴾) ORNATE LEFT PARENTHESIS and U+FD3F (﴿) ORNATE RIGHT PARENTHESIS are not mirrored.

3.5整形

连写的脚本,如阿拉伯语和叙利亚语,需要依赖相邻字符位置的字符形状的选择。(见8.2节,[unicode]的阿拉伯语)。整形被应用在双向算法被使用之后,并且限制这些字符在相同的层级片中。(注意限制整形在一个层级片中,还是在隔断片序列中实际上并没有什么不同,因为隔断引发码和PDI是被定义为连接类型为U,即非连接的。因此,在字符之前和之后有一个方向隔断,字符将不能穿过这个隔断进行连接,即使隔断为空或者溢出深度限制也不能穿过。)考虑下面阿拉伯字符组成的字符串的例子,该字符串在内存中表示为字符1,2,3和4,并且第一,二两个字符被覆盖为LTR。若要显示两个段落方向,后两个字符被嵌入,但有正常的RTL方向。

 

1

2

3

4

ج

062C
JEEM

ع

0639
AIN

ل

0644
LAM

م

0645
MEEM

L

L

R

R

 

一个可以使用显式方向格式码在纯文本中来达到这种效果或者在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

Right-Left Paragraph

1

2

4

3



JEEM-F



AIN-I



MEEM-F



LAM-I

4

3

1

2



MEEM-F



LAM-I



JEEM-F



AIN-I

3.5.1整形和换行

分断一个段落到一个或更多的行并适合在特定的范围内的处理超出了双向算法的处理范围。凡涉及到字符整形的,宽度的计算必须基于已整形的字形。

 

注意,soft-hyphen(SHY)工作在连写的脚本中。也就是说,在一个字的中间,它指示一个点,指明行可在哪里分断。如果渲染系统在此点分断,显示—包括整形—对于给定的语言应该什么是合适的。就这个问题和其它行分断问题的详细信息,见Unicode标准附件#14,“行分断属性”[UAX14]。

 

4相一致的双向性

一个声称与本规范相一致的处理应满足以下条文:

 

UAX9-C1.

在缺乏一个可用的更高级别协议中,这个处理是这样,通过这个附件的第3节,基本显示算法,以确定的顺序渲染文本并显示所有能够显示的字符的表示字形(排除格式码)。特别是,这包括了定义BD1-BD16和步骤P1-P3,W1-W7,N0-N2,I1-I2,L1-L4。

 

l  和所有其它Unicode算法的情况一样,这是一个逻辑描述--特定的实现可以有更有效的机制,只要它们产生了相同的结果。见[Unicode]的一致性,章节3的C18,和后面的备注。

l  双向算法指定right-to-left的字符的内在语意的一部分,因此需要任何此类字符的显示位置与Unicode标准的具有一致性。

 

UAX9-C2.

仅被允许的更高级别的协议被列出在第4.3节,更高级别的协议。它们是HL1,HL2,HL3,HL4,HL5,和HL6。

 

不鼓励使用更高级别的协议,因为它引入了交换问题并可能导致安全问题。有关详细信息,请参阅Unicode技术报告#36,“Unicode安全注意事项”[UTR36]。

 

4.1边界中性

标记一个格式码或控制字符作为BN目标是它对算法的余下部分没有影响。(ZWJ和ZWNJ是例外,见X9)。因为一致性不需要精确的格式码顺序在其他方面,实现可以以不同的方式处理他们,只要它们能保持其它字符的顺序。

 

4.2显式格式码

对于任何Unicode字符,系统不会支持任何特殊的显式方向格式码(虽然它通常没有用,包含一个终止字符而没有包含引发码)。通常,相一致的系统将分为四大类:

 

l  没有双向格式码。这意味着系统不会在视觉上解释从右到左的脚本。

l  隐式双向性。隐式双向算法,隐式方向标记ALM,RLM和LRM被支持。

l  非隔断双向性。隐式方向算法,隐式方向标记,和显式非隔断方向码被支持:ALM ,RLM,LRM,LRE,RLE,LRO,RLO,PDF。

l  全双向性。隐式双向算法,隐式方向标记,和显式方向格式码被支持:ALM ,RLM,LRM,LRE,RLE,LRO,RLO,PDF,FSI,LRI,RLI,PDI。

4.3更高级别的协议

下面的条款在对系统应用更高级别的协议到双向文本的顺序上时是唯一被允许的方式。有些条款适用于结构化文本段。这是指文本被解释为结构化的,是否有显式的标记如XML或HTML,或者内部结构如在一个文字处理器或电子表格中。在这种情况下,一个段是结构由某种方式区分出来的文本跨度。

 

HL1. 覆盖P3,并明确地设置段落嵌入层级。此条并不适用于在规则X5c中决定如何对待FSI。

 

l  一个更高级别的协议可以设置任何段落层级。这可以依据上下文来这么做,如在一个表格单元,段落,文档,或系统级别上。(如果P3被覆盖,P2可以被跳过)。注意,这并不允许一个更高级别的协议覆盖BD2中限止的指定。

l  一个更高级别的协议可以等效于P2和P3,但默认层级1(RTL)而不是0(LTR)来匹配上下文的整个RTL。

l  一个更高级别的协议可以使用一个完全不同的算法,基于段落文本和它的上下文,试探性地自动检测段落嵌入层级。例如,它可以基于在一个文本中是否有比LTR更多的RTL字符。另一个例子,当该段不包含强字符是,它的方向可以有这个段落的前面和后面的层级来确定。

 

HL2. 覆盖W2,并明确地设置EN或AN。

 

l  一个更高级别的协议可以重设类型为EN的字符到AN类型,反之亦然,并且忽略W2。例如,可以使用样式表或标记信息在一个文本片中覆盖EN文本的设置变成总是AN类型,反之亦然。

 

H3. 模拟显式方向格式码。

 

l  一个更高级别的协议可以在一个结构化文本段上模拟利用为一个方向嵌入,隔断或覆盖。该行为总是被定义为等效于算法中显式方向格式码而被插入到文本。例如,一个样式表或标记可以修改文本片上的嵌入层级。

 

HL4. 应用双向算法到段。

 

l  双向算法可以单独应用于一个或多个结构化文本的段中。例如,当在一个编辑器中,显示一个包含了文本数据和可见的标记文档时,一个更高级别的处理能够从文本数据中分开处理那些在标记中的语法元素。

 

HL5. 提供人为的上下文环境。

 

 

HL6. 额外的镜像。

 

 

 

条款HL1和HL3是更为通用的条款HL4和HL5的专门应用。它们在这里提供明确,是因为它们直接对应普通操作。

 

一个HL4应用的例子,假设一个XML文档包含下面的片段。(注意,这是一个简单的例子用于说明目的:元素名称,属性名称,属性值都可以。)

 

ARABICenglishARABIC<e1 type='ab'>ARABICenglish<e2type='cd'>english

 

这能解析为五个不同的段:

  1. ARABICenglishARABIC
  2. <e1 type='ab'>
  3. ARABICenglish
  4. <e2 type='cd'>
  5. english

使得XML可读而作为源文本,在一个统一的方向上,在编辑器中显示能够排序所有这些元素并且分别应用双向算法到每个字段。也可以选择性地排序元素名称,属性名称,和属性值统一在相同的方向,对于最后的显示,标记会被忽略,让所有的文本(段  a,c和e)重新排序在一起。


5 实现注意

5.1参考代码


5.2保留显式格式码

有些实现在运行这个算法时可能会希望保留显式方向嵌入和覆盖格式码。下面提供一个如何这样做的总结。注意,这个总结是一个详实的实现指南。它应该为上面的显式算法提供相同的结果。但在任何情况下,偏差的显式算法也属于规范声明的一致性范畴。

 

l  在规则X9中,不要删除嵌入和覆盖格式码,而是为每个格式码分配嵌入层级,并将它的类型变成BN。

l  在规则X10中,决定一个隔断片序列的sos和eos时,当向前寻找这个隔断片序列的首字符和向后寻找结尾字符时,跳过任何BNs。

l  在规则W1中,从每个NSM向后搜索直到双向类型不是BN的首个字符,并判断如果是一个隔断引发码或PDI,设置这个NSM为ON,否则NSM设置为这个字符相同的类型。如果NSM为第一个字符并且不是BN类型,改变这个NSM为SOS相同的类型。

l  在规则W4中,扫描过和ES或CS相邻的BN类型。

l  在规则W5中,改变所有ET和BN的适当序列,不仅仅是ET。

l  在规则W6中,改变所有ET,ES,或CS以及毗邻的BN类型为ON。

l  在规则W7中,扫描过BN。

l  在规则N0-N2中,视相邻于中性字符的BNs为同样的中性。

l  在规则I1和I2中,忽略BN 。



评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值