阿拉伯文排版处理BiDi

一、阿拉伯文介绍

阿拉伯语属阿非罗-亚细亚语系(闪含语系)闪语族。词一般由3个原生字母构成,可添加前缀,后缀,或使词的内部音位发生变化和插入其它音位构成新词。

阿拉伯文字在伊斯兰教的影响下流传很广。波斯文,乌尔都文以及我国的新疆的维吾尔文等多种文字采用阿拉伯字母。阿拉伯文的字母没有大写和小写的区分,但有印刷体和书写体的区别,而且除去د ذ ر ز و五个字母以外,其他23个字母都可以和后面的字母连写,而且因其在词头,词中和词尾的位置不同,字形也有所变化。阿拉伯文字的书写方向和中文不同,它是自右向左横着写。因此,阿拉伯文的书籍和本子都是右开本的。

阿拉伯字符对应Unicode:U+0600 – U+06FF  

增补阿拉伯字符对应Unicode:U+0750 – U+077F

阿拉伯表达形式字符集A对应Unicode:U+FB50 – U+FDFF。

阿拉伯表达形式字符集B对应Unicode:U+FE70– U+FEFF。

    在换行的时候,一般的,我们需要判断一下是否是整个词,进行整个词的换行,而不能把词拆成两个部分。阿拉伯文里面的数字仍然采用从左往右的显示方式。

二、阿拉伯文变形规则

规则1

根据上面分法,分成first,last,middle,alone,并根据判断是否是词的前连(前面字符在集合1中),后连(后面字符在集合2中),中间(即前后连,前面字符在集合1中,后面字符在集合2中)或单独一个词,进行变形。

据分析,给出变形的数组:分别对应上面的情况。对于其它不在数组中的,其变形和自身相同。

// first,   last,   middle, alone

{ 0xfe80, 0xfe80, 0xfe80, 0xfe80},    // 0x621

{ 0xfe82, 0xfe81, 0xfe82, 0xfe81}, // 0x622

{ 0xfe84, 0xfe83, 0xfe84, 0xfe83}, // 0x623

{ 0xfe86, 0xfe85, 0xfe86, 0xfe85}, // 0x624

{ 0xfe88, 0xfe87, 0xfe88, 0xfe87}, // 0x625

{ 0xfe8a, 0xfe8b, 0xfe8c, 0xfe89}, // 0x626

{ 0xfe8e, 0xfe8d, 0xfe8e, 0xfe8d}, // 0x627

{ 0xfe90, 0xfe91, 0xfe92, 0xfe8f},    // 0x628

{ 0xfe94, 0xfe93, 0xfe93, 0xfe93}, // 0x629

{ 0xfe96, 0xfe97, 0xfe98, 0xfe95},    // 0x62A

{ 0xfe9a, 0xfe9b, 0xfe9c, 0xfe99}, // 0x62B

{ 0xfe9e, 0xfe9f, 0xfea0, 0xfe9d}, // 0x62C

{ 0xfea2, 0xfea3, 0xfea4, 0xfea1},   // 0x62D

{ 0xfea6, 0xfea7, 0xfea8, 0xfea5}, // 0x62E

{ 0xfeaa, 0xfea9, 0xfeaa, 0xfea9}, // 0x62F

{ 0xfeac, 0xfeab, 0xfeac, 0xfeab},    // 0x630  

{ 0xfeae, 0xfead, 0xfeae, 0xfead}, // 0x631

{ 0xfeb0, 0xfeaf, 0xfeb0, 0xfeaf}, // 0x632

{ 0xfeb2, 0xfeb3, 0xfeb4, 0xfeb1}, // 0x633

{ 0xfeb6, 0xfeb7, 0xfeb8, 0xfeb5}, // 0x634

{ 0xfeba, 0xfebb, 0xfebc, 0xfeb9}, // 0x635

{ 0xfebe, 0xfebf, 0xfec0, 0xfebd}, // 0x636

{ 0xfec2, 0xfec3, 0xfec4, 0xfec1}, // 0x637

{ 0xfec6, 0xfec7, 0xfec8, 0xfec5},    // 0x638

{ 0xfeca, 0xfecb, 0xfecc, 0xfec9}, // 0x639

{ 0xfece, 0xfecf, 0xfed0, 0xfecd},     //0x63A

{ 0x63b, 0x63b, 0x63b, 0x63b},

{ 0x63c, 0x63c, 0x63c, 0x63c},

{ 0x63d, 0x63d, 0x63d, 0x63d},

{ 0x63e, 0x63e, 0x63e, 0x63e},

{ 0x63f, 0x63f, 0x63f, 0x63f},

{ 0x640, 0x640, 0x640, 0x640},  

{ 0xfed2, 0xfed3, 0xfed4, 0xfed1}, // 0x641

{ 0xfed6, 0xfed7, 0xfed8, 0xfed5}, // 0x642

