Theppitak's blog

My personal blog.

14 กรกฎาคม 2011

Esaan Tham Font with GSUB

ทิ้งค้างไว้จาก blog ที่แล้ว เสียนาน ก็เพิ่งได้โอกาสกลับมาทำ โครงการอักษรอีสาน ต่อ โดยหลังจากที่ได้ glyph ต่าง ๆ เรียบร้อยแล้ว ก็ถึงคราวทำเรื่องกฎการแสดงข้อความ

ดังที่ได้อธิบายไว้ ว่าอักษรธรรมในยูนิโค้ดจะลงรหัสในข้อความในลำดับที่เรียกว่า logical order ตามแบบอักษรอินเดีย (ซึ่งจากการที่โดนกระแนะกระแหนบ่อย ๆ ว่าอักษรไทยกับลาวเป็น ข้อยกเว้น ของ logical order สำหรับอักษรในตระกูลเดียวกัน ราวกับว่าสองอักษรนี้ไม่ logical กระนั้น ก็ทำให้ผมอยากจะเรียก logical order เสียใหม่ว่า phonetic order) ซึ่งลำดับการเก็บแบบ logical order หรือ phonetic order นี้ แตกต่างจากลำดับที่แสดงผลจริงซึ่งเรียกว่า visual order ทำให้ต้องมีการเรียงลำดับ glyph เสียใหม่ขณะแสดงผล

เนื่องจากทางเลือกอื่น คือการรอให้ rendering engine รองรับอักษรธรรม หรือการรอให้ไมโครซอฟท์จัดทำข้อกำหนดของการแสดงอักษรธรรมด้วย OpenType เป็นเรื่องที่ไม่สามารถสำเร็จได้ในเวลาอันสั้น ผมจึงจำเป็นต้องใช้วิธีสร้างกฎ GSUB ให้สามารถวาดแสดงได้ในตัวเองโดยไม่พึ่ง preprocessing ใน rendering engine เลย

โจทย์หนักที่สุดคือการสลับลำดับสระหน้า เพราะ GSUB ไม่มีรูปแบบกฎที่ซับซ้อนพอที่จะเขียนการสลับ AB เป็น BA ได้ เครื่องมือที่มีให้คือ:

  • Multiple substitution คือการแทน glyph 1 glyph ด้วย glyph string เช่น A → XYZ
  • Ligature substitution คือการแทน glyph string ด้วย glyph 1 glyph เช่น XYZ → A
  • Contextual substitution คือการกำหนด substitution (multiple หรือ ligature ข้างต้น) ที่จะกระทำเมื่อพบแพตเทิร์นที่กำหนด เช่น ถ้าพบ abc ให้ใช้กฎ P ที่ตำแหน่ง glyph ที่ 2 เป็นต้น

ยังมี substitution แบบอื่นอีกพอประมาณ แต่ก็ไม่ได้มีความสามารถมากไปกว่านี้ ซึ่งจะพบว่าการเขียนกฎง่าย ๆ แค่ AB → BA นั้น ไม่ใช่เรื่องง่ายเอาเสียเลย แต่โชคดีที่เผอิญไปได้เคล็ดวิชาจากเพื่อนชาวพม่ามาว่าเขามีลูกสูตรกันแบบนี้:

AB → BAB [Mul. A → BA]
BAB → BA [Lig. AB → A]

คือใช้ contextual 2 กฎ, multiple 1 กฎ และ ligature อีก 1 กฎ โดย contextual นั้นสามารถกำหนดแพตเทิร์นเป็นคลาสได้ แต่ multiple และ ligature นั้น ต้องลิสต์ทุก combination แบบเรียงตัวเท่านั้น

เนื่องจากรูปแบบระหว่างทาง (คือ BAB ในตัวอย่างข้างต้น) นั้นสุ่มเสี่ยงที่จะไปชนกับข้อความจริง ผมจึงดัดแปลงกฎเสียใหม่เป็นแบบนี้:

