Theppitak's blog

My personal blog.

29 สิงหาคม 2561

thpronun

ประกาศเปิดตัวโครงการ thpronun ซึ่งเป็นซอฟต์แวร์เสรีสำหรับวิเคราะห์เสียงอ่านของข้อความภาษาไทย (grapheme-to-phoneme) ภายใต้การสนับสนุนของ บริษัท เมตามีเดีย เทคโนโลยี จำกัด

ซอฟต์แวร์เสรีที่สนับสนุนโดยเอกชน

เรื่องของเรื่องคือ ผมได้รับการติดต่อว่าจ้างจากเมตามีเดียให้พัฒนาโปรแกรมสำหรับแจงคำอ่านจากตัวสะกดของคำไทย ผมจึงได้เสนอที่จะขายไลเซนส์ของตัวโปรแกรมให้ในแบบ GPL ซึ่งปรากฏว่าเมตามีเดียรับข้อเสนอนี้ และยินดีให้เผยแพร่ซอร์สโค้ดของโปรแกรมสู่สาธารณะได้ โดยมุ่งหวังให้เป็น deal ตัวอย่างในเมืองไทยที่นักพัฒนาซอฟต์แวร์เสรีจะสามารถสร้างรายได้จากตัวซอฟต์แวร์โดยตรง จึงขอขอบคุณเมตามีเดียมา ณ ที่นี้

เมื่อได้ข้อตกลงแล้ว ผมจึงนั่งร่างโปรแกรมใน local Git จนโปรแกรมพร้อมทดสอบ จึงได้หารือเรื่อง repository ที่จะใช้ส่งโค้ดระหว่างกัน ซึ่งคุณภัทระก็เสนอให้ใช้ tlwg บน GitHub ไปเลย ดังนั้น TLWG จึงได้เกิดโครงการน้องใหม่คือ thpronun ด้วยประการฉะนี้

นอกจากนี้ ในระหว่างการพัฒนา ก็ได้รับ contribution ใน libthai 0.1.28 เป็นอานิสงส์ด้วย

สัญญาว่าจ้างสิ้นสุดลงเมื่อผมออกรุ่นแรกสู่สาธารณะ (คือ รุ่น 0.2.0) หลังจากนี้ไปก็จะพัฒนาในแบบชุมชนโอเพนซอร์สตามปกติ ส่วนเมตามีเดียก็จะมี customization เชิงพาณิชย์สำหรับลูกค้าต่อไป

ลักษณะของโปรแกรม

ตัวโปรแกรมเป็นคำสั่ง command line รับข้อมูลเข้าเป็นข้อความภาษาไทย แล้วจะให้ข้อมูลออกเป็นคำอ่านที่เป็นไปได้ทั้งหมดของข้อความนั้น โดยสามารถเลือกรูปแบบของคำอ่านได้จาก command-line option

$ thpronun --help
Usage: thpronun [OPTION] [WORD...]

Thai word pronunciation program.

It reads Thai words from command-line arguments, or from standard input
if no argument is given, and generates all possible pronunciations
of the words.

Options:

General:
  -V, --version  Displays program version info
  -h, --help     Displays help
  -d<DICTPATH>   Use exception dict from <DICTPATH>
  -n             Turns off word segmentation

Output structures:
  -j             Turns on JSON output
  -g             Turns on grouping in JSON output (implies '-j')

Output notations:
  -r             Outputs Romanization
  -t             Outputs Thai pronunciation
  -p             Outputs Phonetic form
  -w             Outputs Raw pronunciation code
  -s             Outputs Soundex code

ตัวอย่างคำอ่านภาษาไทย:

$ thpronun -t เถลไถล
เถลไถล:
ถะ-เหฺล-ถะ-หฺลัย
เถน-ถะ-หฺลัย
เถ-ละ-ถะ-หฺลัย

Romanization:

$ thpronun -r เถลไถล
เถลไถล:
thalethalai
thenthalai
thelathalai

phonetic:

$ thpronun -p เถลไถล
เถลไถล:
thah1 ley4 thah1 lahy4
theyn4 thah1 lahy4
they4 lah3 thah1 lahy4

soundex:

$ thpronun -s เถลไถล
เถลไถล:
Ta_-le_-Ta_-lay
Ten-Ta_-lay
Te_-la_-Ta_-lay

หรือกระทั่ง raw code ตามที่เก็บในโปรแกรม:

$ thpronun -w เถลไถล
เถลไถล:
T_a_1@-3,l_E_4@3,T_a_1@-306,l_ay4@6
T_En4@3,T_a_1@-306,l_ay4@6
T_E_4@2,l_a_3@3,T_a_1@-306,l_ay4@6

กลไกภายใน

ตัวโปรแกรมพัฒนาด้วย C++14 (ควรคอมไพล์ด้วย GCC 6.1 ขึ้นไป หรือ Clang 3.4 ขึ้นไป) ซึ่งมีสิ่งอำนวยความสะดวกครบครันอย่างที่ภาษาโปรแกรมในยุคนี้ควรมี ทำให้ได้โค้ดที่กระชับและใช้เวลาพัฒนาไม่นานเกินไป โดยเฉพาะเมื่อเทียบกับ C++ สมัยก่อน ๆ