{ 0xfeda, 0xfedb, 0xfedc, 0xfed9}, // 0x643

{ 0xfede, 0xfedf, 0xfee0, 0xfedd}, // 0x644

{ 0xfee2, 0xfee3, 0xfee4, 0xfee1}, // 0x645

{ 0xfee6, 0xfee7, 0xfee8, 0xfee5}, // 0x646

{ 0xfeea, 0xfeeb, 0xfeec, 0xfee9}, // 0x647

{ 0xfeee, 0xfeed, 0xfeee, 0xfeed},    // 0x648

{ 0xfef0, 0xfeef, 0xfef0, 0xfeef}, // 0x649

{0xfef2, 0xfef3, 0xfef4, 0xfef1},     // 0x64A

判断是否是连接前面的,采用判断该字符前一个字符的判定方法,方法是,看前一个字符是否在集合set1中。如果在,则是有连接前面的。集合1如下:

    static U16 theSet1[23]={

        0x62c, 0x62d, 0x62e, 0x647, 0x639, 0x63a, 0x641, 0x642,

        0x62b, 0x635, 0x636, 0x637, 0x643, 0x645, 0x646, 0x62a,

        0x644, 0x628, 0x64a, 0x633, 0x634, 0x638, 0x626};

判断是否是连接后面的,采用判断该字符后一个字符的判定方法,方法是,看后一个字符是否在集合set2中。如果在,则是有连接后面的。集合2如下:

    static U16 theSet2[35]={

        0x62c, 0x62d, 0x62e, 0x647, 0x639, 0x63a, 0x641, 0x642,

        0x62b, 0x635, 0x636, 0x637, 0x643, 0x645, 0x646, 0x62a,

        0x644, 0x628, 0x64a, 0x633, 0x634, 0x638, 0x626,

        0x627, 0x623, 0x625, 0x622, 0x62f, 0x630, 0x631, 0x632,

        0x648, 0x624, 0x629, 0x649};

规则2

阿拉伯文连字符规则:

连字符是以0x644开头,后面跟的是0x622,0x623,0x625,0x627,并根据情况取下面的字符数组0或1,如果0x644前一个字符是在集合1(同上面的集合1)中间,那么取数组1,否则取数组0。

数组如下:

static U16 arabic_specs[][2]={

{0xFEF5,0xFEF6},

{0xFEF7,0xFEF8},

{0xFEF9,0xFEFA},

{0xFEFB,0xFEFC}};

例1: 0x064A, 0x0644, 0x0622

0x064A 的后面一个字符 0x0644 在集合2中,根据编码规则1得出它是后连字(last),故转换成: 0xFEF3.  

      而0x064A在集合1 中,故用 0xFEF6 替代 0x0644 0x0622 这两个编码。     

       

例2: 0x0632, 0x0644, 0x0622

      0x0632 的后面一个字符 0x0644 在集合2中,根据编码规则1得出它是后连字符(last), 故转换成: 0xFEAF.  

      而 0x0632 不在集合1 中,故用 0xFEF5 替代 0x0644 0x0622 这两个编码。

  

三、ICU4C中的阿拉伯文变形规则接口函数

在ICU4C中阿拉伯文的变形规则中,提供给外部调用的接口函数只有一个:

int32_t  u_shapeArabic(const UChar *source, int32_t sourceLength,

                     UChar *dest, int32_t destCapacity,

                     uint32_t options,

                     UErrorCode *pErrorCode)

u_shapeArabic功能:基于Unicode的阿拉伯字符的变形函数。将一连串的阿拉伯字符转化成为要显示的字符形状,或者将显示状态的阿拉伯字符转化为原本的状态。

参数:  source: 输入的Unicode源文本

        sourceLength:输入的Unicode源文本(source)的字符个数

dest:  目标缓冲区,接受转化后的Unicode字符,仅当destSize是0,它可以是NULL。源source和目标dest必须不重叠。

destSize:目标缓冲区的大小,如果destSize为0,则没有输出产生。

options:选项​​,这是一个32位的组标志,用于指定输入的文本上执行操作。如果没有出现错误,那么结果将写入目标缓冲区。

U_SHAPE_LETTERS_SHAPE :表示将正常的阿拉伯字符转化为显示字符。

 U_SHAPE_LETTERS_UNSHAPE:与U_SHAPE_LETTERS_SHAPE相反。

      

pErrorCode:必须是一个有效的指针错误代码值,如果函数调用失败pErrorCode的内容将被写入代表错误类型的常量值。

返回值
    写入目标缓冲区的数目。如果发生错误,则没有输出被写入,或者它可能是不完整的(例如函数调用后,pErrorCode的值被设置为U_BUFFER_OVERFLOW_ERROR了!)

函数调用举例

UErrorCode errorCode=U_ZERO_ERROR;

//source[256]="اختبار النص العربي";