AB → BAxB [Mul. A → BAx]
BAxB → BA [Lig. AxB → A]

โดย x เป็น dummy ตัวหนึ่งที่ใส่เข้ามาเพื่อป้องกันการ match กับข้อความจริง จะเลือก glyph อะไรก็ได้ที่ไม่มีที่ใช้ในข้อความจริง

และในบางกฎ ก็เหมาะที่จะทำข้างหลังก่อน ซึ่งจะทำให้ลดจำนวนกฎ contextual ลงได้:

AB → AxBA [Mul. B → xBA]
AxBA → BA [Lig. AxB → B]

ได้สูตรแบบนี้แล้ว ผมก็มานั่งออกแบบกฎ GSUB สำหรับอักษรธรรมอีสานได้แล้ว โดยหลังจากประมวลอักขรวิธีต่าง ๆ แล้ว ก็สรุปเป็นขั้นตอนการทำงานดังนี้:

  1. สลับลำดับ ไม้อังแล่น (ง สะกดเหนือพยัญชนะ) ในภาษาบาลี
      NGA-above + SAKOT + Cons --> Cons + NGA-above
    
  2. เปลี่ยนพยัญชนะเป็นรูปตัวห้อย/ตัวเฟื้องเมื่อตามหลัง SAKOT
      SAKOT + Cons --> Cons.sub
    
  3. สลับลำดับสระหน้า
      Cons + [sub|medial-RA] + LV --> LV + Cons + [sub|medial-RA]
    
  4. สลับลำดับ ระวง
      Cons + medial-RA --> medial-RA + Cons
    
  5. จัดการรูปย่อ น + า แบบซับซ้อน
      NA + {sub|tone|UV|BV} + AA --> NAA + {sub|tone|UV|BV}
    
  6. จัดการรูปย่อทั่วไป
      NA + AA --> NAA
      WA + tall-AA --> WAA
    

หลังจากได้ลำดับกฎแล้ว ก็มา implement กฎแต่ละกฎด้วยสูตรข้างต้น โดยมีเคล็ดในการลดจำนวนกฎอยู่ว่า ให้พยายามเลือกเซ็ตที่ใหญ่ที่สุด (เช่น พยัญชนะ) เป็นตัวแปลง แล้วไปลิสต์รายการในตาราง multiple/ligature substitution เป็นหมวด ๆ เอา จะทำให้ได้จำนวนหมวดน้อย จัดการในระดับบนได้ง่าย ถ้าเราไปเลือกแปลงเซ็ตเล็ก (เช่น สระหน้า) แทน จะทำให้ได้หมวดเล็ก ๆ จำนวนมาก จำนวน contextual substitution มาก จัดการในระดับบนได้ลำบาก

หมายเหตุ: แม้จะลดปริมาณกฎลงแล้ว แต่ก็ยังมากอยู่ดี ทำใน fontforge อย่างเดียวไม่ไหว งานนี้สำเร็จได้ด้วย vim ครับ ;-)

ทำฟอนต์เสร็จแล้ว ก็ทำ หน้าทดสอบฟอนต์ เอาไว้ด้วย โดยฝังฟอนต์ลงในเว็บเลย เปิดด้วย Firefox/Iceweasel ตั้งแต่รุ่น 4 ขึ้นไปได้ผลดี ส่วน Chrome/Chromium หรือเบราว์เซอร์อื่นที่ใช้ WebKit (เช่น Epiphany, Midori) นั้น แสดงอักษรได้อยู่ แต่กฎ GSUB จะไม่ทำงาน ทำให้ลำดับการแสดงไม่ใช่ลำดับสำหรับมนุษย์อ่าน ส่วน IE ไม่มีให้ทดสอบครับ

ป้ายกำกับ:

0 ความเห็น:

แสดงความเห็น (มีการกลั่นกรองสำหรับ blog ที่เก่ากว่า 14 วัน)

<< กลับหน้าแรก

hacker emblem
No PAD No UDD
No Violent Mob