build-dependencies:

  • libthai สำหรับแยกแยะอักขระภาษาไทยและแบ่งคำ
  • libdatrie สำหรับจัดการพจนานุกรมคำยกเว้น
  • help2man สำหรับสร้าง man page อัตโนมัติ (ปิดใช้ได้ด้วย configure option --disable-man)

กลไกภายใน ใช้ hard-coded rule base แบบดิบ ๆ เลย ซึ่งมีข้อดีคือไม่ต้องเสียเวลาเตรียม corpus และสามารถปรับแก้กฎต่าง ๆ ได้ตามต้องการภายในกำหนดเวลาพัฒนาที่มี แต่ข้อเสียนอกเหนือจากเรื่อง human error ที่ต้องตามแก้ในกฎก็คือ ไม่สามารถให้น้ำหนักคำอ่านแต่ละแบบตามความน่าจะเป็นได้ ซึ่งเผอิญว่าโจทย์ที่ต้องการคือการค้นฐานข้อมูล ไม่ใช่การสังเคราะห์เสียงพูดที่ต้องเลือกคำอ่านเพียงแบบเดียว จึงยังพอกล้อมแกล้มได้

เมื่อแจงรูปแบบพยางค์ของ input ด้วยกฎแล้ว ก็จะได้ abstract representation ของคำอ่าน ซึ่งสามารถใช้ polymorphism ในการ generate รูปแบบคำอ่านสุดท้ายที่ต้องการ ไม่ว่าจะเป็น romanization, คำอ่านเรียงพยางค์ด้วยอักษรไทย, คำอ่านแบบ phonetic หรือกระทั่ง soundex

องค์ประกอบหลักของแต่ละพยางค์ประกอบด้วย:

  1. เสียงพยัญชนะต้น
  2. เสียงพยัญชนะควบกล้ำ (ถ้ามี)
  3. เสียงสระ
  4. เสียงพยัญชนะสะกด
  5. เสียงวรรณยุกต์

นอกจากนี้ ยังมี requirement เพิ่มเติมจากเมตามีเดียที่ต้องการให้จัดโครงสร้างข้อมูลเป็น lattice โดยแทนที่จะ enumerate คำอ่านทุก combination เป็น list เส้นตรง ก็ให้จัดเป็น lattice โดยแยกเส้นทางคำอ่านเฉพาะในช่วงที่อ่านได้หลายแบบ แล้ว merge กลับเมื่อถึงจุดร่วม โดยให้จัดรูปแบบ output เป็น nested list ของ JSON (ไม่เกิน 2 ชั้น)

$ thpronun -t -g เถลไถล
เถลไถล:
[[[["เถ","ละ"],["เถน"],["ถะ","เหฺล"]],[["ถะ","หฺลัย"]]]]

โครงสร้างดังกล่าวสามารถแทนได้ด้วย Directed Acyclic Graph (DAG) โดยแต่ละ vertex ของกราฟแทนตำแหน่งอักขระจบพยางค์ในข้อความ input และแต่ละ edge แทนเสียงอ่านที่เกิดขึ้นระหว่างตำแหน่งที่มันเชื่อมโยง

ด้วย requirement นี้ ทำให้ทุกฟีเจอร์ที่จะเพิ่มต้องคำนึงถึงการแปลงเข้าสู่ DAG และการทำ output grouping เสมอ จึงต้องเพิ่มข้อมูลตำแหน่งอักขระจบพยางค์เป็นองค์ประกอบที่หกของทุกพยางค์ด้วย โดยบางพยางค์ที่เพิ่มเข้ามาเพราะหลักอักษรนำ (เช่น ถะ ใน เถล และ ไถล ข้างต้น) ต้องแทนด้วยเลขลบที่ encode ขอบเขตพยางค์ที่มันเกาะอยู่ไปด้วยในเวลาเดียวกัน (ในทางเทคนิค สูตรที่ใช้คือ -(ตำแหน่งต้นพยางค์ * 100 + ตำแหน่งท้ายพยางค์))

ข้อยกเว้น

กฎที่ใช้แจงพยางค์จะพยายามครอบคลุมพยางค์ทั่ว ๆ ไป แต่จะไม่เจาะจงข้อยกเว้นบางอย่าง เช่น ทร ที่อ่านเป็น ซ, ฑ นางมณโฑ ที่อ่านเป็น ด, เสียงวรรณยุกต์ของคำแผลง (เช่น กำเนิด, ตำรวจ), การันต์ซับซ้อน (เช่น กษัตริย์, สิริกิติ์, รามเกียรติ์, สุรเกียรติ์) ฯลฯ ซึ่งกรณีเหล่านี้จะจัดการด้วยพจนานุกรมคำยกเว้น (exception dictionary) เพราะการพยายามรวมในกฎจะทำให้เกิด noise เป็นคำอ่านที่ผิดเจือปนเข้ามาโดยไม่จำเป็นสำหรับกรณีทั่วไป

โดยปกติ thpronun จะมีพจนานุกรมคำยกเว้นมาให้อยู่แล้ว แต่คุณก็สามารถสร้างพจนานุกรมคำยกเว้นเองได้ โดยดัดแปลงจากไดเรกทอรี data/ ใน source tree และใช้ตัวเลือก -d<DICTPATH> ใน command line เพื่อระบุพจนานุกรมคำยกเว้น