static const UChar source[] = {0x0627, 0x062E, 0x062A, 0x0628, 0x0627, 0x0631,

 0x0020,//空格键

 0x0627, 0x0644, 0x0646, 0x0635,

 0x0020,//空格键

             0x0627, 0x0644, 0x0639, 0x0631, 0x0628, 0x064A};

UChar dest[256];

int32_t length;

length=u_shapeArabic(source, LENGTHOF(source),dest, LENGTHOF(dest),  U_SHAPE_LETTERS_SHAPE , &errorCode);

if(U_FAILURE(errorCode) || length!=LENGTHOF(source))

{

printf("Error: failure in u_shapeArabic\n");

}

 转化之后:

目标缓冲区的值dest = {0xFE8D,0xFEA7,0xFE98, 0xFE92, 0xFE8E, 0xFEAD, 0xFE8D,   0x0020, 0xFEDF,0xFEE8,0xFEBC, 0xFE8E, 0x0020, 0xFEDF,

0xFECC, 0xFEAE, 0xFE91, 0xFEF2, 0x0000,。。。};

四、BiDi算法介绍

1、名词的介绍  

 

逻辑顺序:指的是人们阅读和从键盘上输入的文字顺序,文本在内存里也是以逻辑顺序存储的

视觉顺序:则是文本在屏幕或是打印机中显示的顺序。

Implicit Bidi ControlsBidi隐式控制字符)

LRM U+200E    LEFT-TO-RIGHT MARK Left-to-right zero-width character

RLM U+200F    RIGHT-TO-LEFT MARK Right-to-left zero-width character

它们的方向和强字符的方向是完全一样的,唯一的区别是,它们是0宽度的字符,不会显示在显示屏上!

Explicit Bidi Controls(Bidi显式控制字符)

LRE   U+202A   LEFT-TO-RIGHT EMBEDDING

Treat the following text as embedded left-to-right.

RLE   U+202B  RIGHT-TO-LEFT EMBEDDING

Treat the following text as embedded right-to-left.

上面的编码标志着一串文本被当作嵌入的文本。比如,一个英文的引号出现在一个阿拉伯语句子的中间可能被标记为被嵌入的LTR文本。如果有一个希伯莱语的短语出现在英语引号的中间,则那个短语可能被标记为被嵌入的RTL.这些编码允许嵌套的嵌入。

例如,可以通过嵌入文本RLE ... PDF,中间的文本被认为是嵌入的从RLE的文本。.

LRO U+202D  LEFT-TO-RIGHT OVERRIDE 

Force following characters to be treated as strong left-to-right characters.

RLO U+202E  RIGHT-TO-LEFT OVERRIDE

Force following characters to be treated as strong right-to-left characters.

RTL覆写,比如,可被用于强制使一个组成为混合的英文,数字和希伯来字母按照自右向左的顺序来书写。

PDF   U+202C  POP DIRECTIONAL FORMATTING

Restore the bidirectional state to what it was before the last LRE, RLE, RLO, or LRO

上面的代码终止的最后一个明确的代码(嵌入或覆盖)的影响,并恢复它以前的代码中遇到的双向状态。

Strong character (强字符): 所谓强字符表示排版的系统知道这个字符是什么方向的字符,任何一个阿拉伯的字符和希伯来的字符都可以认为是强字符,汉字和英文字母也是强字符

Weak characters (弱字符):即排版系统不知道这个字符属于RTL还是LTR的字符,这种字符的排版取决于很多的因素。比如数字 - 1,2,3...。

Neutral characters (中性字符):比如标点符号 - 括号,逗号,句号...;空格等。

排版最后要区分出来的是每个字符的方向,分成R和L两种。R表示向右,L表示想左。RTL强字符的方向是R,LTR强字符的方向是L。下面是字符的划分。

Bidirectional Character Types

Category

Type

Description

General Scope

Strong

L

Left-to-Right

LRM, most alphabetic, syllabic, Han ideographs, non-European or non-Arabic digits, ...

LRE

Left-to-Right Embedding

LRE

LRO

Left-to-Right Override

LRO

R

Right-to-Left

RLM, Hebrew alphabet, and related punctuation

AL

Right-to-Left Arabic

Arabic, Thaana, and Syriac alphabets, most punctuation specific to those scripts, ...

RLE

Right-to-Left Embedding

RLE

RLO

Right-to-Left Override

RLO

Weak

PDF

Pop Directional Format

PDF

EN

European Number

European digits, Eastern Arabic-Indic digits, ...

ES

European Number Separator

Plus sign, minus sign

ET

European Number Terminator

Degree sign, currency symbols, ...

AN

Arabic Number

Arabic-Indic digits, Arabic decimal and thousands separators, ...

CS

Common Number Separator

Colon, comma, full stop (period), No-break space, ...

NSM

Nonspacing Mark

Characters marked Mn (Nonspacing_Mark) and Me (Enclosing_Mark) in the Unicode Character Database

BN

Boundary Neutral

Default ignorables, non-characters, and control characters, other than those explicitly given other types.

