type EPdfBidiError = class(Exception);
type TPdfBidiClass = (bcL, bcR, bcAL, bcEN, bcES, bcET, bcAN, bcCS, bcNSM, bcBN, bcB, bcS, bcWS, bcON, bcLRE, bcLRO, bcRLE, bcRLO, bcPDF, bcLRI, bcRLI, bcFSI, bcPDI);
type TPdfBidiParagraphDirection = (pbdAuto, pbdLeftToRight, pbdRightToLeft);
type TPdfBidiLevels = array of Byte;
type TPdfBidiOrder = array of Integer;
const PdfBidiMaxDepth = 125;
function PdfBidiClassOf(CodePoint: Cardinal): TPdfBidiClass;
function PdfBidiIsRtlCodePoint(CodePoint: Cardinal): Boolean;
function PdfResolveBidiLevels(const Text: WideString; Direction: TPdfBidiParagraphDirection; out Levels: TPdfBidiLevels; out ParagraphLevel: Byte): Boolean;
function PdfBidiVisualOrder(const Text: WideString; const Levels: TPdfBidiLevels): TPdfBidiOrder;
function PdfBidiParagraphIsRtl(const Text: WideString): Boolean;
FPdfBidi implement Unicode Bidirectional Algorithm ตามที่ UAX #9 กำหนด ซึ่งเป็นตัวชี้ทิศทางและลำดับการแสดงผลของข้อความที่มีทิศทางผสม ก่อนที่ข้อความจะถูกวัดขนาดหรือวาด ส่วนขั้นตอน shaping ทำงานกับ run ที่ทิศทางและสคริปต์ถูกตัดสินไปแล้ว ผู้เรียกจึงต้องหาทิศทางนั้นให้เสร็จก่อน
ยูนิตนี้ครอบคลุมกฎ P2-P3 สำหรับระดับย่อหน้า X1-X10 สำหรับ explicit level และ isolating run sequence W1-W7 สำหรับชนิด weak N0-N2 สำหรับคู่วงเล็บและอักขระกลาง I1-I2 สำหรับ implicit level และ L1-L2 สำหรับการรีเซ็ตระดับและการจัดลำดับใหม่ รวมถึง isolate initiator และคู่วงเล็บตาม BD16
การหาค่าทำงานบน code point แทนที่จะเป็น UTF-16 code unit แล้วค่อยขยายระดับที่ได้กลับลงบน code unit ในตอนจบ ทั้งสองครึ่งของ surrogate pair จึงมีระดับเท่ากัน
PdfResolveBidiLevels เป็น entry point หลัก: Direction เลือกการสแกน P2-P3 ผ่าน pbdAuto หรือบังคับระดับย่อหน้า Levels รับ embedding level หนึ่งค่าต่อ code unit ของ Text และ ParagraphLevel รับระดับที่ย่อหน้าตกลงใช้ ผลลัพธ์เป็น False เฉพาะเมื่ออินพุตว่างเปล่า และ Levels มีขนาดเท่ากับ Length(Text) เสมอ
PdfBidiVisualOrder ใช้กฎ L2 กับระดับเหล่านั้น แล้วคืนดัชนี code unit เชิงตรรกะที่ควรอยู่แต่ละตำแหน่งการแสดงผล องค์ประกอบตัวที่ศูนย์ของผลลัพธ์จึงเป็น code unit ที่วาดอยู่ซ้ายสุด ต้องมี Text เพื่อรักษา surrogate pair ให้อยู่ด้วยกัน เพราะการสลับ code unit ทั้งสองจะทำให้คู่นั้นกลายเป็น surrogate สองตัวที่ไม่มีคู่
PdfBidiClassOf เปิดเผยการค้น Bidi_Class ตรง ๆ ตารางมาจาก Unicode Character Database แล้วยุบเป็นช่วง ๆ code point ที่ฐานข้อมูลไม่ได้กำหนดจะ fallback ไปใช้ช่วงเริ่มต้นที่ UAX #9 กำหนดไว้ ได้แก่บล็อก R, AL, ET และ BN ส่วนที่เหลือเป็น L
PdfBidiIsRtlCodePoint และ PdfBidiParagraphIsRtl ตอบสองคำถามที่ผู้เรียกมักถามก่อนจัดเลย์เอาต์: code point หนึ่งตัวเป็น right-to-left แบบ strong หรือไม่ และย่อหน้าทั้งย่อหน้าตกลงเป็น right-to-left ภายใต้ P2-P3 หรือไม่
| Function | Description |
|---|---|
PdfBidiClassOf | คืน TPdfBidiClass ของ code point หนึ่งตัว โดยใช้ตารางช่วงและค่าเริ่มต้นของ UAX #9 สำหรับ code point ที่ฐานข้อมูลไม่ได้กำหนด |
PdfBidiIsRtlCodePoint | คืน True เมื่อ code point เป็นอักขระ right-to-left แบบ strong คือ bcR หรือ bcAL |
PdfResolveBidiLevels | หา embedding level หนึ่งค่าต่อ UTF-16 code unit ของ Text ลงใน Levels และรายงานระดับย่อหน้าใน ParagraphLevel คืน False เฉพาะเมื่ออินพุตว่างเปล่า |
PdfBidiVisualOrder | ใช้การจัดลำดับใหม่ L2 กับ Levels แล้วคืนดัชนี code unit เชิงตรรกะสำหรับแต่ละตำแหน่งการแสดงผล โดยรักษา surrogate pair ให้คงอยู่ครบ |
PdfBidiParagraphIsRtl | คืน True เมื่อ Text ตกลงเป็นระดับย่อหน้า right-to-left ภายใต้ P2-P3 |
| Type | Purpose |
|---|---|
EPdfBidiError | คลาสข้อยกเว้นที่ยูนิตประกาศไว้สำหรับความล้มเหลวด้าน bidi สืบทอดจาก Exception |
TPdfBidiClass | Bidi_Class ของ code point เรียงตามลำดับค่าใน UAX #9 Table 4 |
TPdfBidiParagraphDirection | เลือกระหว่างการตรวจหาตาม P2-P3 กับการบังคับทิศทางย่อหน้า |
TPdfBidiLevels | array of Byte เก็บ embedding level ที่หาได้หนึ่งค่าต่อ UTF-16 code unit |
TPdfBidiOrder | array of Integer เก็บดัชนี code unit เชิงตรรกะเรียงตามลำดับการแสดงผล |
| Constant | Value and purpose |
|---|---|
PdfBidiMaxDepth | 125 คือความลึกการซ้อนแบบ explicit สูงสุดที่ X1 ยอมให้ embedding และ isolate ที่เกินจะถูกนับเป็น overflow แทนที่จะเพิ่มระดับ |
| Value | Meaning |
|---|---|
pbdAuto | หาระดับย่อหน้าจากอักขระ strong ตัวแรก ตาม P2-P3 |
pbdLeftToRight | บังคับระดับย่อหน้า 0 |
pbdRightToLeft | บังคับระดับย่อหน้า 1 |
| Value | Bidi_Class and category |
|---|---|
bcL | Left_To_Right, strong |
bcR | Right_To_Left, strong |
bcAL | Arabic_Letter, strong; กลายเป็น bcR ภายใต้ W3 |
bcEN | European_Number, weak |
bcES | European_Separator, weak |
bcET | European_Terminator, weak |
bcAN | Arabic_Number, weak |
bcCS | Common_Separator, weak |
bcNSM | Nonspacing_Mark, weak; รับคลาสของอักขระก่อนหน้าภายใต้ W1 |
bcBN | Boundary_Neutral, weak; ถูกลบโดย X9 |
bcB | Paragraph_Separator, neutral; สิ้นสุด embedding ทุกชั้นภายใต้ X8 |
bcS | Segment_Separator, neutral |
bcWS | White_Space, neutral |
bcON | Other_Neutral, neutral; คลาสที่วงเล็บถือเข้าไปใน N0 |
bcLRE | Left_To_Right_Embedding, explicit formatting |
bcLRO | Left_To_Right_Override, explicit formatting |
bcRLE | Right_To_Left_Embedding, explicit formatting |
bcRLO | Right_To_Left_Override, explicit formatting |
bcPDF | Pop_Directional_Format, explicit formatting; ปิด embedding หนึ่งชั้นภายใต้ X7 |
bcLRI | Left_To_Right_Isolate, isolate initiator |
bcRLI | Right_To_Left_Isolate, isolate initiator |
bcFSI | First_Strong_Isolate, isolate initiator ที่ทิศทางมาจากอักขระ strong ตัวแรกจนถึง bcPDI ที่จับคู่กัน |
bcPDI | Pop_Directional_Isolate, สิ้นสุด isolate ภายใต้ X6a |
bcB ภายใน Text จะรีเซ็ต embedding ที่เปิดอยู่ทั้งหมดตาม X8 ข้อความที่มีหลายย่อหน้าจึงควรแยกก่อนหาค่าLength(Levels) จึงเท่ากับ Length(Text) และ surrogate pair หนึ่งคู่กินสอง entry ที่มีระดับเดียวกันPdfBidiVisualOrder จึงไม่เห็นช่องว่างในอาร์เรย์ระดับPdfBidiVisualOrder คืนลำดับเดิมเมื่อไม่มีระดับคี่ใดอยู่ เพราะลำดับเชิงตรรกะก็คือลำดับการแสดงผลอยู่แล้วในกรณีนั้นTPdfBidiLevels และ TPdfBidiOrder เป็นอาร์เรย์แบบ dynamic ที่ผู้เรียกเป็นเจ้าของ และถูกปล่อยด้วยการนับอ้างอิงตามปกติ