เนื้อหาของพจนานุกรม จะ map จากตัวสะกดคำไทยไปเป็นคำอ่านในรูป raw code ซึ่งเป็นรูปแบบภายในของโปรแกรมที่แสดงจากตัวเลือก -w เช่น:

ทรง s_og0@3
ทรัพย์ s_ap3@6

การแบ่งคำ

ในกรณีทั่วไป การแบ่งคำข้อความก่อนแจงพยางค์ก็ช่วยลดจำนวนคำอ่านที่เป็นไปได้ลงได้มาก โดยปกติ thpronun จึงเรียกใช้ LibThai เพื่อแบ่งคำก่อน แต่ในบางกรณี การแบ่งคำก็อาจตัดคำอ่านที่ควรจะเป็นออกไปได้ เช่น ในกรณีของคำที่ไม่อยู่ในพจนานุกรมของ LibThai เอง ในกรณีเช่นนั้น คุณก็สามารถปิดการแบ่งคำก่อนได้ โดยใช้ตัวเลือก -n ใน command line

ป้ายกำกับ:

23 สิงหาคม 2561

LibThai 0.1.28 and its Consequences

บันทึกการเปลี่ยนแปลงต่าง ๆ ที่มาใน LibThai 0.1.28 และผลพวงทั้งหลายหลังจากนั้น

LibThai 0.1.28 ออกไปตั้งแต่ต้นเดือน โดยรุ่นนี้มีรายการเปลี่ยนแปลงสำคัญ ๆ คือ:

  • แก้ปัญหาขาด header <thai/thwchar.h> ใน header ที่เกี่ยวกับฟังก์ชัน wide char หลาย ๆ ตัว เช่น thwbrk.h, thwcoll.h ฯลฯ ซึ่งเป็นปัญหาที่พบระหว่างเขียนโปรแกรมตัวหนึ่งที่เรียกใช้ libthai ทำให้ต้อง include thai/thwchar.h เอง ซึ่งไม่สะดวก ในรุ่นนี้สามารถ include header ที่ต้องการแล้วเรียกใช้งานได้เลย ไม่ต้องเพิ่ม include เองอีกแล้ว
  • ปรับโค้ดให้เป็นไปตาม C90 (ANSI C) มากขึ้น เป็นผลพวงจากที่ได้ทำกับ libdatrie 0.2.12 มาแล้ว
  • ปรับข้อมูลพจนานุกรมตัดคำ โดยในรุ่นนี้ได้รับความช่วยเหลือจากคุณ @nuttee15 จาก metamedia technology ที่ได้เสนอคำเพิ่มเข้ามาใน Issue #2 ที่เปิดไว้รับเสนอคำใหม่ในพจนานุกรมตัดคำ

จากนั้นก็ได้ upload debian package พร้อมความเปลี่ยนแปลงอย่างอื่น คือ เพิ่ม pkg-config ให้เป็น dependency ของ libthai-dev เพื่อให้แน่ใจว่า libthai.pc จะสามารถทำงานได้แม้ในระบบที่ติดตั้งแบบเล็กที่สุด (เป็นปัญหาที่พบระหว่างที่ทำงานชิ้นหนึ่งร่วมกับ metamedia technology) และ การรองรับการ build ที่ไม่ต้องใช้ (fake)root

รายการคำจากพจนานุกรมตัดคำของ libthai ก็ได้นำไปใช้ สร้าง hyphenation pattern ที่โครงการ thailatex ซึ่งขณะนี้กลายสภาพเป็นเพียงที่พักงานพัฒนา hyphenation pattern เท่านั้น จากนั้นจึงได้เสนอ pull request สำหรับ update hyphenation pattern สำหรับภาษาไทยในโครงการ TeX hyphenation patterns ซึ่งต้องรอการ merge เพื่อให้มีผลที่ต้นน้ำต่อไป