Neutral

B

Paragraph Separator

Paragraph separator, appropriate Newline Functions, higher-level protocol paragraph determination

S

Segment Separator

Tab

WS

Whitespace

Space, figure space, line separator, form feed, General Punctuation spaces, ...

ON

Other Neutrals

All other characters, including OBJECT REPLACEMENT CHARACTER

Bidi_Class : Bidirectional characters types 的值分配给每一个Unicode字符包括未分配的字符。在 Unicode Character Database [UCD]中正式属性名字就是Bidi_Class

Embedding levels(嵌套级别): 表示文本的嵌套深度和和在当前level上的默认的文本方向。最小嵌入深度为0,最大嵌入深度为61。

Embedding direction嵌套方向):当前level上的默认的文本方向,embedding level是偶数表示方向是L,奇数表示方向是R。

例如:在一个特定文本,Level 0 表示是英文文本,Level 1表示嵌套在英文文本(Level 0)中的阿拉伯文本。Level 2也是英文文本,它是嵌套在阿拉伯文本(Level 1)中的,以此类推,除非方向被覆写,英文文本总是偶数,阿拉伯文本总是奇数!

The paragraph embedding level (段嵌入级别):决定段文本的默认的方向。

The paragraph direction(段方向): 段文本嵌套级别的方向。

Level run:  文本中,一个具有相同的嵌套级别最大字符串。

The directional override status定向覆写状态):定向覆写状态确定双向类型的字符是否是覆写重设。覆写状态通过explicit directional controls设置。此状态有三种状态,如中所示。

Table 2 Directional Override Status

Status

Interpretation

Neutral

No override is currently active

Right-to-left

Characters are to be reset to R

Left-to-right

Characters are to be reset to L

下面例子,大写字母代表从右到左(right-to-left)的字符,小写字母代表从左到右(left-to-right)

Memory:            car is THE CAR in arabic

Character types:   LLL-LL-RRR-RRR-LL-LLLLLL

Resolved levels:   000000011111110000000000

2、基本显示算法

Unicode Bidirectional Algorithm (UBA)接收一个文本流作为输入,并按如下四个主要阶段来处理:

  • 将文本分割为段落。 算法的其他部分分别被应用于各个段落中的文本。
  • 初始化。 一个方向字符类型的列表将会被初始化,原始文本中的每一个字符对应一个项。每一项的值为各个字符的Bidi_Class属性。此后,则直到重排序阶段,原始字符都不会再被引用。然后初始化一个嵌入层级的列表,每一个字符均有一个层级。
  • 套级别的解析。一系列的规则将被应用于嵌入层级的列表和方向字符类型的列表。每一个规则都基于那些列表的当前值,并可能改变那些值。
  • 重新排序。  在每个段落重新排序的文字显示。

)将文本分割成段落

   段划分的段落分隔符或适当的换行功能(回车换行,换行,文本分隔符)

)初始化

     一个方向字符类型的列表将会被初始化,每一项的值为各个字符的Bidi_Class属性。

(三)套级别的解析

该决议的过程包括五个步骤:(1)确定段落级别;(2)确定明确的嵌套级别和方向;(3)解决弱类型;(4)解决中性的类型,以及(5)解决隐式嵌套级别。

(1)确定段落级别

P1。文本拆分成单独的段落。

P2。在每个段落中,找到字符类型是L型,AL,或R.的第一个字符。

需要注意的是字符类型LRE,LRO,RLE,RLO被忽略,这条规则。这是因为它们通常被用于表明嵌入文本是该段的相反的方向上。

P3。在P2中如果一个字符被发是AL或R型,然后设置一个段嵌入级别为1,否则,将其设置为0。

(2)确定明确的嵌套级别和方向

Explicit Embeddings:

  X1。首先,设置当前嵌套级别的值为段嵌套级别的值。设置的定向覆盖状态为Neutral。迭代过程中的每个字符,应用规则X2- X9。只有嵌入级别从0到61是有效的。

 X2。每个RLE,计算最大的奇数嵌套级别。   

    a。如果这个新的级别将是有效的,则该嵌入的code是有效的。记住(push)的当前的嵌入级别和覆盖状态。设置当前的级别的值为新的级别,覆写状态重置到neutral

    b。如果新的级别将是无效的,那么code是无效的。不要改变目前的级别或覆写状态。

     例如,level 0 → 1; levels 1, 2 → 3; levels 3, 4 → 5。。。

X3。每个LRE,计算最大的偶数嵌套级别。

a。如果这个新的级别将是有效的,则该嵌入的代码是有效的。记住(push)的当前的嵌入级别和覆盖状态。设置当前的级别的值为新的级别,覆写状态重置到neutral

    b。如果新的水平将是无效的,那么这段代码是无效的。不要改变目前的级别或覆写状态。

     例如levels 0, 1 → 2; levels 2, 3 → 4; levels 4, 5 → 6。。。