จาก TeX hyphenation pattern ก็ต้อง sync มายังเครื่องมือตัดคำสำหรับเอกสาร LaTeX ด้วย คือ swath ซึ่งนอกจากการปรับพจนานุกรมแล้ว ก็ได้ปรับโค้ดเล็ก ๆ น้อย ๆ ตามที่เคยทำในทุกรุ่นที่ผ่านมา โดยสิ่งที่ทำในรุ่นนี้คือ:

  • ทดลอง build โดยใช้ CFLAGS -Wall แล้วแก้ warning ต่าง ๆ
  • จากการแก้ warning ที่พบในโค้ดส่วน RTF filter ทำให้ตรวจพบความผิดปกติใน method หนึ่งที่ตัวฟังก์ชันทำงานตรงข้ามกับชื่อ คือ RTFToken::isEmpty()

    โค้ดส่วนจัดการ RTF นี้ ไม่ค่อยมีใครเรียกใช้ ความจริงผมเคยเสนอจะตัดทิ้งไปแล้ว แต่ด้วยความช่วยเหลือของคนในชุมชน (คุณวิทยา ไตรสารวัฒนะ) ทำให้ได้วิธีทดสอบความถูกต้องของโปรแกรม จึงยังคงเก็บไว้ แต่เนื่องจากเวลานั้น swath ยังไม่เริ่มทำ TDD จึงยังไม่ได้ใส่ test case ไว้ใน source tree

    เพื่อจะตรวจแก้ฟังก์ชันที่สงสัยนี้ ผมจึงต้องไปดึงเอกสารตัวอย่างมาจาก thread เก่าที่ว่า แล้วนำมา เพิ่ม test case เสียก่อน หากคุณสงสัยว่าทำไม source tarball ของ swath รุ่นนี้ถึงโตขึ้นจนผิดสังเกต มันก็มาจากเอกสาร RTF ทดสอบนี้นี่เอง

    จากนั้น จึงได้ แก้ฟังก์ชันที่สงสัย นั้น แล้วรัน test เปรียบเทียบ output ดูโดยใช้ LibreOffice ปรากฏว่าเป็นการแก้ที่ถูกต้องแล้ว เพราะมันทำให้ได้จุดตัดคำครบถ้วนสมบูรณ์ขึ้น

    ไม่ว่าจะมีใครใช้หรือไม่ก็ตาม แต่คอมไพเลอร์ยังคงคอมไพล์มันอยู่ และนำผมเข้ามาเจอและแก้บั๊กจนได้

  • แก้ warning อื่น ๆ และทำความสะอาดโค้ดเล็ก ๆ น้อย ๆ
  • สุดท้าย มี pull request ของคุณ @pepa65 ที่เสนอไว้นานแล้ว เพื่อร่างแฟ้ม INSTALL ที่อธิบายความแตกต่างของวิธี build swath จาก git กับจาก released tarball ซึ่งผมก็เห็นว่ามีประโยชน์กว่าแฟ้ม INSTALL ที่ GNU automake มันเติมให้แบบอัตโนมัติ จึง merge เข้ามาเสีย

แล้วก็ออกรุ่น swath 0.6.1 ตามมาด้วย Debian upload ซึ่งก่อนอัปโหลดก็ได้ปรับ branch layout ของ swath packaging ตาม DEP-14 ด้วย

ป้ายกำกับ: , ,

21 สิงหาคม 2561

DEP-14 Note

บันทึกเตือนความจำสำหรับการทำตาม DEP-14: Recommended layout for Git packaging repositories เพื่อนำไปใช้ทำกับแพกเกจอื่น ๆ ในความดูแลของผมต่อไป

สำหรับ Debian package ปกติที่จัดการเวอร์ชันบน Git และใช้ git-buildpackage ในการ build นั้น จะมี branch layout ดังนี้:

master
pristine-tar
upstream
  • master เป็น branch หลักที่มีทั้ง upstream source tree และ Debian control files ครบสำหรับ build deb
  • upstream เป็น branch ที่เก็บ upstream source tree ซึ่ง maintainer จะ import source รุ่นใหม่เข้าที่นี่ก่อน (ถ้ามีการ repack เพื่อให้สอดคล้องกับ DFSG ก็ repack ก่อน import) แล้วจึง merge เข้าที่ master เพื่อเป็นการปรับรุ่น upstream ของ deb เอง
  • pristine-tar เป็น branch สำหรับ regenerate binary tarball รุ่นต่าง ๆ ของ upstream เพื่อใช้เป็นไฟล์ *.orig.tar.[gz|bz2|xz] โดยเก็บเฉพาะ delta ระหว่างรุ่นต่าง ๆ เพื่อให้ใช้เนื้อที่อย่างมีประสิทธิภาพสูงสุด

ศึกษารายละเอียดเพิ่มเติมได้ที่:

DEP-14 ได้แนะนำให้ใช้ branch layout แบบใหม่ เพื่อให้ Debian derivatives ต่าง ๆ ทำงานได้สะดวกขึ้น พร้อมกับเป็นการเตรียมการสำหรับเครื่องมือสร้างแพกเกจบน Git ต่าง ๆ ที่จะมีต่อไปในอนาคต

สำหรับแพกเกจใน Debian เอง โดยสรุปแล้วจะมีการเปลี่ยนแปลง branch layout ภาคบังคับอยู่ 2 รายการ คือ:

  • master เปลี่ยนเป็น debian/master
  • upstream เปลี่ยนเป็น upstream/latest

ทำให้มี branch layout ขั้นต่ำคือ:

debian/master
pristine-tar
upstream/latest

ที่เหลือก็ขึ้นอยู่กับความเปลี่ยนแปลงที่เกิดขึ้นกับแพกเกจ เช่น:

  • ถ้ามีการ update ใน stable release (เช่น security update, proposed update, backport) ก็ทำใน branch debian/jessie, debian/wheezy, debian/wheezy-backports ฯลฯ แล้วแต่กรณี
  • ถ้ามี experimental upload ก็ทำใน branch debian/experimental ซึ่งเป็น branch ชั่วคราวจนกว่าจะ merge เข้า debian/master หรือถ้าแพกเกจไหนมี experimental upload คู่ขนานกับ stable upload อยู่เป็นนิตย์ ก็อาจจะแทน branch debian/master ด้วย debian/sid แล้วก็ไม่ต้องลบ branch debian/experimental เลยก็ได้
  • ถ้าต้อง update upstream ขนานกันหลาย branch ก็อาจจะใช้ upstream/latest เก็บ development release ล่าสุด และสร้าง branch เช่น upstream/1.2.x ไว้เก็บ upstream รุ่น 1.2.*

สำหรับแพกเกจที่ผมดูแล ส่งตรงจาก linux.thai.net ซึ่งที่ผ่านมามีแต่ release เป็นเส้นตรง ไม่มีแยกแขนง ก็มักไม่มีความซับซ้อนอะไร (ยกเว้นตอนที่มี security update) สิ่งที่ต้องทำในตอนนี้จึงมีเพียง 3 ขั้นตอน:

  1. เปลี่ยนชื่อ branch upstream เป็น upstream/latest
  2. เปลี่ยนชื่อ branch master เป็น debian/master
  3. เพิ่มไฟล์ debian/gbp.conf เพื่อระบุให้ใช้ branch ชื่อใหม่

เปลี่ยนชื่อ branch upstream

การเปลี่ยนชื่อ Git remote branch สามารถทำได้ตามขั้นตอนดังนี้

  1. เปลี่ยนชื่อ local branch
    $ git branch -m upstream upstream/latest
    
  2. ลบ remote branch upstream
    $ git push origin :upstream
    
  3. push local branch ชื่อใหม่ไปที่ remote
    $ git push --set-upstream origin upstream/latest
    

เปลี่ยนชื่อ branch master

โดยหลักการแล้วก็ทำเหมือนตอนเปลี่ยนชื่อ branch upstream นั่นแหละ แต่จะมีความไม่ตรงไปตรงมานิดหน่อย

  1. เปลี่ยนชื่อ local branch
    $ git branch -m master debian/master
    
  2. ลบ remote branch master

    ขั้นตอนนี้แหละที่ tricky ที่สุด เพราะ คุณกำลังจะลบ default branch (master) ออกจาก repository!

    ถ้าคุณใช้ Salsa ล่ะก็ hook script ของมันจะปฏิเสธไม่ให้ลบ default branch ผ่านการ push เลยทีเดียว คุณต้องทำผ่าน web interface ดังนี้

    1. เปลี่ยน default branch ไปที่อื่นก่อน โดยใช้เมนู Settings > General > Default Branch
    2. ลบ branch master โดยใช้เมนู Repository > Branches สังเกตว่า branch master จะถูก protect ไว้ ไม่สามารถลบผ่านการ push ได้ แต่ใช้ web interface ลบได้ โดยมันจะถามยืนยันก่อนลบ
  3. push local branch ชื่อใหม่ไปที่ remote
    $ git push --set-upstream origin debian/master
    
  4. เปลี่ยน default branch ให้ชี้มาที่ debian/master โดยใช้เมนู Settings > General > Default Branch

เพิ่มไฟล์ debian/gbp.conf

คุณอาจจะต้อง clone Git repository ใหม่อีกครั้ง เพื่อให้ค่าต่าง ๆ ของ origin ตรงกับของ remote (เช่น HEAD)

จากนั้น เพิ่มไฟล์ debian/gbp.conf ที่มีเนื้อหาดังนี้:

[DEFAULT]
pristine-tar = True
debian-branch = debian/master
upstream-branch = upstream/latest

ก็จะสามารถ build package ด้วย git-buildpackage ได้ตามปกติผ่าน branch layout ตาม DEP-14

ป้ายกำกับ:

12 กรกฎาคม 2561

Midyear Thanks

ขอขอบคุณ ทุกท่านที่ได้ สนับสนุน งานพัฒนาซอฟต์แวร์เสรีของผมในช่วงครึ่งปีที่ผ่านมาครับ

ขอขอบคุณผู้สนับสนุนดังนี้:

  • เดือนกุมภาพันธ์ 2560
    • ผู้ไม่ประสงค์จะออกนาม
  • เดือนมีนาคม 2560
    • ผู้ไม่ประสงค์จะออกนาม
  • เดือนเมษายน 2560
    • ผู้ไม่ประสงค์จะออกนาม
    • คุณวิทยา ไตรสารวัฒนะ
  • เดือนพฤษภาคม 2560
    • ผู้ไม่ประสงค์จะออกนาม
  • เดือนมิถุนายน 2560
    • ผู้ไม่ประสงค์จะออกนาม
    • คุณวิทยา ไตรสารวัฒนะ
  • เดือนกรกฎาคม 2560 (นับถึงวันที่ 12)
    • ผู้ไม่ประสงค์จะออกนาม

ขอให้ทุกท่านมีความเจริญก้าวหน้าในกิจที่ทำอยู่ครับ May the Source be with you!