Explicit Overrides:

  X4。每个RLO,计算最大的奇数嵌套级别。

a。如果这个新的级别将是有效的,则该嵌入的code是有效的。记住(push)的当前的嵌入级别和覆盖状态。设置当前的级别的值为新的级别,覆写状态重置到right-to-left

b。如果新的级别将是无效的,那么code是无效的。不要改变目前的级别或覆写状态。

X5。每个LRO,计算最大的偶数嵌套级别。

a。如果这个新的级别将是有效的,则该嵌入的代码是有效的。记住(push)的当前的嵌入级别和覆盖状态。设置当前的级别的值为新的级别,覆写状态重置到left-to-right

    b。如果新的水平将是无效的,那么这段代码是无效的。不要改变目前的级别或覆写状态。

X6。对于所有类型(除BN,RLE,LRE,RLO,LRO和PDF)

    a。设置在当前字符的级别的值为当前嵌套级别。

    b。每当方向的覆写状态是不是neutral,根据覆写状态重置当前字符的类型。

如果定向覆盖状态是neutral的,那么字符保留其正常的类型:阿拉伯语字符仍然是AL,拉丁字符仍然是L,中性字符仍然是N,等等。如果定向覆盖状态为R,则文字会变成R.如果定向覆盖状态为L,则文字会变成L。当前的嵌套级别不变。

Terminating Embeddings and Overrides:

X7。每个PDF,确定和她匹配的RLE,LRE,RLO或LRO。如果有一个有效的匹配,恢复(pop)的最后记得(push)嵌套级别和方向覆写。

X8。每个段落结束时,所有明确的方向嵌入和覆盖完全终止。

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

X10。其余的规则施加到的同一个Level run上的字符。对于每个run,确定start-of-level-run (sor) 和 end-of-level-run (eor) 的类型是L还是R,这取决于他两边的Level的值的较大者(如果在段开始或者结束,其中一边是段级别)。如果较大者是奇数时,该类型是R,否则,它是L。

Symbol

Description

N

Neutral or Separator (B, S, WS, ON)

e

The text ordering type (L or R) that matches the embedding level direction (even or odd)

sor

The text ordering type (L or R) assigned to the position before a level run.

eor

The text ordering type (L or R) assigned to the position after a level run.

例如:

Levels:  0   0   0   1   1   1   2

Runs:   <--- 1 ---> <--- 2 ---> <3>

Run 1 is at level 0, sor is L, eor is R.
Run 2 is at level 1, sor is R, eor is L.
Run 3 is at level 2, sor is L, eor is L.

(3)解决弱类型

解决弱类型都是基于level run的。

W1。在一个level run中,检查每一个nonspacing mark (NSM),设置 NSM的字符类型为它的前一个字符的类型,其特征的类型。如果NSM是在一个level run的开始位置,它的字符类型为sor的类型。

假设下面的例子sor的为R:

    AL  NSM  NSM → AL  AL  AL    

sor   NSM      → sor   R

W2。从European number的位置反向搜索,直到找到第一个强类型(R,L,AL,或SOR)。如果找到的是AL类型,修改European number 的类型为Arabic number。

     

AL  EN    → AL  AN

AL  N  EN  → AL  N  AN

Sor  N  EN  → sor  N  EN

L    N  EN   → L   N  EN

R    N  EN   → R  N  EN

W3。将所有AL类型改为R.

W4。一个统一的欧洲之间的分隔两个欧洲的数字到欧洲的数字。一个单一的共同到该类型的相同类型的变化的两个数字之间的分隔符。两个European number之间的单个European separator 改为European number。两个相同类型number之间的单个common separator改为number类型。

EN  ES  EN → EN  EN  EN

EN  CS  EN → EN  EN  EN

AN  CS  AN → AN  AN  AN

W5。与European numbers相邻的连续的European terminators全部修改为European numbers类型

ET  ET  EN → EN  EN  EN

EN  ET  ET → EN  EN  EN

AN  ET  EN → AN  EN  EN

W6。否则,separators分隔符和terminators终止符更改为Other Neutral(ON)。

AN  ET    → AN  ON

L    ES  EN → L  ON  EN

EN  CS  AN → EN ON  AN

ET  AN    → ON  AN

W7。从一个European number开始反向搜索,直到第一个强类型(R,L,或sor)为止。如果找到的是L类型,改变European number类型为L。

    L  N  EN => L  N  L

    R  N  EN => R  N  EN

(4)解决中性的类型

解决中性的类型都是基于level run的。

N1。被具有相同方向的强字符包围的一组连续的中性字符修改其方向与强字符方向相同。

European numbers和 Arabic numbers被当做R类型来使用。Start-of-level-run (sor) 和 end-of-level-run (eor)被当做level run边界使用。

L   N   L  →     L  L   L

  R   N   R  →     R  R   R

  R   N   AN  →   R  R  AN

  R   N   EN  →   R  R  EN