สรุปกิจกรรมเกี่ยวกับซอฟต์แวร์เสรีในครึ่งแรกของปีนี้ของผม:

  • งานแปล VLC ทยอยทำยามว่างในช่วง 3 เดือนแรก
  • ย้าย/ซ่อม Debian mirror ที่ debianclub.org โดยมีลำดับเหตุการณ์ดังนี้:
    • ได้รับแจ้งจาก EMBES Technology ซึ่งเป็น sponsor ของเราว่าจะเลิกเช่า hosting จนกระทั่งได้ sponsor ใหม่ คือคุณวิทยา ไตรสารวัฒนะ จึงได้อาศัยคุณณัฐนันท์ (EMBES) ช่วยย้ายเครื่องเซิร์ฟเวอร์ไปที่ host ใหม่ (แจ้งปิดบริการ และ เปิดบริการ)
    • เกิดปัญหาฮาร์ดดิสก์เต็ม จึงได้ทำ poll สำรวจ arch ที่ผู้ใช้ใช้งานจริง เพื่อทำ partial mirror ซึ่งก็ตัดสินใจทำ mirror เฉพาะ source, amd64, arm64, armel, armhf, i386, mipsel, powerpc, sparc
    • ได้รับบริจาคฮาร์ดดิสก์ 8 GB จากอดีตผู้ดูแล ftp.th.debian.org เพื่อให้รองรับ full mirror ได้อีกครั้ง จึงขอรบกวนคุณณัฐนันท์ช่วยเป็นธุระเอาฮาร์ดดิสก์เข้าไปติดตั้งอีกครั้ง แล้วจัดการ remote เข้าไปติดตั้งระบบใหม่ และถ่ายโอนข้อมูลจากฮาร์ดดิสก์เก่ามาฮาร์ดดิสก์ใหม่ โดยในครั้งนี้ได้ถือโอกาสเปลี่ยน web server จาก Apache มาเป็น NGiNX ด้วย (แจ้งปิดบริการ และ เปิดบริการ)
    • เกิดปัญหาเสถียรภาพของระบบ โดยเครื่องจะ hang เป็นระยะ ต้องรบกวนเจ้าหน้าที่ NOC เข้าไปกดรีเซ็ตที่ตัวเครื่องวันละหลายรอบ พยายามตรวจสอบหาสาเหตุอยู่หลายสัปดาห์ก็ได้ข้อสรุปว่าต้องอัปเกรดตัวเครื่องเพื่อให้รองรับฮาร์ดดิสก์ 8 GB ได้ จนในที่สุดคุณวิทยาก็ประสานงานจัดเตรียมเครื่องเซิร์ฟเวอร์มาให้จนได้ พร้อมกันนั้น คุณภานุวัฒน์แห่ง ServeNET ได้จัดเตรียม VM สำหรับให้บริการ เว็บไซต์ debianclub แยกต่างหากจากตัว mirror และเมื่อตัวเครื่อง mirror พร้อมก็ sync mirror ต่าง ๆ จนเสร็จ แล้วจึง เปิดบริการ mirror ตามมา
    • ระหว่างวันที่ 1-6 ก.ค. เครื่อง mirror เกิดขาดการติดต่อโดยไม่ทราบสาเหตุ (ผมพยายามสอบถามเจ้าหน้าที่ NOC ก็ยังไม่ได้รับคำตอบ) ซึ่งมีผลข้างเคียงคือ debianclub ถูกตัดออกจาก Debian mirror list หลังจากนั้น เมื่อเครื่อง mirror กลับมาทำงานแล้ว ผมก็ยัง maintain mirror ต่อไปตามปกติ แต่ยังไม่ได้ register กลับเข้า Debian mirror list อีก เพราะต้องการความแน่ใจเสียก่อนว่าเครื่องจะไม่ down โดยไม่ทราบสาเหตุเช่นนี้อีก ระหว่างนี้ก็ให้บริการอยู่นอก list ไปก่อน
  • Update แผนที่ OSM ในบริเวณตัวเมืองขอนแก่นตามโอกาส
  • โครงการอักษรอีสาน:
  • libdatrie: ทำและออกรุ่น 0.2.11 และ 0.2.12
  • Fonts-TLWG: แก้ปัญหา ligature ใน Laksaman และเรื่องอื่น ๆ และออกรุ่น 0.6.5
  • Debian package ทั้งหลาย:
  • ปรับคำแปล Xfce ยามว่างในช่วงเดือนหลัง ๆ

ป้ายกำกับ: ,

19 มิถุนายน 2561

libdatrie 0.2.12

libdatrie 0.2.12 ออกแล้ว ไล่หลัง 0.2.11 เพียง 2 เดือน หลังจากผ่านการทำความสะอาดโค้ดมาระยะหนึ่ง

เริ่มจากผมได้รับแพตช์จากคุณ Peter Moulder ทางเมลส่วนตัว ซึ่งเสนอปรับแก้ 3 รายการคือ:

  • แก้การประกาศฟังก์ชันที่ไม่รับพารามิเตอร์ด้วยรูปแบบ f(void) แทน f() เนื่องจากรูปแบบ f() เป็นเพียงการประกาศแบบ K&R ซึ่งไม่ระบุข้อมูลเกี่ยวกับพารามิเตอร์ ซึ่งในการ call จริงอาจใส่อาร์กิวเมนต์ใด ๆ มาก็ได้ ในขณะที่แบบ f(void) จะแสดงความชัดเจนว่าไม่รับพารามิเตอร์ใด ๆ
  • เปลี่ยนการกำหนดชนิด Bool ซึ่งเดิมกำหนดเป็น enum TRUE, FALSE ซึ่งจะไปชนกับค่าในซอร์สโค้ดอื่นได้ง่ายมาก จึงเปลี่ยนชื่อหลบแล้ว define TRUE, FALSE แบบมีเงื่อนไขเอา
  • ตัด typedef ชนิด byte, word, dword ที่ไม่มีการใช้งานใน libdatrie แต่มีโอกาสชื่อชนกับซอร์สอื่นที่ไปใช้ร่วมสูงมาก