AN  N   R  →    AN  R   R

AN  N   AN  →  AN  R  AN

AN  N   EN  →  AN  R  EN

EN  N   R  →    EN  R   R

EN  N   AN  →   EN  R  AN

EN  N   EN  →   EN  R  EN

N2。剩余的其它中性字符修改为嵌套方向。

    N→e

嵌套方向有嵌套级别来决定:如果嵌套级别是偶数则是L,是奇数则为 R。

假设下面的例子中 eor 为L and sor 为 R

L   N  eor → L   L  eor

R   N  eor → R   e  eor

sor  N  L   → sor  e  L

sor  N  R   → sor  R  R

(5)解决隐式嵌套级别

I1。 对于偶数(left-to-right)的嵌入方向的所有字符,R类型字符增加一个等级和AN或EN类型字符增加两个等级。

I2。对于奇数(right-to-left)的嵌入方向的所有字符,L、AN或EN类型字符增加一个等级。

Type

Embedding Level

Even

Odd

L

EL

EL+1

R

EL+1

EL

AN

EL+2

EL+1

EN

EL+2

EL+1

(四)重新排列顺序,用于显示

L1依次从高嵌套等级到低嵌套等级等级翻转字符,到最低奇数级别1为止,例如,做高级别为4,段落级别为0,依次翻转4,翻转3-4,翻转2-3-4,翻转1-2-3-4。

L2。当且仅当(a)在该字符的方向嵌套方向是R,和(b)的Bidi_Mirrored该字符的属性值是true的时候,该字符镜像字符代替。例如U +0028“(” 左括号和U 0029“)可以成为镜像字符。

以上的BiDi算法是对Unicode Standard Annex #9 的部分翻译和自己的理解。具体的Unicode Bidirectional Algorithm 可以到UAX #9: Unicode Bidirectional Algorithm上去浏览。

五、BiDi算法部分ICU4C的接口函数

1、  UBiDi *  ubidi_open (void)

函数功能:生成一个UBiDi结构,是一个空的对象,通过调用ubidi_setPara()ubidi_setLine(),一些Bidi properties(例如有几个段等)被写到UBiDi结构中。该对象可以重复使用,除非调用ubidi_close()释放它。

返回值:An empty UBiDi object(空的UBiDi对象)。

2void ubidi_setPara ( UBiDi *  pBiDi,

           const UChar *  text,

           int32_t   length,

           UBiDiLevel   paraLevel,

           UBiDiLevel *  embeddingLevels,

           UErrorCode *  pErrorCode )

函数功能:执行BiDi算法(Unicode Bidi algorithm)的函数。输入一个包含一段或者多段的文本,计算每个字符的左右方向,执行BiDi算法。

当输入的是同一方向的文本的时候,该函数不执行算法中的所有步骤,因为没有必要 。

输入的文本可以由多个段落组成,段落的段落分隔符划分,回车换行等(CR LF,LF,FS)

参数:

pBiDiubidi_open 函数返回会的对象,pBiDi在函数调用后会包含文本重新排列的信息,包括所有字符的resolved levels等。

text 指向BiDi算法要解析转换的文本。

length文本text的长度。如果length == -1则文本text必需要以\0结尾。

paraLevel文本text的默认默认嵌套等级,通常是0(LTR)或者1(RTL)。如果要有文本text来确定的话,可以设置为UBIDI_DEFAULT_LTR 或者 UBIDI_DEFAULT_LTR,如果文本包含多个段落,那么每个段落会被单独确定,段落之间不受影响。段落文本方向是第一个强字符强字符的方向,如果没有强字符的话,UBIDI_DEFAULT_LTR默认设置为L,UBIDI_DEFAULT_LTR默认设置为R。也可以设置为0- UBIDI_MAX_EXPLICIT_LEVEL之间的数字,那么偶数为L,奇数为R。

embeddingLevels预设每个字符的嵌套等级(忽略想LRE 和PDF这类的字符)。如果不需要指定的话,可以是为NULL。

例子:

a、static const char* const text = "__ABC\\u001c"                     /* Para #0 offset 0 */

                                    "__\\u05d0DE\\u001c"    /*       1        6 */

                                    "__123\\u001c"          /*       2       12 */

                                    "\\u000d\\u000a"        /*       3       18 */

                                    "FG\\u000d"             /*       4       20 */

                                    "\\u000d"               /*       5       23 */

                                    "HI\\u000d\\u000a"      /*       6       24 */

                                    "\\u000d\\u000a"        /*       7       28 */

                                    "\\u000a"               /*       8       30 */

                                    "\\u000a"               /*       9       31 */

                                    "JK\\u001c";            /*      10       32 */

    static const int32_t paraCount=11;

static const int32_t paraBounds[]={0, 6, 12, 18, 20, 23, 24, 28, 30, 31, 32, 35};

static const UBiDiLevel paraLevels[]={UBIDI_LTR, UBIDI_RTL, UBIDI_DEFAULT_LTR, UBIDI_DEFAULT_RTL, 22, 23};

    static const UBiDiLevel multiLevels[6][11] = {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},

                                           {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},

                                           {0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0},

                                            {0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0},

                                            {22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22},

                                             {23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23}};

    u_unescape(text, src, MAXLEN);

srcSize=u_strlen(src);

ubidi_setPara(pBidi, src, srcSize, UBIDI_LTR, NULL, &errorCode);

src有11个段落,段落边界为paraBounds中的数据。

b、检查段落paraLevel的设置【multiLevels[6][11]

for (k=0; k<6; k++) {

        ubidi_setPara(pBidi, src, srcSize, paraLevels[k], NULL, &errorCode);

        for (i=0; i<paraCount; i++) {

            paraIndex=ubidi_getParagraph(pBidi, paraBounds[i], NULL, NULL, &gotLevel, errorCode);

            if (paraIndex!=i) {

                log_err("For paraLevel=%d paragraph=%d, found paragraph index=%d  pected=%d\n",

                        paraLevels[k], i, paraIndex, i);

            }

            if (gotLevel!=multiLevels[k][i]) {

                log_err("For paraLevel=%d paragraph=%d, found level=%d expected %d\n",

                        paraLevels[k], i, gotLevel, multiLevels[k][i]);

            }

        }

        gotLevel=ubidi_getParaLevel(pBidi);

        if (gotLevel!=multiLevels[k][0]) {

            log_err("For paraLevel=%d getParaLevel=%d, expected %d\n",

                    paraLevels[k], gotLevel, multiLevels[k][0]);

        }

    }

c、检查逻辑顺序到视觉顺序的转换

srcLen = u_unescape("abc  \\u05d2\\u05d1\n", src, MAXLEN);

ubidi_setPara(pBidi, src, srcLen, UBIDI_DEFAULT_LTR, NULL, &errorCode);

destLen = ubidi_writeReordered(pBidi, dest, MAXLEN, 0, &errorCode);

dest的值为:abc  \\u05d1 \\u05d2\n

3 void ubidi_setLine ( const UBiDi *   pParaBiDi,

  int32_t   start,

              int32_t   limit,

              UBiDi *   pLineBiDi,

              UErrorCode *   pErrorCode )

函数功能:可以理解为ubidi_setPara()调用之后,共享其中的一部分数据,形成新的UbiDi对象。

参数:

pParaBiDi ubidi_setPara函数调用成功,ubidi_setPara函数的第一个参数。

Start:在文本开始的index

limit 文本结束index

pLineBiDi  包含一段文本的排列信息的BiDi对象指针

例子:

1、static const char* const text = "__ABC\\u001c"                     /* Para #0 offset 0 */

                                    "__\\u05d0DE\\u001c"    /*       1        6 */

                                    "__123\\u001c"          /*       2       12 */

                                    "\\u000d\\u000a"        /*       3       18 */

                                    "FG\\u000d"             /*       4       20 */

                                    "\\u000d"               /*       5       23 */

                                    "HI\\u000d\\u000a"      /*       6       24 */

                                    "\\u000d\\u000a"        /*       7       28 */

                                    "\\u000a"               /*       8       30 */

                                    "\\u000a"               /*       9       31 */

                                    "JK\\u001c";            /*      10       32 */

    static const int32_t paraCount=11;

static const int32_t paraBounds[]={0, 6, 12, 18, 20, 23, 24, 28, 30, 31, 32, 35};

ubidi_setPara(pBidi, src, srcSize, UBIDI_RTL, NULL, &errorCode);

   

  i=paraBounds[1];

k=paraBounds[2];

ubidi_setLine(pBidi, i, k, pLine, &errorCode);

destLen = ubidi_writeReordered(pLine, dest, MAXLEN, 0, &errorCode);

dest的值为: \\u001c DE\\u05d0__

4、int32_t ubidi_writeReordered ( UBiDi *   pBiDi,

  UChar *   dest,

  int32_t   destSize,

                      uint16_t   options,

                      UErrorCode *  pErrorCode )

函数功能:将,ubidi_setPara或ubidi_setLine排列完成的数据写到缓冲区dest

参数

pBiDiubidi_setPara或ubidi_setLine函数调用成功第一个参数。

dest:在目标缓冲区、

destSizedest的大小

optionsUBIDI_OUTPUT_REVERSE设置字符翻转输出等一些设置,不用的话可是只为0。

例子:

参见上面的例子

5、void ubidi_setReorderingMode ( UBiDi *  pBiDi,

UBiDiReorderingMode  reorderingMode)  

函数功能:在调用ubidi_setPara之前设置生效,BiDi算法一些设置。

参数:

reorderingModeUBIDI_REORDER_DEFAULT:执行标准的BiDi算法