นั่นนำไปสู่การตรวจสอบหาประเด็นอื่น ๆ ต่อ โดยใช้ CFLAGS="-ansi -pedantic -Wall" รวมถึงการใช้ CC=clang ด้วย ทำให้จับประเด็นเพิ่มได้อีกหลายรายการ หนึ่งในนั้นมีผลข้างเคียงเป็นการป้องกันการตัดท้ายชื่อพาธของพจนานุกรมที่ยาวมาก ๆ ด้วย

เหลือประเด็นที่ยังคงไว้ คือการใช้ "%ls" ใน printf() format string เพื่อพิมพ์ข้อความยูนิโค้ดที่อยู่ในรูป wide char string ซึ่งไม่มีใน C90 โค้ดนี้เป็นโค้ดที่อยู่ใน test ในส่วนแสดงข้อมูลการทำงานของ test เท่านั้น ไม่ได้อยู่ในโค้ดของ library และไม่ใช่ส่วนสำคัญที่ชี้ขาดผลการ test จึงปล่อยไว้อย่างนั้น เพราะหากพยายามทำให้ได้ตาม C90 จริง ๆ จะยุ่งยากเกินจำเป็น

ตามมาด้วย Debian upload ซึ่งเพิ่ม pkg-config เข้าใน dependency ของ libdatrie-dev เพื่อประกันว่า datrie-0.2.pc สามารถทำงานได้แม้ในระบบที่ติดตั้งแบบ minimal จริง ๆ

ป้ายกำกับ:

25 เมษายน 2561

Fonts-TLWG 0.6.5

Fonts-TLWG 0.6.5 ออกแล้ว โดยมีความเปลี่ยนแปลงที่สำคัญคือการแก้บั๊กของฟอนต์ Laksaman เมื่อใช้กับเอกสาร LaTeX โดยผมได้รับรายงานปัญหานี้จาก อ. กิตติพิชญ์ มีสวาสดิ์ ในการประชุม โสเหล่ ของ KKLUG เมื่อเดือนมีนาคมที่ผ่านมา

อาการคือ คำที่มี ff, ffi, ffl จะไม่มี ligature สามชุดนี้ปรากฏ ผมสร้างเอกสารทดสอบ โดยในข้อความแต่ละชุด บรรทัดแรกจะป้อนข้อความปกติ ส่วนบรรทัดที่สองจะเลี่ยง ligature:

if iff film flow difficult affluent

if if{}f f{}ilm f{}low dif{}f{}icult af{}f{}luent

ผลลัพธ์คือ:

Laksaman bug on LaTeX

สังเกต ligature ที่หายไปในบรรทัดแรกของข้อความแต่ละชุด

ในฟอนต์ต้นทาง คือ TH Sarabun New นั้น มี ligature ของละตินมาให้เพียงสองตัว คือ fi และ fl แต่ในกฎ LIGKERN ของ TeX จะใช้ทั้งหมด 5 ตัว โดยอีก 3 ตัวที่ยังขาดคือ ff ffi และ ffl เมื่อสร้าง glyph ทั้งสามตัวเพิ่มเข้าไปก็จะได้ผลลัพธ์ที่ควรจะเป็น:

Laksaman fixed for LaTeX

ผลข้างเคียงก็คือ ฟอนต์ Laksaman เมื่อใช้บนเดสก์ท็อปหรือบนเว็บจะมี ligature ครบกว่า TH Sarabun New ซึ่งความแตกต่างนี้ต้องสังเกตใกล้ ๆ อย่างละเอียดพอสมควร

ก่อนแก้:

Laksaman on Firefox, before

หลังแก้:

Laksaman on Firefox, after

ขอขอบคุณ อ. กิตติพิชญ์ ผู้ใช้ LaTeX ตัวจริงคนหนึ่งมา ณ ที่นี้ ที่จับบั๊กนี้ได้ครับ