UBIDI_REORDER_INVERSE_LIKE_DIRECT 逆BiDi算法UBIDI_REORDER_INVERSE_NUMBERS_AS_L 逆BiDi算法"FED 123 456 CBA" (大写字母代表RTL) 设置UBIDI_REORDER_INVERSE_LIKE_DIRECT 转化为

"ABC 456 123 DEF",

设置UBIDI_REORDER_INVERSE_NUMBERS_AS_L则转化为"DEF 123 456 ABC"

例子

    ubidi_setReorderingMode(pBidi, UBIDI_REORDER_INVERSE_LIKE_DIRECT);

    srcLen = u_unescape("abc \\u05d2\\u05d1\n", src, MAXLEN);

    ubidi_setPara(pBidi, src, srcLen, UBIDI_DEFAULT_LTR, NULL, &errorCode);

    destLen = ubidi_writeReordered(pBidi, dest, MAXLEN, 0, &errorCode);

    dest 的值为:\\u05d1\\u05d2 abc

6、一些其他的函数,具体的可以参看:

ICU-docs - International Components for Unicode Docs | icu-docs

  

void ubidi_setContext (UBiDi *pBiDi, const UChar *prologue, int32_t proLength, const UChar *epilogue, int32_t epiLength, UErrorCode *pErrorCode)

   Set the context before a call to ubidi_setPara()

UBiDiDirection ubidi_getDirection (const UBiDi *pBiDi)

  Get the directionality of the text.

UBiDiDirection ubidi_getBaseDirection (const UChar *text, int32_t length)

  Gets the base direction of the text provided according to the Unicode Bidirectional Algorithm.

const UChar * ubidi_getText (const UBiDi *pBiDi)

  Get the pointer to the text.

int32_t ubidi_getLength (const UBiDi *pBiDi)

  Get the length of the text.

UBiDiLevel ubidi_getParaLevel (const UBiDi *pBiDi)

  Get the paragraph level of the text.

int32_t ubidi_countParagraphs (UBiDi *pBiDi)

  Get the number of paragraphs.

int32_t ubidi_getParagraph (const UBiDi *pBiDi, int32_t charIndex, int32_t *pParaStart, int32_t *pParaLimit, UBiDiLevel *pParaLevel, UErrorCode *pErrorCode)

  Get a paragraph, given a position within the text.

void ubidi_getParagraphByIndex (const UBiDi *pBiDi, int32_t paraIndex, int32_t *pParaStart, int32_t *pParaLimit, UBiDiLevel *pParaLevel, UErrorCode *pErrorCode)

  Get a paragraph, given the index of this paragraph.

UBiDiLevel ubidi_getLevelAt (const UBiDi *pBiDi, int32_t charIndex)

  Get the level for one character.

const UBiDiLevel * ubidi_getLevels (UBiDi *pBiDi, UErrorCode *pErrorCode)

  Get an array of levels for each character.

void ubidi_getLogicalRun (const UBiDi *pBiDi, int32_t logicalPosition, int32_t *pLogicalLimit, UBiDiLevel *pLevel)

  Get a logical run.

int32_t ubidi_countRuns (UBiDi *pBiDi, UErrorCode *pErrorCode)

  Get the number of runs.

UBiDiDirection ubidi_getVisualRun (UBiDi *pBiDi, int32_t runIndex, int32_t *pLogicalStart, int32_t *pLength)

  Get one run's logical start, length, and directionality, which can be 0 for LTR or 1 for RTL.

int32_t ubidi_getVisualIndex (UBiDi *pBiDi, int32_t logicalIndex, UErrorCode *pErrorCode)

  Get the visual position from a logical text position.

int32_t ubidi_getLogicalIndex (UBiDi *pBiDi, int32_t visualIndex, UErrorCode *pErrorCode)

  Get the logical text position from a visual position.

void ubidi_getLogicalMap (UBiDi *pBiDi, int32_t *indexMap, UErrorCode *pErrorCode)

  Get a logical-to-visual index map (array) for the characters in the UBiDi (paragraph or line) object.

void ubidi_getVisualMap (UBiDi *pBiDi, int32_t *indexMap, UErrorCode *pErrorCode)

  Get a visual-to-logical index map (array) for the characters in the UBiDi (paragraph or line) object.

void ubidi_reorderLogical (const UBiDiLevel *levels, int32_t length, int32_t *indexMap)

  This is a convenience function that does not use a UBiDi object.

void ubidi_reorderVisual (const UBiDiLevel *levels, int32_t length, int32_t *indexMap)

  This is a convenience function that does not use a UBiDi object.

void ubidi_invertMap (const int32_t *srcMap, int32_t *destMap, int32_t length)

  Invert an index map.

int32_t ubidi_getProcessedLength (const UBiDi *pBiDi)

  Get the length of the source text processed by the last call to ubidi_setPara().

int32_t ubidi_getResultLength (const UBiDi *pBiDi)

  Get the length of the reordered text resulting from the last call to ubidi_setPara().

  • 14
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值