นอกจากนี้ยังมีความเปลี่ยนแปลงอื่น ๆ คือ:

  • แปลงแฟ้มต้นฉบับของฟอนต์เป็นฟอร์แมตของ Fontforge รุ่นล่าสุด
  • ปรับ configure script ให้ตรวจรุ่นของ fontforge ตามรูปแบบเลขรุ่นที่เปลี่ยนไป จากแบบตัวเลขมาเป็นแบบวันที่
  • ย้ายที่ติดตั้งแฟ้ม fontconfig template ทั้งหลาย จาก /etc/fonts ไปไว้ที่ /usr/share/fontconfig ตามการเปลี่ยนแปลงใน fontconfig ล่าสุด
  • ปรับค่า TTFWeight ของฟอนต์น้ำหนักปกติทุกฟอนต์ให้เป็นค่า 400 (Regular) ทั้งหมด เนื่องจากบางฟอนต์ที่ยังมีค่าเป็น 500 (Medium) นั้น ทำให้แอปบางตัว (เช่นที่รายงานใน Issue #5 คือ EditPad) จัดฟอนต์ให้ไปอยู่ในกลุ่มตัวหนา

ได้อัปโหลดฟอนต์รุ่นใหม่ขึ้น CTAN ไว้แล้ว รอสักระยะถึงจะมาถึงดิสโทรต่าง ๆ เพื่อให้ใช้กับเอกสาร LaTeX ได้

ส่วนบนเดสก์ท็อปนั้น ก็ได้อัปโหลด 1:0.6.5-1 เข้า Debian เรียบร้อยแล้วครับ

ป้ายกำกับ: ,

23 เมษายน 2561

libdatrie 0.2.11

libdatrie 0.2.11 ออกแล้ว หลังจากที่ไม่ได้ออกรุ่นมาเลยถึงสองปีครึ่ง และนับเป็น release แรกของ libdatrie ที่ออกจาก github หลังจากที่ปล่อยให้แพกเกจอื่น ๆ ทยอยออกรุ่นกันไปเยอะแล้ว

รุ่นนี้มีความเปลี่ยนแปลงที่สำคัญนอกเหนือจากการย้ายมา Github คือ:

  • ตรวจสอบ error จาก iconv() ได้ถูกต้องยิ่งขึ้น จากรายงานของ Daniel Macks ใน Issue #3
  • ปรับคำบรรยายแพกเกจใน README เนื่องจาก @flackbash ทักท้วงมาใน Issue #4 ว่า complexity ของการ search ของ trie เป็น O(m) เมื่อ m คือความยาวของคีย์ ไม่ใช่ O(1) เสียหน่อย ซึ่งอันที่จริง การอ้าง O(1) ก็เป็นเพียงการบอกว่า เวลาที่ใช้มันไม่ขึ้นกับขนาดของฐานข้อมูลเท่านั้น ในทำนองเดียวกับที่ hash table มักจะอ้าง compexity O(1) เหมือนกัน ทั้ง ๆ ที่เวลาที่ใช้ในการคำนวณค่า hash โดยปกติก็เป็น O(m) อย่างไรก็ดี ถูกทักท้วงมาเช่นนี้ก็แก้ไขตามนั้น แต่ยังคงเน้นว่ามันไม่ขึ้นกับขนาดของฐานข้อมูล
  • ปรับแก้การทำงานของ trie_state_get_data() ซึ่งเดิมทำงานไม่ถูกต้องในกรณีที่คีย์ที่ใช้ค้นนั้นเป็นส่วนต้นของคีย์อื่น (เช่น กา เป็นส่วนต้นของ การ) ซึ่งทำให้โครงการ pytries หันไปใช้วิธีเพิ่มฟังก์ชันใหม่ โดยส่งแพตช์มาให้ผมในเมลส่วนตัว ผมจึงแสดงความเห็นไปว่าน่าจะปรับแก้ฟังก์ชันเดิมให้ทำงานให้ถูกต้อง แทนที่จะเพิ่มฟังก์ชันใหม่ เรื่องเงียบไประยะหนึ่ง จนกระทั่งมีการผลักดันแพกเกจ python-datrie เข้า Debian (ITP) Andreas Tille จึงหันมากระทุ้งผมเรื่องนี้อีกครั้ง (Debian #894278) จนในที่สุดผมก็เลยแก้ปัญหานี้ (commit) และ Filip Pytloun ก็ได้ช่วยทดสอบให้ และปรับแก้ที่ pytries ให้
  • แก้ปัญหา segfault เมื่อใช้งานกับ alpha map แบบเต็มช่วง (0-255) โดย Xiao Wang รายงานใน Issue #6 ว่าเขาพบเมื่อใช้งานกับ PHP ซึ่งผมยังไม่มีเวลาไปตรวจสอบ จึงทิ้งช่วงมาจนกระทั่ง @nevermatch เข้ามาวิเคราะห์และชี้จุดผิดพลาดให้ ซึ่งผมก็ยังคง reproduce ไม่ได้อยู่ดี คือมันไม่ segfault แต่พบความผิดปกติตามที่เขาชี้จริง จึงถือว่ามีข้อมูลเพียงพอที่จะแก้ปัญหาได้แล้ว ซึ่งต้องขอขอบคุณทั้งสองท่านอย่างมากที่ช่วยตรวจสอบปัญหานี้ให้

ถือว่ารุ่นนี้ได้รับ contribution จากผู้ใช้ค่อนข้างหลากหลาย โดยเฉพาะการช่วยวิเคราะห์ปัญหาที่น่าประทับใจ และได้รู้เพิ่มเติมว่ามีการใช้งาน libdatrie กับ PHP ด้วย

และเช่นเคย upload 0.2.11-1 เข้า Debian เรียบร้อยแล้ว พร้อมความเปลี่ยนแปลงอื่น ๆ เพิ่มเติม เช่น การรองรับการ build ที่ไม่ต้องใช้ (fake)root

ป้ายกำกับ:

hacker emblem