ฟอนต์

รวมหมวดฟอนต์

เอาไฟล์มาสรุปให้ดาวน์โหลดได้ง่าย ๆ

ฟอนต์ที่ทดลองทำเอง
เช่น Verachart เป็นต้น ดาวน์โหลดเฉพาะไฟล์ ttf ttfonts.tar.gz
เมื่อแตกไฟล์แล้ว เอาไฟล์ ttf ทั้งหมดไปใส่ไว้ภายใต้ ~/.fonts หรือ /usr/share/fonts/truetype และ
ทำลิงก์ไฟล์ 65-z-ttfonts.conf ไปยัง /etc/fonts/conf.d
$ sudo ln -sf 65-z-ttfonts.conf /etc/fonts/conf.d
$ fc-cache -r

ฟอนต์ทดแทนฟอนต์วินโดวส์
คือ Angsima, Bromlila และ Corada ดาวน์โหลดเฉพาะไฟล์ ttf thaifont-abc.tar.gz
เมื่อแตกไฟล์แล้ว เอาไฟล์ ttf ทั้งหมดไปใส่ไว้ภายใต้ ~/.fonts หรือ /usr/share/fonts/truetype และ
ทำลิงก์ไฟล์ 65-z-thaifont-abc.conf และไฟล์ 90-1-thaifont-abc-synthetic.conf ไปยัง /etc/fonts/conf.d

$ sudo ln -sf 65-z-thaifont-abc.conf /etc/fonts/conf.d
$ sudo ln -sf 90-1-thaifont-abc-synthetic.conf /etc/fonts/conf.d
$ fc-cache -r

ฟอนต์ของ DejaVu ที่เติมภาษาไทยเข้าไป

***update*** ฟอนต์รุ่นใหม่ของ ltn มีการทดแทนฟอนต์ Browallia ด้วย Garuda อยู่แล้ว (กำหนดเลขไฟล์เป็น 90-XXX) หากไม่ต้องการใช้ชุดทดแทนของฟอนต์นี้ ให้เปลี่ยนชื่อไฟล์ 90-1-thaifont-abc-synthetic.conf เป็นตัวเลขที่มากกว่า 90 เช่น 95-1-thaifont-abc-synthetic.conf เป็นต้น

ดาวน์โหลดแบบรวมซอร์ส sfd ด้วย

บันทึกเกร็ดฟอนต์ของเดเบียน
  • ไฟล์คอนฟิกของฟอนต์ในเดเบียนอยู่ที่ /etc/fonts/conf.d ซึ่งเดเบียนบรรจุไฟล์จริงไว้ที่ /etc/fonts/conf.avail แล้วใช้โยงลิงก์มาที่ conf.d อีกทีนึง
  • ถ้ามีชื่อฟอนต์ซ้ำกันในไฟล์คอนฟิก ชื่อไฟล์ที่มาทีหลังจะมีความสำคัญน้อยกว่าชื่อไฟล์ที่มาถึงก่อน เช่น 65-1-ttfonts.conf จะสำคัญมากกว่า 65-2-ttfonts.conf
    ดังนั้นถ้าจะ override ฟอนต์ของระบบ จะต้องตั้งให้ชื่อให้เรียงอยู่ก่อน
บันทึกเกร็ดฟอนต์ทดแทน
  • ถ้าใช้ฟอนต์ทดแทนฟอนต์วินโดวส์ (Angsima, Bromlila, Corada) บน openoffice เราสามารถกำหนดเป็น style โดยกำหนดฟอนต์ดังนี้ เช่น สไตล์ Normal กำหนดเป็น
    Angsima; Angsana New

    ทั้งในหมวด Western text font และ CTL font
    จะให้ผลที่แน่นอนกว่าการใช้การทดแทนฟอนต์ใน openoffice เอง นอกจากนี้ยังสามารถเปิดใช้งานทั้งใน Microsoft Word และ OpenOffice Writer ได้โดยไม่เกิดปัญหาครับ

ubuntu: แปลงฟอนต์วินโดวส์

จำเป็นต้องใช้ฟอนต์เก่าจากวินโดวส์มาทำอาร์ตเวิร์ก จึงต้องเตรียมฟอนต์เก่าให้สามารถแสดงผลในลินุกซ์ได้
ขั้นตอนคร่าว ๆ มีดังนี้

  • ติดตั้ง FontForge
    $ sudo aptitude install fontforge
  • ดาวน์โหลดซอร์สโค๊ดของฟอนต์ไทย เพื่อเอาไฟล์ sfd มาทำเป็นต้นแบบ - ไฟล์นามสกุล sfd เป็นไฟล์ข้อมูลของโปรแกรม FontForge
  • แตกไฟล์ และนำเอาไฟล์ Loma.sfd จากไดเรคทอรี่ ./thaifonts-scalable-0.4.5/nectec มาทำเป็น template
    $ cp ./thaifonts-scalable-0.4.5/nectec template.sfd
  • เปิดไฟล์ template
    $ fontforge template.sfd
  • Merge เทมเพลตเข้ากับไฟล์ฟอนต์ที่จะแก้ไข ใช้เมนู Element -> Merge Font... จะได้ชุดอักษรจากฟอนต์เก่ามาทับบนเทมเพลต
  • ตั้งชื่อฟอนต์โดยใช้เมนู Element -> Font Info...
  • ตั้ง Encode เป็น Unicode BMP โดยใช้เมนู Encoding -> Reencode -> ISO 16041-1 (UNICODE, BMP)
  • ย้ายชุดอักษรเก่าให้มาทับบนตำแหน่งของยูนิโค๊ด โดยเริ่มที่ชุดของภาษาอังกฤษคือ SPACE (0x0020) จนถึง TILDE(~ = 0x007E) และชุดของภาษาไทยคือ ก.ไก่ (0x0E01) จนถึง ฟองมัน (0x0E5B) และชุดเศษที่เหลือคือ เริ่มตั้งแต่ตำแหน่ง 0xF700 เป็นต้นไป โดยเทียบจากชุดของเทมเพลต (ใช้วิธี cut&paste ธรรมดา ใช้คีย์ ctrl+alt+[ และ ] ในการเลื่อนหาชุดฟอนต์)
  • แก้ไขข้อมูลในชุดสระ คือ อั อิ อี อึ อื อุ อู อฺ อ็ โดยแก้เพียงชุดแรกในช่วง 0x0E01 ถึง 0x0E5B เท่านั้น โดยเติม anchor ตามตัวอย่างจากเทมเพลต ด้วยเมนู คลิกขวา -> Add anchor
  • ตรวจดูความเรียบร้อย แล้วสั่งผลิตฟอนต์จากเมนู File -> Generate Fonts...

ฟอนต์ที่ได้จะสามารถใช้ได้กับลินุกซ์ที่ส่วนใหญ่จะใช้รหัสฟอนต์เป็นยูนิโค๊ด และมีความสามารถในการแก้สระลอยได้ในตัวเอง

หมายเหตุ

  • อาจเลือก AutoHint ได้ตามความเหมาะสม

ลองทำฟอนต์ทดแทน ๑ - Angsima

ช่วงนี้กำลังทดลองเอา OpenOffice บนลินุกซ์ มาใช้แทน Microsoft Office บนวินโดวส์ ได้ผลดีพอควร แต่มาติดปัญหาสำคัญคือฟอนต์ในเอกสารเก่าจำนวนมาก เป็นฟอนต์บนวินโดวส์ คือ Angsana New และ Browallia New
ถ้าจะแก้ ก็ต้องแก้กันเป็นจำนวนมาก จึงคิดว่าถ้าเราทำฟอนต์ใหม่ทดแทน Angsana ให้สามารถเปิดเอกสารเก่าที่เป็นฟอนต์ Angsana โดยไม่เสียรูปแบบ น่าจะเป็นทางออกที่ดีกว่า
จึงทดลองนำฟอนต์นรบุตรมาแปลงขนาดความกว้างตัวอักษรให้เท่ากับ Angsana ตั้งชื่อว่า Angsima (อังสิมา) แปลว่าอะไรก็ไม่รู้ แต่ตั้งชื่อให้คล้าย เพื่อให้เวลาเราเลือกฟอนต์ รายชื่อจะได้อยู่ใกล้ ๆ กัน
ทดลองรุ่นแรก แบบหยาบ ๆ ได้ผลคือ

ภาษาไทยได้แล้ว แต่ภาษาอังกฤษยังไม่ได้
( ทำส่วนโค้งเป็นแบบ quadratic เพราะเริ่มถนัดแล้ว และสามารถแปลงเป็น cubic ได้โดยไม่สูญเสียตำแหน่ง )

ดาวน์โหลดซอร์ส sfd

(ไฟล์ ttf นำไปไว้ใน หน้ารวม Fonts)

ถ้าต้องการทำเป็นฟอนต์ทดแทนฟอนต์อังศณา ในไฟล์คอนฟิกของฟอนต์ ต้องเติมท่อนนี้ลงไปด้วย
$ sudo vi /etc/fonts/65-thai-XXX.conf

...
         <alias>
            <family>Angsana New</family>
            <accept>
                <family>Angsima</family>
            </accept>
        </alias>
...

ท่อนนี้เอาตัวอย่างมาจาก http://lists.freedesktop.org/archives/fontconfig/2006-June/002326.html

ถึงตอนนี้เรียกว่ายังไม่เสร็จ แต่ก็พอใช้งานได้ แต่จะพยายามทำต่อให้ใช้งานได้จริง ๆ ครับ

update

  • 50-06-06 ทำ Angsima แบบปกติเสร็จแล้ว แก้(ลบ) Kerning ของต้นฉบับอักษรภาษาอังกฤษ ปรับปรุงเลขไทยให้เหมือนต้นแบบ ปรับระยะห่างระหว่างบรรทัดเรียบร้อย แต่ยังไม่ได้ทดสอบแบบละเอียด
  • 50-06-07 ทำ Angsima-Bold เสร็จแบบหยาบ ๆ
  • 50-06-08 Angsima-Normal ปรับละเอียดเสร็จแล้ว
  • 50-06-09 สร้างเส้นใหม่-ปรับละเอียด Angsima-Bold เสร็จ
  • 50-06-09 ทำ Oblique และ BoldOblique ด้วยเมนู Element -> Transformation -> Transform -> skew แบบอัตโนมัติทั้งตาราง แต่ยังไม่ได้ทดสอบ

ลองทำฟอนต์ทดแทน ๒ - Bromlila

ฟอนต์ตัวที่สองตั้งชื่อว่า บรมลีลา - Bromlila
จุดประสงค์จะใช้แทน Browallia (โดยไม่เสียรูปแบบ)

ใช้ฐานจากฟอนต์ Garuda ยกเว้นตัวหนาที่เป็นภาษาอังกฤษ ตัว Garuda-Bold ยังไม่หนาเท่า Browallia ผมจึงเอามาจากตัวหนา sans ของ Freefont แทน

มีข้อสังเกตุคือ ตัว Browallia กับ Garuda เมื่อปรับสัดส่วนแล้ว มีลายเส้นที่แทบจะนาบกันสนิท

เที่ยวนี้ขี้เกียจทำ Screenshot เพราะตัวเหมือนกันเป๊ะ ๆ ดาวน์โหลดเลยดีกว่า

ดาวน์โหลดได้ดังนี้

  • รวมทั้งหมดในไฟล์เดียว Bromlila.tar.gz

ลองทำฟอนต์ทดแทน ๓ - Corada

ฟอนต์ตัวที่สามตั้งชื่อว่า ครดา - Corada
จุดประสงค์จะใช้แทน Cordia (โดยไม่เสียรูปแบบ)

ชื่อฟอนต์ไม่มีคำแปล เพราะพยายามให้อักขระสามตัวแรกเหมือนกัน เพื่อให้เลือกฟอนต์ได้ง่าย จำง่าย

ใช้ฐานจากฟอนต์ Loma แต่ยากหน่อยตรงที่ฐานของฟอนต์โลมาคือ FreeSerif ไม่มีขนาดความหนาแบบ Light เลยต้องเสียเวลาสร้างตัวภาษาอังกฤษแบบ Light ด้วย และใช้ขนาดปกติ (Book) ของ FreeSerif เป็น Bold ของ Corada แทน
ดังนั้นสำหรับเที่ยวนี้ ในส่วนของอักขระภาษาอังกฤษจึงไม่ค่อยเรียบร้อยเท่าที่ควร เพราะสร้างขึ้นเอง

พยายามให้หน้าตาฟอนต์ออกมาอยู่ระหว่างฟอนต์คอร์เดีย และสุดยอดฟอนต์แห่งยุค คือ ดีบีฟองน้ำ

ดาวน์โหลดได้ดังนี้

  • รวมทั้งหมดในไฟล์เดียว Corada.tar.gz

ลองทำฟอนต์ทดแทน - สรุป

สรุปงานทำฟอนต์ทดแทนฟอนต์ของวินโดวส์ทั้งสามตัว คือ

  • Angsana แทนด้วย Angsima
  • Browallia แทนด้วย Bromlila
  • Cordia แทนด้วย Corada

สร้างด้วยการปรับความกว้างของฟอนต์แต่ละอักขระ ให้เท่ากับฟอนต์วินโดวส์ และลบ kerning ของฟอนต์ต้นแบบออกให้หมด ส่วนความสูงของฟอนต์ ไม่ได้ปรับความสูงของสระให้เท่าของวินโดวส์ แต่ใช้วิธีสร้างอักขระพิเศษเพิ่มมาตัวนึง ให้จุดสูงสุดของอักขระเท่ากับไม้โทสูงของวินโดวส์ (ซึ่งสูงที่สุด) และให้จุดต่ำสุดของอักขระนี้ ให้เท่ากับสระอูที่ต่ำที่สุดของวินโดวส์
รวมทั้งสร้างไฟล์คอนฟิกให้ฟอนต์ ให้แทนชื่อฟอนต์จากวินโดวส์ตามรายละเอียดข้างบน ไฟล์นี้ตั้งชื่อว่า 65-2-thaifont-abc.conf ซึ่งจะต้องนำไปใส่ไว้ที่ไดเรกทอรี่ /etc/fonts/conf.d

เมื่อทำครบทุกอย่างแล้ว เราก็จะสามารถเปิดไฟล์ตระกูลออฟฟิศจากวินโดวส์ โดยไม่เสียรูปแบบ

สามารถดาวน์โหลดไฟล์รวมได้ดังนี้

ลองทำฟอนต์ใช้เอง

ทดลองทำฟอนต์ใช้เองโดยใช้ไฟล์ฟอนต์ Norasi.sfd ของ NECTEC เป็นต้นแบบ
จุดประสงค์เพื่อทำฟอนต์แบบ Serif ที่ใช้รหัสอักขระยูนิโค๊ด จะนำมาแทนฟอนต์ Angsana ของวินโดวส์


ตั้งชื่อว่า นรบุตร (Noraputta)
ตั้งใจให้ออกมาเป็นลูกผสมของ Norasi กับ Angsana
ยังไม่ค่อยสวยเหมือนที่มืออาชีพเขาทำกัน ฮิ้นต้งฮิ้นติ้งไม่รู้เรื่องเลย เอาแค่พอใช้งานได้ตามสไตล์
ฝากลองทดสอบและใช้ฟรีโดยไม่มีเงื่อนไขครับ

update

  • 50-04-13 ปรับมาใช้เทมเพลตของ thaifonts รุ่น 0.4.6 ทำให้ไม่มีปัญหาสระอำและวรรณยุกต์ กับ OO.o
  • 50-04-19 ทำเรื่องเลขไทยใหม่ เก็บกวาดเรื่อง Hinting แบบหยาบ ๆ
  • 50-04-20 ทำ Noraputta-Bold เพิ่ม
  • 50-04-28 แก้ปัญหาเส้นบางเกินไป ทำให้เวลาถูก Hint แล้วเส้นขาด ด้วยการเพิ่มความหนาของเส้น

ลองทำฟอนต์ใช้เอง (ต่อ) - Gentini

ลองทำ Serif อีกฟอนต์นึง เพื่อให้สมดุลย์กับ Sans serif โดยเอาต้นแบบมาจากฟอนต์ Gentium ซึ่งเป็น Serif ที่ดูสบายตา กึ่งทางการกึ่งลำลอง จึงตั้งชื่อให้คล้าย ๆ กัน ว่า เจนทินี - Gentini
ทำไปก็งงไป ทำให้ทราบว่างานออกแบบฟอนต์นี่ยากจริง ๆ ผมเองยังมือไม่ถึง จึงอ่านบุคลิกของฟอนต์ Gentium ไม่ออก เลยทำแค่ฟอนต์ขนาด book แค่ตัวเดียว

ดาวน์โหลด
ไฟล์รวม Gentini.tar.gz

ลองทำฟอนต์ใช้เอง (ต่อ) - Pantaka

ไม่ใช่งานใหม่ แต่เป็นการเอา โลมาบุตร มาชำระล้าง ปรับปรุงเล็กน้อย แต่พอทำไป ๆ มันชักเพี้ยนมาทาง อัมพุช
เลยเปลี่ยนชื่อเสียเลยดีกว่า เพื่อไม่ให้สับสน และไม่ให้ทับซ้อนกับโลมา
ตั้งชื่อว่า ปัณถกะ - Pantaka แปลว่าชำนาญทาง
ทำแล้วรู้สึกว่าอ่านง่ายกว่า วีรชาติ เลยบรรจุเข้าประจำการเป็นฟอนต์หน้าจอแทนวีรชาติ

ดาวน์โหลดไฟล์รวมดังนี้

update

  • 50-07-02 ได้ปรับปรุงวีรชาติ จนฟอนต์วีรชาติอ่านง่ายกว่าแล้ว จึงเอาฟอนต์วีรชาติกลับเข้ามาประจำการเป็นฟอนต์แบบ San-Serif แทนปัณถกะ

ลองทำฟอนต์ใช้เอง (ต่อ) - Umpush

หลังจากทำฟอนต์ Corada เสร็จแล้ว มีความรู้สึกว่าฟอนต์ Corada ตัวหนา น่าจะมาทำฟอนต์บนจอได้ เลยเอา Corada เดิม มาแปลงเป็น Light เอา Bold ทำเป็น Book แล้วสร้าง Bold ใหม่ ตั้งชื่อว่า อัมพุช - Umpush

สามารถทำเป็นฟอนต์สำหรับหน้าจอก็ได้ หรือจะออกเป็นงานพิมพ์ก็ได้
หากต้องการให้ดูทางหน้าจอภาพได้ง่ายขึ้น เพียงแค่ลดขนาดสระบน-ล่าง และวรรณยุกต์ ลงไปเป็นสัก 90% ก็จะดูทางจอภาพได้ง่ายขี้นแล้วครับ

ดาวน์โหลดได้ดังนี้

อย่าลืมว่า ฟอนต์ทุกตัวที่สร้างไว้ ถูกออกแบบไว้ด้วยการ Hint ของระบบ ดังนั้น จะต้องคอนฟิกฟอนต์ทุกครั้ง ด้วย
$ sudo vi /etc/fonts/conf.d/65-1-ttfonts.conf

<?xml version="1.0"?>
<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
<fontconfig>
    <alias>
        <family>serif</family>
        <accept>
            <family>Noraputta</family>
            <family>Angsima</family>
        </accept>
    </alias>
    <alias>
        <family>sans-serif</family>
        <accept>
            <family>Umpush</family>
            <family>Verachart</family>
            <family>Lomaputta</family>
            <family>Bromlila</family>
            <family>Corada</family>
        </accept>
    </alias>
    ...
    <match target="font">
        <test name="family"><string>Umpush</string></test>
        <edit name="autohint" mode="assign"><bool>true</bool></edit>
        <edit name="hinting" mode="assign"><bool>true</bool></edit>
    </match>
</fontconfig>

$ fc-cache -r

ถ้าไม่ประสบเหตุอื่นอีก ฟอนต์นี้คงเป็นฟอนต์สุดท้ายแล้วครับ

ลองทำฟอนต์ใช้เอง - Verachart

ทำฟอนต์หน้าจอเพิ่ม ตั้งชื่อว่า วีรชาติ เพราะเอามาจากฟอนต์ Vera ของ BitStream


ตัวบางไปหน่อย line spacing แคบไปนิด Hinting ยังแย่อยู่
ช่วยทดสอบและใช้ได้ฟรีเหมือนเดิมครับ

ดาวน์โหลดซอร์ส

(ไฟล์ ttf สามารถดาวน์โหลดที่ หน้ารวม Fonts)

การทดลองที่ไม่สำเร็จ
ทดลองทำ Truetype Instruction ด้วยเมนู AutoInstruction
แก้ไขการที่โปรแกรม Fontforge ทำ Truetype Hint ไม่สมบูรณ์ ด้วยการปรับละเอียดที่ส่วนโค้งของตัวอักษรแทน
ไม่สำเร็จเนื่องจาก คุณภาพพอดูได้แค่ 11 ปอยนต์ ที่เหลือนอกจากนี้ ดูไม่ได้เลย

  • ไฟล์ฟอนต์ Verachartti.ttf
  • ไฟล์ FontForge Verachartti.sfd
  • ไฟล์ชุดนี้ยังไม่สมบูรณ์ แต่ถ้าหากต้องการทดลองใช้งานดู ไม่จำเป็นต้องแก้ไขไฟล์ 65-ttfonts.conf เนื่องจากมีการ Hint อยู่ในฟอนต์เรียบร้อยแล้ว

update

  • 50-04-22 ปรับลดความสูงของฟอนต์ให้สมดุลย์กับภาษาอังกฤษ ใช้วิธี Hint แบบ AutoInstruction
  • 50-04-24 กลับมาใช้ Hint แบบเดิม เปลี่ยนมาให้ระบบ Hint ได้ผลดีกว่า โดยเฉพาะฟอนต์ชุดนี้ ให้ปรับไฟล์ 65-1-ttfont.conf เป็นดังนี้
    <fontconfig>
            <alias>
                    ...
                    <family>Verachart</family>
                    <default><family>san-serif</family></default>
            </alias>
            ...
            <match target="font">
                    <test name="family"><string>Verachart</string></test>
                    <edit name="autohint" mode="assign"><bool>true</bool></edit>
                    <edit name="hinting" mode="assign"><bool>true</bool></edit>
                    <edit name="hintstyle" mode="assign"><int>3</int></edit>
            </match>
    </fontconfig>
    

    จะให้ผลที่ดูนุ่มสบายตากว่า แม้ในขนาดฟอนต์ที่เล็กมาก ๆ
    หรือถ้าหากเห็นว่าเบลอเกินไป ก็ตั้งค่า hintstyle เป็น 4 ก็จะดูคมแข็งขึ้นครับ
    (ดูรายละเอียดการปรับตั้งที่ ลองทำฟอนต์ใช้เอง - แถม)

  • 50-04-24 สร้างฟอนต์ชุด Verachart-Bold เพิ่ม
  • 50-04-24 ปรับละเอียด
  • 50-06-06 ปรับปรุง Verachart-Bold เสร็จ ทำ Verachart-Oblique เสร็จ
  • 50-07-02 ปรับละเอียด Verachart 90% และ Verachart-Bold 50%
  • 50-07-04 ปรับละเอียด Verachart และ Verachart-Bold ครั้งที่สอง ทำเรื่อง Hinting โดยปรับให้ความหนาของตัวอักษร ในแนวนอน มีความสม่ำเสมอ จะทำให้การ Hint ดูดีขึ้นมาก
  • 50-07-12 ปรับปรุงตัวหนา ช ฃ ช ซ โ พ ฟ เล็กน้อย

ลองทำฟอนต์ใช้เอง - แถม

update

  • การตั้งค่าตามเนื้อความในโพสต์นี้ จะมีผลทำให้ฟอนต์ที่ถูกระบุในการปรับตั้งครั้งนี้ ไม่อยู่ภายใต้การควบคุมของระบบ (กับ Gnome คือ System -> Preference -> Fonts) นั่นคือการแสดงผลของฟอนต์ชุดนี้จะไม่เปลี่ยนไปตามการตั้งค่า Smoothing และ Hinting ของระบบ

โพสต์นี้ได้ความรู้มาจากคุณสมเจตน์ ท่านช่วยทดสอบให้ ได้ความว่า

ฟอนต์ชุดนี้ หากจะนำไปใช้ให้ได้ผลดีมีความคมชัด ต้องกำหนด Hint ให้ฟอนต์ด้วย ดังนี้

สร้างไฟล์ชื่อ 65-1-ttfonts.conf เอาไปใส่ไว้ในไดเรกทอรี่ /etc/fonts/conf.avail แล้วสร้างลิงก์ไปยังไดเรกทอรี่ /etc/fonts/conf.d
$ sudo vi /etc/fonts/conf.avail/65-1-ttfonts.conf

<?xml version="1.0"?>
<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
<fontconfig>
        <alias>
                <family>Lomaputta</family>
                <family>Lomaputta2</family>
                <family>Garudatas</family>
                <default><family>san-serif</family></default>
        </alias>
        <alias>
                <family>Noraputta</family>
                <family>Norasitas</family>
                <default><family>serif</family></default>
        </alias>
        <match target="font">
                <test name="family"><string>Lomaputta</string></test>
                <edit name="autohint" mode="assign"><bool>true</bool></edit>
                <edit name="hinting" mode="assign"><bool>true</bool></edit>
                <edit name="embeddedbitmap" mode="assign"><bool>false</bool></edit>
        </match>
        <match target="font">
                <test name="family"><string>Lomaputta2</string></test>
                <edit name="autohint" mode="assign"><bool>true</bool></edit>
                <edit name="hinting" mode="assign"><bool>true</bool></edit>
                <edit name="embeddedbitmap" mode="assign"><bool>false</bool></edit>
        </match>
        <match target="font">
                <test name="family"><string>Garudatas</string></test>
                <edit name="autohint" mode="assign"><bool>true</bool></edit>
                <edit name="hinting" mode="assign"><bool>true</bool></edit>
                <edit name="embeddedbitmap" mode="assign"><bool>false</bool></edit>
        </match>
        <match target="font">
                <test name="family"><string>Noraputta</string></test>
                <edit name="autohint" mode="assign"><bool>true</bool></edit>
                <edit name="hinting" mode="assign"><bool>true</bool></edit>
                <edit name="embeddedbitmap" mode="assign"><bool>false</bool></edit>
        </match>
        <match target="font">
                <test name="family"><string>Norasitas</string></test>
                <edit name="autohint" mode="assign"><bool>true</bool></edit>
                <edit name="hinting" mode="assign"><bool>true</bool></edit>
                <edit name="embeddedbitmap" mode="assign"><bool>false</bool></edit>
        </match>
</fontconfig>

$ sudo ln -sf /etc/fonts/conf.avail/65-1-ttfonts.conf /etc/fonts/conf.d

แล้วก็รีบูตเครื่องใหม่ หลังจากนั้นก็จะเห็นฟอนต์ชุดนี้มีความคมชัดเพิ่มขึ้นครับ

ลิงก์รอศึกษา

ลองทำฟอนต์ใช้เอง ๒

ลองทำอีกชุดนึงสำหรับ sans-serif โดยเอาฟอนต์โลมามาเป็นต้นแบบ
จุดประสงค์เพื่อจะนำมาทำเป็นฟอนต์สำหรับดูที่หน้าจอมอนิเตอร์ โดยต้องการให้อ่านง่าย จึงปรับให้หัวตัวอักษรกลมขึ้น แยกเส้นตัวอักษรให้เด็ดขาดขึ้น

ตั้งชื่อว่าโลมาบุตร (Lomaputta)
ช่วยทดสอบหรือเอาไปใช้ได้ฟรี (ฟอนต์โลมาต้นฉบับเป็น GPL)

เพิ่มความหนานิดนึง ดูคล้าย ๆ Tahoma เสียดายอันเก่า เลยตั้งชื่อใหม่เป็นโลมาบุตร2

update

  • 50-04-14 ปรับช่องไฟละเอียด, ปรับส่วนโค้งอักษรแบบละเอียด, ปรับมาใช้เทมเพลตของ thaifonts รุ่น 0.4.6 ทำให้สามารถใช้งาน OO.o ได้
  • 50-04-16 ปรับแต่ง Hinting ด้วยมือ ดูดีขึ้นมาก
  • 50-04-17 ปรับมาใช้เทคนิกการ Hint แบบเบลอภายในฟอนต์ด้วย AutoHint และคมภายนอกด้วย ManualHint
  • 50-04-17 สร้าง Lomaputta-Bold เพิ่ม
  • 50-04-17 เปลี่ยนกลับมาใช้ Hint ด้วยมือล้วน เนื่องจากแสดงผลในเดเบียนฟุ้ง(Blur)เกินไป (คงไม่เกี่ยวกับเดเบียน แต่อาจเป็นเพราะเครื่องเดเบียนของผมใช้จอภาพเป็น LCD ของเอเซอร์ ซึ่งการแสดงผลดูแล้วจะสู้ของแอลจีไม่ได้) แก้อาการหลอกตาด้วยการลดขนาดหัวตัวอักษรแทน
    รู้ปัญหาอาการฟุ้งบนเดเบียนแล้ว คือ เดเบียนไม่ทำ AutoHint เป็นค่าปริยาย จึงต้องทำเองด้วยมือ
    $ sudo ln -sf /etc/fonts/conf.avail/10-autohint.conf /etc/fonts/conf.d
    แล้วเข้าระบบใหม่ ผลที่ได้คือ ฟอนต์บนเดเบียนดูคมกว่าบนอูบุนตูอีก
  • 50-04-18 สร้าง Lomaputta-Oblique เพิ่ม
  • 50-04-19 แก้ไขตาราง Contextual ซ่อมเรื่องวรรณยุกต์กับสระอุ-สระอู ตามต้นฉบับ
  • 50-04-19 สร้าง Lomaputta-BoldOblique เพิ่ม
  • 50-04-27 ล้างลายเส้นเก่า สร้างลายเส้นใหม่ เลียนแบบของคุณเทพ

ลองทำฟอนต์ใช้เอง ๓

คราวก่อนทำฟอนต์โลมาบุตร ดูแล้วยังรู้สึกว่าอ่านยาก (ไม่รู้ว่าเป็นที่ hinting หรือเปล่า)
คราวนี้ลองเอาอีกฟอนต์นึงคือฟอนต์ครุฑ (Garuda) มาปรับให้เหมาะกับการแสดงผลบนจอคอมพิวเตอร์
โดยการลดขนาดสระและวรรณยุกต์ และปรับความกว้างของบรรทัดให้แคบเข้า
เอาไว้เป็นตัวเผื่อเลือกสำหรับการแสดงผลของฟอนต์ sans-serif
ตั้งชื่อว่าฟอนต์ ครุฑทัศน์ (Garudatas)

ดาวน์โหลด

update
ทำเพิ่มเติมสำหรับ serif ด้วย คือฟอนต์นรสีห์ เป็น นรสีห์ทัศน์
ดาวน์โหลด

หมายเหตุ
ไม่เหมาะสำหรับงานเอกสารที่ต้องพิมพ์ออกมา สำหรับงานพิมพ์ใช้ฟอนต์ Garuda และ Norasi เดิมสวยอยู่แล้ว

เกร็ดเกี่ยวกับโปรแกรม FontForge

โปรแกรม FontForge เป็นโปรแกรมที่ใช้ออกแบบฟอนต์ของลินุกซ์ที่ได้รับความนิยม มีความสามารถสูง แต่มีข้อเสียคือ ทำ AutoHint ได้แย่มาก เพราะโปรแกรมจะกันเหนียวด้วยการกำหนดแถบการ Hint ไว้กว้างเกินไป

การกำหนดแถบการ Hint ไว้กว้าง ทำให้เวลาแสดงฟอนต์ทางจอภาพ อักษร ก ไก่ จะดูเตี้ยกว่าอักษร ข ไข่ ซึ่งไม่ถูก AutoHint ไว้กว้างแบบนั้น

ถ้าจะทำให้ได้ผลการ Hint ที่ดี แบบที่เรา ๆ ท่าน ๆ สามารถทำได้เอง โดยไม่ต้องใช้ฝีมือระดับเทพ ก็คือการ Hint เองด้วยมือ
ซึ่งทำได้โดยการกำหนดสองจุดคู่ที่จะทำการ Hint แล้วสั่ง Add HHint หรือ Add VHint ตามลักษณะของส่วนโค้งตัวอักษรนั้น ๆ

วิธีนี้ทำให้ได้ตัวอักษรที่ดูสูงเท่ากัน แต่ปัญหาคือโปรแกรม FontForge ไม่ทำปุ่มลัดสำหรับคำสั่ง Add HHint และ Add VHint ไว้ด้วย
งานที่จะต้องมานั่งเลือกจุดคู่แล้วไล่คลิกเมนูสั่งงาน นับว่าเปลืองเวลาเกินไป
แต่ยังโชคดีที่โปรแกรมถูกออกแบบให้สามารถกำหนดปุ่มลัดได้เอง เพียงแต่ต้องออกแรงหน่อยนึงด้วยการคอมไพล์แพกเกจเอง
เราจะกำหนดปุ่มลัดเอง ทำให้การทำงานเร็วขึ้นมาก

เริ่มด้วยการติดตั้งแพกเกจที่จำเป็นในการคอมไพล์ก่อน
$ sudo aptitude install libfreetype6-dev
(แพกเกจอื่น ๆ ไม่แน่ใจครับ อาจตรวจดูจากข้อผิดพลาดตอนสั่ง ./configure ก็ได้)

ไปเอาซอร์สมาก่อนคือไฟล์ fontforge_full-20070312.tar.bz2

เมื่อได้ไฟล์มาแล้วก็แตกไฟล์ออกก่อน
$ tar xfj fontforge_full-20070312.tar.bz2

คอมไพล์และติดตั้ง
$ cd fontforge-20070312
$ ./configure
$ make
$ sudo make install

ต่อไปเป็นการสร้างไฟล์สำหรับกำหนดปุ่มลัด
ทำตามคู่มือ ในการนำเอาไฟล์ FontForge-MenuShortCuts.pot ออกมา
$ cd fontforge-20070312/fontforge
$ make -f Makefile.in FontForge-MenuShortCuts.pot

เราจะได้ไฟล์ FontForge-MenuShortCuts.pot
เราจะแก้ไขในไฟล์นี้โดยกำหนดให้ Add HHint ใช้ปุ่มลัดเป็น Alt+Ctl+1
และ Add VHint ใช้ปุ่มลัดเป็น Alt+Ctl+2
$ vi FontForge-MenuShortCuts.pot

...
#: charview.c:8105
msgid "Add HHint|No Shortcut"
msgstr "Alt+Ctl+1" 

#: charview.c:8106
msgid "Add VHint|No Shortcut"
msgstr "Alt+Ctl+2"
...

คอมไพล์ไฟล์ pot
$ msgfmt -o FontForge-MenuShortCuts.mo FontForge-MenuShortCuts.po

ย้ายไฟล์นี้ไปไว้ที่ไดเรกทอรี่ที่เก็บ locale ของเครื่อง
ถ้าตั้ง locale เป็น en_US.UTF8 คำสั่งจะเป็น
$ sudo mkdir -p /usr/local/share/locale/en_US/LC_MESSAGES
$ sudo mv FontForge-MenuShortCuts.mo /usr/local/share/locale/en_US/LC_MESSAGES

เสร็จเรียบร้อยแล้ว สั่งรันโปรแกรมด้วยคำสั่ง
$ /usr/local/bin/fontforge -dontopenxdevices [ชื่อไฟล์ฟอนต์]

ผลที่ได้จากการทำ Hint ด้วยมือ ดูดีขึ้นกว่าทำ AutoHint มากครับ

จากความรู้ที่ได้นี้ สามารถสรุปการ Hint ได้คือ

  • ส่วนในของฟอนต์เลือกใช้ AutoHint เนื่องจากต้องการให้เกิดการเบลอภายในให้มากที่สุด
  • ส่วนนอกของฟอนต์ คือ จุดสูงสุดและต่ำสุด ต้อง Hint เองด้วยมือ เพราะต้องการให้ส่วนสูงมีความคงที่
  • เพิ่มเติม

  • อ่านมาจาก http://osdir.com/ml/fonts.fontforge.user/2006-08/msg00030.html
    บอกว่า ปกติ FontForge ไม่ทำ Hint ของทรูไทป์โดยตรง แต่เป็นของ Type1
    ถ้าจะพยายามทำ Hint ให้ทรูไทป์ วิธีทำคือให้เปลี่ยนส่วนโค้งเป็นแบบ Quadratic
    Element -> Font Info ... -> เลือก Quadratic Splines

    หลังจากนั้นจึงทำ AutoHint และ AutoInstruction แล้วจึงสั่งผลิตฟอนต์ทรูไทป์
    เลือกช่วงฟอนต์ที่ต้องการ -> Hints -> AutoHint
    Hints -> AutoInstr
    Files -> Generates Fonts

    ทดลองแล้วได้ผลดีพอควร

  • 50-05-05 ลองทดสอบเรื่อง Truetype Hint ได้ความว่า
    ถึงแม้ว่า Fontforge จะทำ Truetype Hint ได้แย่มากก็ตาม แต่เราก็อาจบังคับทำได้ โดยได้ผลดีพอควร โดยมีข้อแม้ว่า :-
    1. จุดต่าง ๆ ที่กำกับส่วนโค้ง ต้องเลือกใช้จุดให้ตรงจริง ๆ ทั้งสามชนิดคือ จุดส่วนโค้ง จุดหัวมุม และจุดแทนเจนต์ ห้ามใช้จุดมั่วเด็ดขาด
    2. ขนาดความหนาของส่วนตัวอักษร และจุดกำกับส่วนโค้งต่าง ๆ ควรอยู่ในตำแหน่งทางเรขาคณิตแบบง่าย ๆ
    3. ส่วนโค้งที่ยาวมาก ต้องมีจุดแบ่งส่วนกำกับเสมอ
    4. ถ่าหากจุดเริ่มต้นส่วนโค้ง ไม่อยู่ในตำแหน่งทางเรขาคณิตอย่างง่าย ควรใช้จุดแทนเจนต์กำกับอยู่ใกล้ ๆ (เช่น ปลายหางของตัว จ.จาน เป็นต้น)
    5. เมื่อทดสอบแล้วพบว่ามีบางอักขระที่มีอาการฟุ้ง ให้ลองปรับลดหรือเพิ่มความกว้างของอักขระนั้นที่ละ 1 พิกเซล (ส่วนใหญ่ลองที่ขนาดฟอนต์ประมาณ 11 ปอยนต์)

    ถ้าผ่านข้อกำหนดดังกล่าว การ Hint จะได้ผลดีที่สุด

  • การทำงานกับส่วนโค้งที่เป็น Quadratic ไม่ยากจนเกินไปนัก หัดสักพักก็จะชิน เมื่อชินแล้วจะพบว่าการบังคับส่วนโค้ง ง่ายกว่าแบบ Cubic เสียอีก เพราะโดนบังคับด้วยจุดขนาบ
  • อนาคตของ Truetype Hint น่าจะเสื่อมความนิยมลงไปเรื่อย ๆ ผกผันกับขนาดความละเอียดของจอภาพที่จะละเอียดขึ้นไปเรื่อย ๆ
  • การทำให้การ Hint แบบ Adobe ได้ผลดี ควรปรับความหนาของตัวอักษรในแนวนอนให้มีความสม่ำเสมอ จึงจะได้ความสูงของตัวอักษรที่เท่า ๆ กัน ที่ขนาดตัวอักษรต่าง ๆ กัน

ลองทำฟอนต์ใช้เอง - จบ

ค้นไปค้นมา ไปพบฟอนต์ที่เป็นฟรีแวร์และ gpl สรุปได้ดังนี้

  • ฟอนต์แห่งชาติ นอกเหนือจากฟอนต์โลมา ครุฑ และนรสีห์แล้ว ยังมีฟอนต์กินรีที่สวยอยู่แล้ว ใช้แทนฟอนต์ Angsana ในวินโดวส์ได้เลย - ดาวน์โหลด national-fonts.zip
  • ฟอนต์ของบริษัท Tep Club เป็นฟรีแวร์ มีฟอนต์สวยมาก ๆ คือ CmPrasanmit - ดาวน์โหลด tep-clubfonts.zip
  • ฟอนต์ที่ SIPA พัฒนา ไม่ค่อยสวยเท่าไหร่ - ดาวน์โหลด dip-sipa-font.zip
  • ฟอนต์ตระกูล JS ของคุณหมอภาณุฑัต ที่แนบมากับลินุกซ์ทะเล 7 สามารถนำออกจากแผ่น TLE-7 ได้เลย หรือสามารถดาวน์โหลดไฟล์ rpm แล้วนำมาแตกเองด้วยโปรแกรม Archeive Manager - ดาวน์โหลด thai-ttf-js.0.0.2-1.src.rpm
  • มีฟอนต์สวย ๆ มากจริง ๆ

ทำฟอนต์เอง มาตายตรง hinting มือไม่ถึง :P เวลาไม่เอื้อให้ศึกษา
แต่ก็ยังชอบใจ โลมาบุตรอยู่ดี ;D

fonts: TrueType Hinting Instruction

ศึกษาการทำ TrueType Hinting Instruction

ลิงก์

การทำ Truetype Hinting Instructions

ตัวอย่างไฟล์

ไฟล์ DejaVuSansThai.tar.gz

fonts: บันทึกการทดลองทำ DejaVuSansThai รุ่นแรก

บันทึกการแปลงฟอนต์ Waree เป็น DejaVuSansThai รุ่นแรก

  • ใช้หลักการ instruct โดยพยายามยึดตามแนวของ DejaVu เท่าที่พอแกะออก
  • ยังขาดการทำ delta hint เพราะยังทำไม่เป็น ดังนั้นการออกแบบฟอนต์และการ instruct จึงพยายามปรับ เพื่อเลี่ยงการทำ delta hint ซึ่งต้องรอศึกษาต่อไป
    ทำ delta hint ได้แล้ว แต่เสียเวลาและสายตามากเกินไป จึงพยายามออกแบบโค๊ดให้ลดการทำ delta hint ให้มากที่สุด
    ล่าสุดเปลี่ยนแนวคิดการออกแบบเป็น กำหนดจุดให้น้อยที่สุดเพื่อให้ฟอนต์เพี้ยนน้อยที่สุด จึงจำเป็นต้องพึ่งการใช้ delta hint อย่างมาก
  • ลดความสูงของฟอนต์ไทย ให้เท่ากับอังกฤษตัวเล็ก แล้วพยายามย่นความสูงของสระและพยัญชนะลงมา แต่กระนั้นก็ยังล้นระยะห่างระหว่างบรรทัดของ DejaVu อยู่ดี
  • ค่า cvt ของ DejaVu Sans เต็ม 256 ค่าแล้ว แต่มีความจำเป็นต้องเพิ่มค่าของเราเข้าไป ทำให้ต้องอ้างค่าเป็น word แทน byte โค๊ดจึงอาจดูยากขึ้นเล็กน้อย
  • เขียนสคริปต์ไพธอน เล็ก ๆ ชื่อ dfont.py เอาไว้คลี่และเรียงสแต็ก ใช้ได้ผลดีพอควร แต่สคริปต์ยังขาดการตรวจสอบความถูกต้องของ instructing code ซึ่งถ้าใช้ด้วยพารามิเตอร์ --all อาจทำให้ fontforge หยุดการทำงานได้ - รอปรับปรุงต่อไป
  • ยังเหลือบั๊กอีกหลายจุด ตัว ค.ควาย จ.จาน ย.ยักษ์ ยังออกแบบไม่ดี ตัว ร.เรือ ธ.ธง ยัง instruct ได้ไม่ดี และยังไม่ได้ทำตัวหนา ตัวเอียง และตัวหนาเอียง แต่มีความจำเป็นต้องเว้นวรรคนาน จึงบันทึกไว้เพื่อให้จำได้

ความรู้ที่ได้

  • การทำ TrueType Hinting Instruction ตัวอักษรจะเพี้ยนเสมอ ทั้งนี้เนื่องมาจากการบังคับให้ลงจุดในจอมอนิเตอร์ ดังนั้น การ instruct ก็คือการบอกว่าจะให้เพี้ยนยังไง จึงจะทำให้ดูคมชัด
  • การเคลื่อนจุดด้วยวิธี indirect เช่นคำสั่ง MIRP (ซึ่งต้องอ้างค่าจาก cvt - Control Value Table) มีความแม่นยำแน่นอนกว่าการเคลื่อนค่าแบบ direct (เช่นคำสั่ง MDRP)
  • คำสั่ง ip (Interpolate) จะทำให้ฟอนต์ดูฟุ้ง แต่ระยะที่ได้ แน่นอนกว่าการเคลื่อนจุดแบบ direct (เว้นเสียแต่ถ้าไม่มีการทดลงจุด คืออาร์กิวเมนต์ rnd ควรใช้ MDRP เสมอ ไม่งั้นบางทีอาจเพี้ยน)
  • ถ้ามีความจำเป็นต้องใช้การ Interpolate อาจทำแค่แกนเดียว ส่วนอีกแกนนึงให้ใช้การเคลื่อนแบบ direct แทน
  • พยายามยึดแนวแกนซ้ายขวาและความสูงเฉลี่ยของตัวอักษรไว้ ดังนั้นจึงควรบรรจุค่าความสูงและความกว้างของตัวอักษรในตาราง cvt เสมอ
  • ควรออกแบบความกว้างของตัวอักษรให้ดูสม่ำเสมอ แล้วเขียนโค๊ดโดยดูจากตาราง cvt เป็นสำคัญ แต่จะมีข้อยกเว้นเป็นบางตัวอักษรที่มีหัวอักษรอยู่ตรงกลางฟอนต์ เช่น ค.ควาย หรือ ด.เด็ก อาจต้องยอมทิ้งค่าความกว้างจาก cvt โดยต้องไล่จุดไปตามแนวที่ผ่านหัวอักษรไปเรื่อย ๆ ไม่งั้นเวลาแสดงผลที่ขนาดปอยต์ต่าง ๆ หัวอักษรอาจเยื้องไปทางซ้ายบ้างขวาบ้าง บางครั้งอาจดูลีบติดไปทางแกนอักษรข้างใดข้างหนึ่งบ้าง - อาจแก้ได้ด้วย delta hint
  • การทำ delta hint หรือ grid fitting โดยกำหนดให้เป็น version 1 จะได้ฟอนต์ที่ดูนุ่มนวลดีกว่า version 0

ผลการทดลอง - บนลินุกซ์ ความละเอียดจอภาพ 85 dpi

  • ดูดีที่ 12 และ 13 ปอยต์
  • ตั้งแต่ 7 ปอยต์ลงไป อ่านไม่ได้
  • ตั้งแต่ 18 ปอยต์ขึ้นไป ไม่มีปัญหาเรื่องการ hint
  • แย่สุดที่ 14 ปอยต์ ที่เหลือนอกจากนี้ พอดูได้
  • รุ่นล่าสุด (510519) ปรับด้วย delta shift hint พยายามทำให้ดูดีในทุก ๆ ppem แต่ปรับละเอียดที่ 14,15 และ 16 ppem

ดาวน์โหลดไฟล์ฟอนต์ ttf ล่าสุด (รุ่น 510823)

ดาวน์โหลดไฟล์ซอร์ส

  • DejaVuSansThai-src-510331.tar.gz
  • ปรับปรุงช่องไฟ (ยังไม่ดี) ปรับปรุงเรื่องหัวเล็กน้อย (ดีขึ้น) และปรับหลัก instruction บางส่วน (ดีขึ้น)
    DejaVuSansThai-src-510408.tar.gz
  • รุ่นสุดท้ายก่อนทำ delta hint DejaVuSansThai-src-510415.tar.gz
  • เริ่มทำ delta hint (gridfit) ของตัว ค.ควาย ด.เด็ก และปรับปรุงตัว ร.เรือ DejaVuSansThai-src-510416.tar.gz
  • ทำตัวหนาเพิ่ม DejaVuSansThai-src-510420.tar.gz
  • ปรับวิธี hint ใหม่หมด เลียนแบบจาก Serif ซึ่งพยายามเลียนจาก Tahoma อีกที โดยปรับละเอียดที่ 16 ppem และปรับหัวให้ดูนุ่มขึ้น ที่ 14,15 และ 16 ppem
    DejaVuSansThai-src-510519.tar.gz
  • ทำตัวหนาเสร็จ ปรับวิธี hint อีกเล็กน้อย โดยใช้หลัก hint เท่าที่จำเป็น
    รุ่นนี้คิดว่าสมบูรณ์พอจะเอาไปใช้งานได้แล้ว DejaVuSansThai-src-510523.tar.gz
  • ทำรุ่นใหม่ โดยใช้ฟังก์ชั่น SHPIX บีบหัวให้ฟุ้งน้อยลง จึงทำให้ดูเหมือนกับคมขึ้น ตามไปช่วย hint ให้ภาษาลาวเล็กน้อย แต่ยังไม่เสร็จ DejaVuSansThai-src-510612.tar.gz
  • hint ภาษาลาวเสร็จ ปรับแก้เรื่องเส้นขีดฆ่า ตามคำแนะนำของคุณเทพ (แต่ตัวหนายังไม่ได้แก้ คิดว่าจะแก้ใหญ่ในภายหลังครับ) DejaVuSansThai-src-510618.tar.gz
  • เพิ่มตาราง open type (แก้ปัญหา OpenOffice-2.4 "สระอำ" เพี้ยน) และตรวจแก้ข้อมูลตาราง lookup gsub ขาดไป : DejaVuSansThai-src-510823.tar.gz

ภาพตัวอย่างของรุ่นล่าสุด

ภาพตัวอย่าง รุ่นแรก (จากจอภาพขนาด 85 dpi)








fonts: โค๊ดไพธอนที่ใช้ช่วยทำงาน

สรุปรวมจาก python: เขียนโค๊ดคลี่แสต็กฟอนต์ และ python: โค๊ดฟอนต์ย้อนกลับ
รวมเป็นไฟล์เดียว ตั้งชื่อว่า dfont.py การทำงานแค่คลี่และเรียงแสต็กใหม่
เวลาใช้งาน จะต้องเขียนสคริปต์ไพธอนเพิ่มสำหรับฟอนต์แต่ละตัว เพื่อมาเรียกใช้ dfont เป็นมอดูล

ปรับปรุง

  • 51-05-06 เพิ่มฟังก์ชั่นการทำ grid fitting ทำให้การเขียนโค๊ดง่ายขึ้น
    เช่นต้องการขยับจุด 20 ไป +8 ที่ฟอนต์ขนาด 9, 10 และ 11 ppem
    เดิมเขียนโค๊ดเป็น
    deltap1    3    20 15   20 31  20 47

    เปลี่ยนเป็น
    f_deltap    20 9+8   20 10+8   20 11+8

โค๊ดมีดังนี้
$ vi dfont.py

#!/usr/bin/env python
"""
This file is only stack render engine.
Usage:
1. Decode from instucted code
    ./dfont.py -d INST_CODE.txt > PSEUDO_CODE.txt
2. Encode from pseudo_code
    ./dfont.py -e PSEUDO_CODE.txt > INST_CODE.txt


Implement:
1 Create FONTNAME.py contain code as followed:
    1.1 import dfont, sys, os
    1.2 cvt_dict = {"NAME": VALUE, ... }
    1.3 pseudo_code_dict = {"CHARACTER_NAME": "PSEUDO CODE TRUETYPE INSTRUCTION", ...}
    1.4 if __name__ == "__main__":
            if sys.argv[1] == "--all":
                #$0 --all = ENCODE ALL CHARS
                sfd_file = os.path.basename(sys.argv[0]).split('.',1)[0] + '.sfd'
                print "Encoding all character in %s" % (sfd_file)
                dfont.inst_encode_all( sfd_file, pseudo_code_dict, cvt_dict )
            else:
                #$0 uni0E01 > x.txt = PRINT INSTRUCTION CODE OF uni0E01 TO x.txt
                print '\n'.join(dfont.inst_encode( pseudo_code_dict[sys.argv[1]], cvt_dict ))

2 Encode:
    2.1 Process all character, this script will modify FONTNAME.sfd
        Usage: ./FONTNAME.py --all
    2.2 Encode each character, for example KO_KAI="uni0E01"
        Usage: ./FONTNAME.py uni0E01 > INSTRUCTED.TXT
"""

import sys
import os

# put cvt_dict and pseudo_code_dict varirable in FONTNAME.py
# cvt value example:
#cvt_dict = {
#    "base":             10,     #0
#    "hstem":            8,      #184
#    "vstem":            96,     #154 
#    "vstem_small":      27,     #135 
#    "headstem_small":   11,     #113    =kho
#    "headdia_small":    180,    #377    =kho
#    "headhole_small":   254,    #150    =kho
#    "headneck_kho":     39,     #180    =kho
#    "hwidth_ko":        164,    #987    =ko,tho 
#    "hwidth_kho":       14,     #690    =ko,tho 
#    "hwidth_sho":       108,    #772    =sho
#    "vheight":          49,     #1208 
#    "vheight_hi":       54,     #1208   =ko 
#    "vheight_hi_kho":   54,     #1229   =kho 
#    "vheight_lo":       88,     #20 
#    "vheight_lo_sho":   256,    #6  *** NEWVALUE
#    "head_diff":        131,    #254    =head of character: bo,po
#    "beak_diff":        9,      #102    =beak of ko,tho
#    "front_space_ko":   65,     #201    =front spacing of ko,tho
#    "front_space_kho":  38,     #340    =front spacing of kho
#}

# pseudo_code example:
#pseudo_code_dict = {
#    #ko_kai
#    "uni0E01": """
#srp0    26
#mirp[rp0,min,rnd,grey] front_kai 14
#mirp[min,rnd,grey]  hstem  12
#mirp[rp0,rnd,grey]  hwidth_14 25
#mirp[min,rnd,grey]  hstem 1
#mdrp[min,rnd,grey]  27
#srp0   14
#mdrp[rp0,rnd,grey]  18
#mdrp[min,rnd,grey]  8
#mdrp[rp0,rnd,grey]  17
#mdrp[min,rnd,grey]  10
#iup[x]
#svtca[y-axis]
#mdap[rnd]  1
#ALIGNRP    13
#mirp[rp0,rnd,grey]  vheight_shoot    22
#mirp[min,rnd,grey]  vstem_curve  5
#srp1    5
#srp2    13
#sloop   10
#ip  19 8 18 9 17 10 2 25 15 12
#iup[y]
#""",
#}


#BEGIN
# inst_dict = { "COMMAND" : ("Description",diff,pops,push), ... }
# diff: 0=NOOP, 1=1BYTE, 2=2BYTE, ...
#       -1=FIRST BYTE IS ONE BYTE COUNTER,
#       -2=FIRST BYTE IS TWO BYTE COUNTER, 
#       -3=CLEAR STACK
#       -4=REQUIRE SOME PROCESSING
inst_dict = {
    "AA" :       ("Adjust Angle", 1, 1, 0),
    "ABS" :      ("ABSolute value", 0, 1, 1),
    "ADD" :      ("ADD", 1, 2, 1),
    "ALIGNPTS" : ("ALIGN Points", 2, 2, 0),
    "ALIGNRP" :  ("ALIGN to Reference Point", 1, 1, 0),
    "AND" :      ("logical AND", 1, 2, 1),
    "CALL" :     ("CALL function", 1, 1, 0),
    "CEILING" :  ("CEILING", 0, 1, 1),
    "CINDEX" :   ("Copy the INDEXed element to the top of the stack", 1, 1, -4),
    "CLEAR" :    ("CLEAR the stack", -3, 0, 0),
    "DEBUG" :    ("DEBUG call", 1, 1, 0),
    "DELTAC1" :  ("DELTA exception C1", -2, 0, 0),
    "DELTAC2" :  ("DELTA exception C2", -2, 0, 0),
    "DELTAC3" :  ("DELTA exception C3", -2, 0, 0),
    "DELTAP1" :  ("DELTA exception P1", -2, 0, 0),
    "DELTAP2" :  ("DELTA exception P2", -2, 0, 0),
    "DELTAP3" :  ("DELTA exception P3", -2, 0, 0),
    "DEPTH" :    ("DEPTH of the stack", 0, 0, 1),
    "DIV" :      ("DIVide", 1, 2, 1),
    "DUP" :      ("DUPlicate top stack element", 0, 1, 1),
    "EIF" :      ("End IF", 0, 0, 0),
    "ELSE" :     ("ELSE clause", 0, 0, 0),
    "ENDF" :     ("END Function definition", 0, 0, 0),
    "EQ" :       ("EQual", 1, 2, 1),
    "EVEN" :     ("EVEN", 0, 1, 1),
    "FDEF" :     ("Function DEFinition", 1, 1, 0),
    "FLIPOFF" :  ("set the auto FLIP Boolean to OFF", 0, 0, 0),
    "FLIPON" :   ("set the auto FLIP Boolean to ON", 0, 0, 0),
    "FLIPPT" :   ("FLIP PoinT", 1, 1, 0),
    "FLIPRGOFF" :    ("FLIP RanGe OFF", 2, 2, 0),
    "FLIPRGON" : ("FLIP RanGe ON", 2, 2, 0),
    "FLOOR" :    ("FLOOR", 2, 2, 0),
    "GC" :       ("Get Coordinate projected onto the projection vector", 0, 1, 1),
    "GETINFO" :  ("GET INFOrmation", 0, 1, 1),
    "GFV" :      ("Get Freedom Vector", 0, 0, 2),
    "GPV" :      ("Get Projection Vector", 0, 0, 2),
    "GT" :       ("Greater Than", 1, 2, 1),
    "GTEQ" :     ("Greater Than or EQual", 1, 2, 1),
    "IDEF" :     ("Instruction DEFinition", 1, 1, 0),
    "IF" :       ("IF test", 1, 1, 0),
    "INSTCTRL" : ("INSTRuction execution ConTRoL", 2, 2, 0),
    "IP" :       ("Interpolate Point", 1, 1, 0),
    "ISECT" :    ("moves point p to the InterSECTion of two lines", 5, 5, 0),
    "IUP" :      ("Interpolate Untouched Points through the outline", 0, 0, 0),
    "JMPR" :     ("JuMP Relative", 1, 1, 0),
    "JROF" :     ("Jump Relative On False", 1, 1, 0),
    "JROT" :     ("Jump Relative On True", 3, 3, 0),
    "LOOPCALL" : ("LOOP and CALL function", 2, 2, 0),
    "LT" :       ("Less Than", 1, 2, 1),
    "LTEQ" :     ("Less Than or Equal", 1, 2, 1),
    "MAX" :      ("MAXimum of top two stack elements", 1, 2, 1),
    "MD" :       ("Measure Distance", 1, 2, 1),
    "MDAP" :     ("Move Direct Absolute Point", 1, 1, 0),
    "MDRP" :     ("Move Direct Relative Point", 1, 1, 0),
    "MIAP" :     ("Move Indirect Absolute Point", 2, 2, 0),
    "MIN" :      ("MINimum of top two stack elements", 1, 2, 1),
    "MINDEX" :   ("Move the INDEXed element to the top of the stack", 1, 1, 3),
    "MIRP" :     ("Move Indirect Relative Point", 2, 2, 0),
    "MPPEM" :    ("Measure Pixels Per EM", 0, 0, 1),
    "MPS" :      ("Measure Point Size", 0, 0, 1),
    "MSIRP" :    ("Move Stack Indirect Relative Point", 1, 1, 0),
    "MUL" :      ("MULtiply", 1, 2, 1),
    "NEG" :      ("NEGate", 0, 1, 1),
    "NEQ" :      ("Not EQual", 1, 2, 1),
    "NOT" :      ("logical NOT", 0, 1, 1),
    "NPUSHB" :   ("PUSH N Bytes", -1, 0, 0),
    "NPUSHW" :   ("PUSH N Words", -1, 0, 0),
    "NROUND" :   ("No ROUNDing of value", 0, 1, 1),
    "ODD" :      ("ODD", 0, 1, 1),
    "OR" :       ("logical OR", 1, 2, 1),
    "POP" :      ("POP top stack element", 1, 1, 0),
    "PUSHB" :    ("PUSH Bytes", -4, 0, 0),
    "PUSHW" :    ("PUSH Words", -4, 0, 0),
    "RCVT" :     ("Read Control Value Table entry", 0, 1, 1),
    "RTDG" :     ("Round Down To Grid", 0, 0, 0),
    "ROFF" :     ("Round OFF", 0, 0, 0),
    "ROLL" :     ("ROLL the top three stack elements", 0, 3, 3),
    "ROUND" :    ("ROUND value", 0, 1, 1),
    "RS" :       ("Read Store", 0, 1, 1),
    "RTDG" :     ("Round To Double Grid", 0, 0, 0),
    "RTG" :      ("Round To Grid", 0, 0, 0),
    "RTHG" :     ("Round To Half Grid", 0, 0, 0),
    "RUTG" :     ("Round Up To Grid", 0, 0, 0),
    "S45ROUND" : ("Super ROUND 45 degrees", 1, 1, 0),
    "SANGW" :    ("Set Angle Weight", 1, 1, 0),
    "SCANCTRL" : ("SCAN conversion ConTRoL", 1, 1, 0),
    "SCANTYPE" : ("SCANTYPE", 1, 1, 0),
    "SCFS" : ("Sets Coordinate From the Stack using projection vector and freedom vector", 2, 2, 0),
    "SCVTCI" :   ("Set Control Value Table Cut-In", 1, 1, 0),
    "SDB" :      ("Set Delta Base in the graphics state", 1, 1, 0),
    "SDPVTL" :   ("Set Dual Projection Vector To Line", 2, 2, 0),
    "SDS" :      ("Set Delta Shift in the graphics state", 1, 1, 0),
    "SFVFS" :    ("Set Freedom Vector From Stack", 2, 2, 0),
    "SFVTCA" :   ("Set Freedom Vector To Coordinate Axis", 0, 0, 0),
    "SFVTL" :    ("Set Freedom Vector To Line", 2, 2, 0),
    "SFVTP" :    ("Set Freedom Vector To Projection Vector", 0, 0, 0),
    "SHC" :      ("SHift Contour using reference point", 1, 1, 0),
    "SHP" :      ("SHift Point using reference point", 1, 1, 0),
    "SHPIX" :    ("SHift point by a PIXel amount", 2, 2, 0),
    "SHZ" :      ("SHift Zone using reference point", 1, 1, 0),
    "SLOOP" :    ("Set LOOP variable", 1, 1, 0),
    "SMD" :      ("Set Minimum Distance", 1, 1, 0),
    "SPVFS" :    ("Set Projection Vector From Stack", 2, 2, 0),
    "SPVTCA" :   ("Set Projection Vector To Coordinate Axis", 0, 0, 0),
    "SPVTL" :    ("Set Projection Vector To Line", 2, 2, 0),
    "SROUND" :   ("Super ROUND", 1, 1, 0),
    "SRP0" :     ("Set Reference Point 0", 1, 1, 0),
    "SRP1" :     ("Set Reference Point 1", 1, 1, 0),
    "SRP2" :     ("Set Reference Point 2", 1, 1, 0),
    "SSW" :      ("Set Single Width", 1, 1, 0),
    "SSWCI" :    ("Set Single Width Cut-In", 1, 1, 0),
    "SUB" :      ("SUBtract", 1, 2, 1),
    "SVTCA" :    ("Set freedom and projection Vectors To Coordinate Axis", 0, 0, 0),
    "SWAP" :     ("SWAP the top two elements on the stack", 0, 2, 2),
    "SZP0" :     ("Set Zone Pointer 0", 1, 1, 0),
    "SZP1" :     ("Set Zone Pointer 1", 1, 1, 0),
    "SZP2" :     ("Set Zone Pointer 2", 1, 1, 0),
    "SZPS" :     ("Set Zone PointerS", 1, 1, 0),
    "UTP" :      ("UnTouch Point", 1, 1, 0),
    "WCVTF" :    ("Write Control Value Table in Funits", 1, 1, 0),
    "WCVTP" :    ("Write Control Value Table in Pixel units", 2, 2, 0),
    "WS" :       ("Write Store", 2, 2, 0),
}

stack1_command = ("NPUSHB", "NPUSHW")
stack2_command = ("PUSHB", "PUSHW")

arg_1_command = ("MDAP", "MDRP")
arg_2_command = ("MIAP", "MIRP")

delta_command = ("F_DELTA",)
#step_list USE IN SPECIAL PSUEDO FUNCTION f_delta
step_list = [-8, -7, -6, -5, -4, -3, -2, -1, 1, 2, 3, 4, 5, 6, 7, 8]

normal_command = []
for i in range(6):
    normal_command.append( [ j for j in inst_dict.keys() if inst_dict[j][1]==i ] )

#           -1=FIRST BYTE IS ONE BYTE COUNTER,
#           -2=FIRST BYTE IS TWO BYTE COUNTER, 
#           -3=CLEAR STACK
#           -4=REQUIRE PROCESS
pop_command = []
pop_command.append([])
for i in range(-1, -4, -1):
    pop_command.append( [ j for j in inst_dict.keys() if inst_dict[j][1]==i ] )


def usage(prog_name):
    print """\
Usage: %s [-d CODE | [-e] PSUEDO_CODE]
""" % (prog_name)
    return

def line_format(cmd,stack,desc):
    #STRING, LIST, STRING
    return "%-24s %-10s ;%10s" % (cmd, "  ".join(stack), desc,)

def f_delta(first_cmd, l):
    #PSUEDO COMMAND TO PROCESS DELTA FUNCTION
    #
    #first_cmd = PSUEDO COMMAND, ex: f_deltap1 = deltap1
    #l = LIST OF ARGUMENT (INCLUDE SELF UN-UPPERCASE COMMAND IN FIRST ARGUMENT)
    #
    #usage: 
    #   f_deltap 0 16+8     #deltap1 MOVE POINT 0 AT 16 PPEM +8
    #real code will be:
    #   DELTAP1  1 0 127
    #
    d1_list = []
    d2_list = []
    d3_list = []
    first_cmd = first_cmd[2:]   #TRIM F_ OUT
    l.pop(0)
    while len(l) > 0:
        point = l.pop(0)
        pstep = l.pop(0)
        if '+' in pstep or '-' in pstep:
            if '+' in pstep:
                ppem, no_of_step = pstep.split('+')
                ppem = int(ppem)
                no_of_step = int(no_of_step)
            else:
                ppem, no_of_step = pstep.split('-')
                ppem = int(ppem)
                no_of_step = -int(no_of_step)
            if ppem < 9:
                raise ValueError('ppem must greater than 9, %s: %s %s' % (first_cmd, point, pstep,))
            if abs(no_of_step) > 8:
                raise ValueError('no_of_step must be between +8 and -8, %s: %s %s' % (first_cmd, point, pstep,))
            if no_of_step == 0:
                raise ValueError('no_of_step must not be 0, %s: %s %s' % (first_cmd, point, pstep,))
            num = (ppem-9)*16 + step_list.index(no_of_step)
            if num < 256:
                d1_list.extend([point,str(num)])
            elif num < 512:
                d2_list.extend([point,str(num)])
            elif num < 1024:
                d3_list.extend([point,str(num)])
            else:
                raise ValueError('Number of delta point exceed 1024, %s' % (num,))
        else:
            raise SyntaxError('Please use + or - in f_delta second argument, %s %s' % (point, pstep,))
    new_l = []
    if d1_list:
        new_l.append(first_cmd+'1')
        new_l.append(str(len(d1_list)/2))
        new_l.extend(d1_list)
    if d2_list:
        new_l.append(first_cmd+'2')
        new_l.append(str(len(d2_list)/2))
        new_l.extend(d2_list)
    if d3_list:
        new_l.append(first_cmd+'3')
        new_l.append(str(len(d3_list)/2))
        new_l.extend(d3_list)
    return  new_l[0], new_l

def inst_decode(txt):
    """Decode TrueType Instruction code into simpler code"""
    txt_list = txt.split("\n")
    new_list = []
    commentlist = []
    stack_list = []
    sloop = 0
    i = 0
    while i < len(txt_list):
        if "[" in txt_list[i]:
            c1,c2 = txt_list[i].strip().split("[",1)
        else:
            c1,c2 = txt_list[i].strip(), ""
        #SKIP EMPTY LINE
        if c1 == "":
            i += 1
            continue
        #STACK: NPUSHB, NPUSHW
        if c1 in stack1_command:
            i += 1
            n = int(txt_list[i].strip())
            i += 1
            while n > 0:
                stack_list.append(txt_list[i].strip())
                i += 1
                n -= 1
            continue
        #STACK2: PUSHB, PUSHW
        if c1[:5] in stack2_command:
            n = int(txt_list[i].strip()[6:])
            i += 1
            while n > 0:
                stack_list.append(txt_list[i].strip())
                i += 1
                n -= 1
            continue
        temp_list = []
        #POP ONE
        if c1 in pop_command[1]:
            idx = stack_list.pop()
            temp_list = [idx]
            for j in range(int(idx)):
                temp_list.append(stack_list.pop()) 
            new_list.append(line_format(txt_list[i],temp_list,inst_dict[c1][0]))
            i += 1
            continue
        #POP PAIR
        elif c1 in pop_command[2]:
            idx = stack_list.pop()
            temp_list = [idx]
            for j in range(int(idx)):
                temp_list.append(stack_list.pop()) 
                temp_list.append(stack_list.pop()) 
            new_list.append(line_format(txt_list[i],temp_list,inst_dict[c1][0]))
            i += 1
            continue
        #POP ALL (CLEAR STACK)
        elif c1 in pop_command[3]:
            stack_list = []
            new_list.append(line_format(txt_list[i],temp_list,inst_dict[c1][0]))
            i += 1
            continue

        #NORMAL COMMAND
        if c1 in normal_command[0]:
            count = 0
        elif c1 in normal_command[1]:
            count = 1 
        elif c1 in normal_command[2]:
            count = 2 
        elif c1 in normal_command[3]:
            count = 3 
        elif c1 in normal_command[4]:
            count = 4 
        elif c1 in normal_command[5]:
            count = 5 
        else:
            count = -1
            #print 'count-1:',c1
            new_list.append('count-1:%s' % (c1,))
        if count > 0:
            if sloop > 0:
                count += sloop-1
                sloop = 0
        while count > 0:
            if stack_list == []:
                #print temp_list
                new_list.extend(temp_list)
                break
            cnum = stack_list.pop()
            temp_list.append(cnum)
            if c1 == 'SLOOP':
                sloop = int(cnum)
            count -= 1
        new_list.append(line_format(txt_list[i],temp_list,inst_dict[c1][0]))
        i += 1
    return new_list
    
def stack_format(stack_list):

    def flush(new_list, cmd_list, ind, cur_ind):
        #if len(cmd_list)==0: return [], [], cur_ind
        if ind == 0:
            temp = ['PUSHB_','NPUSHB']
        else:
            temp = ['PUSHW_','NPUSHW']
        if len(cmd_list) < 8:
            new_list.append('%s%s' % (temp[0],len(cmd_list),))
        else:
            new_list.append(temp[1])
            new_list.append(' %s' % (len(cmd_list),))
        new_list.extend(cmd_list)
        return new_list, [], cur_ind

    if len(stack_list)==0: return []
    n = 0
    new_list = []
    cmd_list = []
    stack_list.reverse()
    if int(stack_list[n]) < 256:
        ind = 0     #BYTE
    else:
        ind = 1     #WORD
    while n < len(stack_list):
        if int(stack_list[n]) < 256:
            cur_ind = 0     #BYTE
        else:
            cur_ind = 1     #WORD
        if ind == cur_ind:
            cmd_list.append(' %s' % (stack_list[n],))
        else:
            new_list, cmd_list, ind = flush(new_list, cmd_list, ind, cur_ind)    #FLUSH
            cmd_list.append(' %s' % (stack_list[n],))
        n += 1
    new_list, cmd_list, ind = flush(new_list, cmd_list, ind, cur_ind)    #FLUSH
    return new_list 

def inst_encode(txt,cvt_dict={}):
    """Encode simple code into TrueType Instruction code"""
    if cvt_dict == {}:
        print "WARNING: cvt_dict is empty, please supply cvt_dict argument"
    comment_list = ['#',';']    #COMMENT CHARACTER
    flush_list = ['SVTCA']      #FLUSH STACK
    stack_list = []
    new_list = []
    cmd_list = []
    txt_list = txt.split('\n')
    for line in txt_list:
        line = line.replace("\t"," ")
        if " " in line:
            l = line.split(" ")
        else:
            l = [line] 
        l = [ j for j in l if j != "" ]
        if l == []:
            continue
        if l[0][0] in comment_list: #BYPASS COMMENT
            continue
        for f in flush_list:    #?FLUSH STACK
            if f == l[0][:len(f)]:
                new_list.extend(stack_format(stack_list))
                stack_list = []
                new_list.extend(cmd_list)
                cmd_list = []
                break
        if '[' in l[0]:         #TO UPPERCASE
            ltemp = l[0].split('[',1)
            first_cmd = ltemp[0].upper()
            first_word = first_cmd+'['+ltemp[1]
        else:
            first_word = l[0].upper()
            first_cmd = first_word
        #CHECK SPECIAL COMMAND ---------------------------------------
        # f_delta -> f_deltap1 = deltap1, ...
        for temp in delta_command:
            if temp in first_word:
                first_word, l = f_delta(first_word, l)
    
        #END CHECK SPECIAL COMMAND -----------------------------------
        cmd_list.append(first_word)     #KEEP COMMAND
        l.pop(0)                        #PROCESS REST STACK NUM
#
        temp_arg_list = []
        for element in l:
            if element[0] in comment_list:
                break 
            else:
                temp_list = element.split('[',1)
                if element in cvt_dict.keys():                  #CHECK CVT VALUE
                    stack_list.append(cvt_dict[element])
                    temp_arg_list.append(element)
                elif temp_list[0].upper() in inst_dict.keys():  #TRAP DELTA COMMAND
                    temp_list[0] = temp_list[0].upper()
                    element = '['.join(temp_list)
                    cmd_list.append(element)
                else:
                    stack_list.append(element)
                    temp_arg_list.append(element)
        #TEST NUMBER OF PARAMETER
        if first_cmd in arg_1_command:
            if len(temp_arg_list) != 1:
                raise ValueError('%s must take 1 argument, %s' % (first_word, temp_arg_list,))
        elif first_cmd in arg_2_command:
            if len(temp_arg_list) != 2:
                raise ValueError('%s must take 2 arguments, %s' % (first_word, temp_arg_list,))
        #
    if stack_list != []:
        new_list.extend(stack_format(stack_list))
    new_list.extend(cmd_list)
    return new_list


def inst_encode_all(sfd_file, pseudo_code_dict={}, cvt_dict={}):
    """Encode all characters in pseudo_code_dict, modify FONTNAME.sfd."""
    if os.path.isfile(sfd_file):
        sfd = open(sfd_file).read().split('\n')
        sfd_bak = sfd[:]
    else:
        print "File %s.sfd is not existed, script aborted." % (sfd_file,)
        sys.exit[1]
    for chr,txt in pseudo_code_dict.iteritems():
        search_line = 'StartChar: %s' % (chr,)
        if search_line in sfd:
            line = sfd.index(search_line)
            while line < len(sfd):
                if sfd[line] == "TtInstrs:":
                    temp_sfd = sfd[:line+1]
                    #print temp_sfd[-2:]
                    temp_sfd += inst_encode(txt,cvt_dict)
                    line += 1
                    while sfd[line] != "EndTTInstrs":
                        line += 1
                    temp_sfd += sfd[line:]
                    sfd = temp_sfd[:]
                    break
                elif sfd[line] in ["Back","Fore"]:
                    temp_sfd = sfd[:line] \
                               + ["TtInstrs:"] \
                               + inst_encode(txt,cvt_dict) \
                               + ["EndTTInstrs"]+sfd[line:]
                    sfd = temp_sfd[:]
                    break
                elif sfd[line] == "EndChar":
                    print "sfd file format error at %s" % (chr,)
                    break
                else:
                    line += 1
        else:
            print "Character %s not found, script aborted." % (chr,)
            sys.exit(1)
    if sfd != sfd_bak:
        os.rename(sfd_file, "%s.bak" % (sfd_file,))
        f = open(sfd_file,"w")
        f.write("\n".join(sfd))
        f.close()
        print "%s modified." % (sfd_file,)
    else:
        print "No modified."


if __name__ == "__main__":
    if sys.argv[1] == "-d":
        #DECODE
        #$0 -d x.txt > d.txt = PRINT PSEUDO_CODE FROM x.txt TO d.txt
        f = open(sys.argv[2])
        txt = f.read()
        f.close()
        print '\n'.join(inst_decode(txt))
    elif sys.argv[1] == "-e":
        #ENCODE
        #$0 -e d.txt > x.txt = PRINT INSTRUCTED CODE FROM PSEUDO_CODE d.txt TO x.txt
        f = open(sys.argv[2])
        txt = f.read()
        f.close()
        print '\n'.join(inst_encode(txt))
    else:
        #DEFAULT IS ENCODING
        #$0 -e d.txt > x.txt = PRINT INSTRUCTED CODE FROM PSEUDO_CODE d.txt TO x.txt
        f = open(sys.argv[1])
        txt = f.read()
        f.close()
        print '\n'.join(inst_encode(txt))

fonts: ตัวอย่างการ hint ก.ไก่

มีค่า cvt ของฟอนต์ DejaVu Sans ที่เกี่ยวข้อง ทำเป็นตัวแปรแบบดิกชันนารีในไพธอน คือ

cvt_dict = {
    ...
    "front_kai":      11,     #113    =front spacing of ko_kai 
    "hstem":          8,      #184    =horizontal stem width
    "w_881":          163,    #881    =width of ko kai
    "vheight_shoot":  184,    #1147   =overshoot height
    "vstem_curve":    185,    #156    =curve vertical stem width
    ...
}

ค่า cvt ของฟอนต์ ดูได้จากเมนูของ FontForge คือ Hints -> Edit 'cvt' ... โดยจะเรียงตั้งแต่ลำดับที่ 0 เป็นต้นไป
ค่า cvt ของ ก.ไก่ข้างต้น คือ กำหนดให้ลำดับที่ 11 (ซึ่งมีค่า 113) เป็นระยะช่องไฟด้านหน้า เป็นต้น

โครง ก.ไก่

ตัวอย่างจะเริ่มด้วยการ hint ในแนวแกน X ก่อน (เป็นค่าปริยาย) จะแสดงเป็นโค๊ด instruction แบบจำลอง เพื่อให้ดูง่าย เวลาใช้งานจริง ก็นำไปเขียนเป็นโค๊ดในไพธอน โปรแกรม dfont.py จะคลี่แสต็กและเรียงออกมาเป็นโค๊ดจริง ๆ อีกที

แนวแกน X
เส้นทาง hint ในแนวแกน X

จุดเริ่มต้นคือจุดที่ 26 (จุดสุดท้ายคือ 25 + 1)

srp0                26

จากจุดเริ่มต้น จะเคลื่อนจุดอ้างอิง rp0 ไปยังจุดที่ใกล้ขอบซ้ายที่สุด คือจุด 18 เพื่อกั้นเป็นระยะช่องไฟด้านหน้า ด้วยค่า cvt คือ front_kai
การเคลื่อนด้วยการอ้างอิงค่า คำสั่งคือ MIRP
การเคลื่อนจุดอ้างอิงไปด้วย ใช้อาร์กิวเมนต์ rp0
การปัดเศษให้ลงจุด (round) ใช้อาร์กิวเมนต์ rnd
การแรเงาพื้นที่ ใช้อาร์กิวเมนต์ grey (ปกติควรเป็น white แต่ DejaVu ใฃ้ grey เสมอ จึงใช้ตาม เพื่อกันสับสน)
ค่า cvt ที่ใช้ คือ front_kai
จุดที่จะเคลื่อนไป คือ 18

mirp[rp0,min,rnd,grey]  front_kai 18

จุดที่จะไปต่อคือ จุด 8 แต่เนื่องจากเมื่อเคลื่อนไปแล้ว ไม่มีจุดที่จะไปต่อ เราจึงไม่ต้องเคลื่อนจุด rp0 ไปด้วย จึงไม่ต้องใส่อาร์กิวเมนต์ rp0
ระยะของปาก ก.ไก่ ไม่จำเป็นต้องเป็นค่าที่แน่นอนนัก เราจึงไม่ต้องอ้างค่า cvt จึงใช้คำสั่ง MDRP
ปัดเศษด้วย ใช้ rnd
แรเงา ใช้ grey
จุดที่เคลื่อนไปคือ 8

mdrp[min,rnd,grey]  8

ตอนนี้ rp0 ยังอยู่ที่เดิม คือ จุด 18 เราต้องเคลื่อนไปต่อที่จุด 17 และ 10 ตามลำดับ แต่หากเราเคลื่อนไปจุด 17 ก่อน แล้วต่อไปที่ 10 จะทำให้ระยะที่ถูกทด (round) แล้ว มีค่ามากเกินไป ทำให้ปาก ก.ไก่ ไม่สวยงาม เราจึงใช้การเคลื่อนไปที่จุด 10 ก่อน แล้วจึงย้อนมาที่ 17 อีกครั้งหนึ่ง ทำให้ได้ภาพที่สวยงามกว่า

mdrp[rp0,rnd,grey]  10
mdrp[min,rnd,grey]  17

จุดที่จะไปต่อคือจุด 14 แต่ตอนนี้จุดอ้างอิง rp0 มาอยู่ที่จุด 10 แล้ว หากเราเคลื่อนจากจุดนี้ไปเลย จะทำให้ระยะทดไม่แน่นอน จึงควรย้อนกลับไปที่จุดแรกคือ 18 จะดีกว่า

srp0                18
mdrp[rp0,rnd,grey]  14

ต่อไปเป็นการกำหนดความหนาของเส้นตั้ง (hstem) ต้องใช้ค่า cvt เสมอ
ต้องอ้างอิงจาก cvt คำสั่งคือ MIRP
ไม่ต้องย้าย rp0 ไปด้วย เพราะไม่มีจุดที่จะไปต่อ จึงไม่ต้องใช้อาร์กิวเมนต์ rp0
ถึงแม้ขนาดเส้นจะบางอย่างไร การแสดงผลต้องกำหนดค่าให้อย่างน้อย 1 จุดเสมอ ใช้อาร์กิวเมนต์ min
แรเงาใช้ grey

mirp[min,rnd,grey]  hstem  12

ต่อไปเป็นการกำหนดความกว้างของตัวอักษร ต้องใช้ cvt เสมอ โดยต้องย้ายจุด rp0 ไปด้วย เพื่อไปทำงานต่อ โดยจะกำหนดขนาดให้คลุมความกว้างรวมก่อน แล้วจึงย้อนมากำหนดความหนาของเส้นตั้งตัวหลังอีกครั้งหนึ่ง

mirp[rp0,rnd,grey]  w_881 25
mirp[min,rnd,grey]  hstem  1

กั้นเป็นฃ่องไฟด้านหลัง โดยไม่ต้องกำหนดระยะ min และเผื่อการแรเงาเป็น white เพื่อให้มีช่องว่างอย่างน้อย 1 จุดคั่นกับอักษรตัวถัดไป มีประโยชน์สำหรับฟอนต์ที่มีการทดลงจุดในแนวแกน X มาก ๆ ซึ่งจะทำให้ล้นความกว้างที่มีอยู่ (คำสั่งนี้คิดเอง เพื่อให้ไม่ต้องทำ delta hint ตามแบบของ DejaVu)

mdrp[rnd,white]     27

เกลี่ยจุดที่เรายังไม่ได้อ้างอิงในแนวแกน X ซึ่งจะใช้คำสั่งสี้ปิดท้ายเสมอ (IUP คือ Interpolate Untouched Point)

IUP[x]

แนวแกน Y
เส้นทาง hint ในแนวแกน Y

เริ่มย้ายมาแกน Y

SVTCA[y-axis]

จะเริ่มต้นที่จุด 1 ในแนวแกน Y นี้ เรายังไม่ได้ทำอะไรเลย จึงควรปัดให้ลงจุดเสียก่อน

mdap[rnd]           1

บอกว่าจุด 13 อยู่ในระดับเดียวกับจุด 1

alignrp             13

กำหนดความสูงของฟอนต์

mirp[rp0,rnd,grey]  vheight_shoot    22

กำหนดความหนาของเส้นในแนวนอน

mirp[min,rnd,grey]  vstem_curve  5

กำหนดให้ rp1 เป็นจุด 5 และ rp2 เป็นจุด 13 เพื่อจะเกลี่ยจุดที่สำคัญอื่น ๆ โดยใช้ rp1 และ rp2 เป็นจุดอ้างอิงในการเฉลี่ยค่า

srp1                5
srp2                13
sloop               10
ip      19 8 18 9 17 10 2 25 15 12

เกลี่ยจุดที่เหลือ ในแนวแกน Y

IUP[y]

เมื่อนำโค๊ดที่เขียนมาถอดด้วยสคริปต์ dfont.py จะได้โค๊ด instruction ดังนี้

NPUSHB
 15
 27
 1
 8
 25
 163
 12
 8
 14
 18
 17
 10
 8
 18
 11
 26
SRP0
MIRP[rp0,min,rnd,grey]
MDRP[min,rnd,grey]
MDRP[rp0,rnd,grey]
MDRP[min,rnd,grey]
SRP0
MDRP[rp0,rnd,grey]
MIRP[min,rnd,grey]
MIRP[rp0,rnd,grey]
MIRP[min,rnd,grey]
MDRP[rnd,white]
IUP[x]
NPUSHB
 19
 12
 15
 25
 2
 10
 17
 9
 18
 8
 19
 10
 13
 5
 5
 185
 22
 184
 13
 1
SVTCA[y-axis]
MDAP[rnd]
ALIGNRP
MIRP[rp0,rnd,grey]
MIRP[min,rnd,grey]
SRP1
SRP2
SLOOP
IP
IUP[y]

จบแล้วครับ

fonts: ตัวอย่างการ hint ข.ไข่

ค่า cvt ที่เกี่ยวข้องคือ

cvt_dict = {
    ...
    "w_690":            14,     #690    =width of kho_khai
    "hstem":            8,      #184    =horizontal stem
    "vstem":            96,     #154    =vertical stem
    "headstem":         258,    #116    =head stem
    "headstem_plus_hole":   259, #268   =head stem + hole width
    "headdia":          257,    #384    =head diameter
    "vheight":          188,    #1120   =normal height 
    "vheight_shoot":    184,    #1147   =overshoot height
    "front_khai":       32,     #51     =front spacing of kho_khai
    ...
}

ภาพลายเส้น
โครง ข.ไข่

แนวแกน X
เส้นทาง hint ในแนวแกน X

ตัวอย่างนี้เริ่มซับซ้อน เนื่องจากช่วงหัวของ ข.ไข่ มีทางเดินที่ต้องการทดลงจุดมาก ทำให้ระยะที่ได้มีค่าไม่แน่นอนตามขนาดฟอนต์ที่เปลี่ยนไป ผลที่ได้คือ
ถ้าเราทำให้ฟอนต์มีขนาดคงที่ จะทำให้บางครั้งหัวดูลีบหรือใหญ่เกินไป
ถ้าเราทำให้หัวดูสวยงาม บางครั้งฟอนต์จะดูกว้างหรือลีบเกินไป
ทางแก้คือเราจะ hint ให้ช่วงหัวให้ดูสวยงามก่อน แล้วโยงไปเส้น stem ด้านหลัง แล้วจึงโยงกลับมาที่ stem ด้านหน้าอีกครั้ง โดยใช้ตรงโค้งช่วงคอของอักษรเป็นช่วงที่รับระยะการเปลี่ยนแปลง วิธีนี้ทำให้เกิดผลข้างเคียงน้อยที่สุด (แต่ก็ยังมีอยู่ดี ต้องแก้ต่อไปด้วย delta hint สำหรับขนาดฟอนต์ที่ดูไม่งาม)

เริ่มต้นที่จุด 53

srp0                53

เคลื่อนไปจุดใกล้ทีสุดด้วยระยะ front_khai

mirp[rp0,min,rnd,grey]  front_khai 21

จัดการหัวก่อน ด้วยการกำหนดขนาดเส้นหัว เคลือนไปอีกด้านหนึ่ง แล้วกำหนดขนาดย้อนกลับอีกครั้ง

mirp[min,rnd,grey]  headstem 50
mirp[rp0,rnd,grey]  headdia 15
mirp[min,rnd,grey]  headstem 44

กำหนดจุดคอดที่จุด 12

mdrp[rnd,grey]      12

เคลื่อนไปที่จุด 7 ด้วยการแรเงาแบบ white เพื่อแยกหัวออกจากคออักษรให้ชัดเจน

mdrp[rp0,rnd,white] 7

เคลื่อนไปต่อ เพื่อกำหนดความกว้างของคอ ให้เท่ากับระยะ vstem (ยืมค่า vstem ซึ่งน้อยกว่า hstem มาใช้สำหรับคออักษร)

mirp[rp0,rnd,grey]  vstem 27

เคลื่อนไปต้อ ด้วยการแรเงาแบบ white เพื่อแยกเส้นหน้าและเส้นหลังจากกัน

mdrp[rp0,rnd,white] 36

เคลื่อนต่อ กำหนดขนาดของเส้นหลัง

mirp[rp0,min,rnd,grey] hstem 38

กำหนดระยะกั้นหลัง

mdrp[rnd,white]     54

เคลื่อนย้อนกลับมาที่จุด 1 เพื่อกำหนดความกว้างหลักของอักษร ให้เท่ากับระยะ w_690

mirp[rp0,rnd,grey]  w_690 1

กำหนดความกว้างของเส้นหน้า

mirp[min,rnd,grey]  hstem 32

เกลี่ยจุดที่เหลือ จบการทำงานในแกน X

IUP[x]

แนวแกน Y
เส้นทาง hint ในแนวแกน Y

ทำงานในแกน Y

SVTCA[y-axis]

ปัดเศษลงจุด 1

mdap[rnd]           1

กำหนดความสูงปกติของเส้นหลัง

mirp[rnd,grey]      vheight 38

กำหนดความหนาของเส้นแนวนอนให้เท่ากับระยะ vstem

mirp[min,rnd,grey]  vstem 33

เคลื่อนไปเพื่อกำหนดความสูงหลอกตา (overshoot) ของหัว

mirp[rp0,rnd,grey]  vheight_shoot 24

เคลื่อนจุด กำหนดความหนาของคอแนวนอน โดยกำหนดให้เท่ากับความหนาของหัว คือ headstem

mirp[rp0,rnd,grey]  headstem 10

ย้ายมากำหนดส่วนคอด

mdrp[rp0,rnd,white] 12

ตรงนี้ เราจะเคลื่อนลงไปใต้สุดของหัวเลย เพื่ออาศัยพื้นที่ส่วนบนของหัว เป็นระยะรองรับการทดจุด (มีพื้นที่ดำมากกว่าส่วนอื่น) แล้วจึงกำหนดจุดของความหนาหัวและรูของหัวอักษรจากด้านล่าง

mdrp[rp0,rnd,grey]  18
mirp[min,rnd,grey]  headstem 41
mirp[rnd,grey]      headstem_plus_hole 47

เกลี่ยจุดเป็นคำสั่งสุดท้าย

IUP[y]

เสร็จแล้ว
ถอดด้วย dfont.py จะได้โค๊ด instruction ดังนี้

NPUSHB
 12
 32
 8
 1
 14
 54
 38
 8
 36
 27
 96
 7
 44
PUSHW_1
 258
PUSHB_2
 12
 15
PUSHW_1
 257
PUSHB_1
 50
PUSHW_1
 258
PUSHB_3
 21
 32
 53
SRP0
MIRP[rp0,min,rnd,grey]
MIRP[min,rnd,grey]
MIRP[rp0,rnd,grey]
MDRP[rnd,grey]
MIRP[min,rnd,grey]
MDRP[rp0,rnd,white]
MIRP[rp0,rnd,grey]
MDRP[rp0,rnd,white]
MIRP[rp0,min,rnd,grey]
MDRP[rnd,white]
MIRP[rp0,rnd,grey]
MIRP[min,rnd,grey]
IUP[x]
PUSHB_1
 47
PUSHW_1
 259
PUSHB_1
 41
PUSHW_1
 258
PUSHB_3
 18
 12
 10
PUSHW_1
 258
PUSHB_7
 24
 184
 33
 96
 38
 188
 1
SVTCA[y-axis]
MDAP[rnd]
MIRP[rnd,grey]
MIRP[min,rnd,grey]
MIRP[rp0,rnd,grey]
MIRP[rp0,rnd,grey]
MDRP[rp0,rnd,white]
MDRP[rp0,rnd,grey]
MIRP[min,rnd,grey]
MIRP[rnd,grey]
IUP[y]

fonts: ตัวอย่างการ hint ค.ควาย

ค่า cvt ที่เกี่ยวข้องคือ

cvt_dict = {
    ...
    "w_1001":           71,     #1001   =width of kho_kwai
    "w_690":            14,     #690    =
    "front_kai":        11,     #113    =front spacing of ko_kai
    "hstem":            8,      #184    =horizontal stem thick
    "headstem":         258,    #116    =head stem thick
    "headstem_plus_hole":   259, #268   =head stem thick + head hole width
    "vheight_shoot":    184,    #1147   =overshoot height
    "vstem_curve":      185,    #156    =curve range vertical stem
    ...
}

ภาพลายเส้น
โครง ค.ควาย

แนวแกน X
เส้นทาง hint ในแนวแกน X

เริ่มที่จุด 52

srp0                52

เคลื่อนไปที่จุดใกล้สุด 33 ด้วยระยะ front_kai

mirp[rp0,min,rnd,grey] front_kai 33

กำหนดความหนาของเส้นหน้า ที่จุด 8

mirp[min,rnd,grey]  hstem 8

เคลื่อนไปกำหนดความกว้างของตัวอักษรที่จุดขวาสุด 39 ด้วยความกว้าง w_1001

mirp[rp0,rnd,grey]  w_1001  39

กำหนดช่องไฟหลัง

mdrp[rnd,white]     53

กำหนดความหนาของเส้นหลัง ที่จุด 1

mirp[min,rnd,grey]  hstem 1

มาเริ่มกันใหม่ที่จุด 8

srp0                8

เคลื่อนลงข้างล่าง สู่จุด 11 (เพื่อจะนำไปสู่ฐานของเส้นหน้า) ให้การแรเงาเป็น white เพื่อแยกเส้นหน้ากับคอของหัวอักษร

mdrp[rp0,rnd,white] 11

เนื่องจากเส้นตรงนี้บางมาก จึงกำหนดจุดเพื่อคงระยะอย่างน้อย ที่จุด 27 กำหนดเป็น min

mdrp[min,rnd,grey]  27

จากจุด 11 เคลื่อนไปลงจุด 30

mdrp[rp0,rnd,grey]  30

กำหนดความหนาของฐาน ที่จุด 29

mirp[min,rnd,grey]  hstem 29

กลับไปที่จุด 11

srp0                11

เคลื่อนไปที่จุด 14 ด้วยการแรเงา white อีกครั้ง เพื่อแยกเส้นให้เด็ดขาด

mdrp[rp0,rnd,white] 14

คงความหนาของส่วนคอดไว้

mdrp[min,rnd,grey]  25

ย้อนกลับไปตั้งต้นที่จุด 33 อีกครั้งหนึ่ง เพื่อจะสร้างหัวอักษร โดยให้ระยะ จาก 14-49 เป็นตัวรองรับการทดจุด

srp0                33

เพื่อความแน่นอนแม่นยำ เราจะเคลื่อนจุดไปหาจุดขวาสุดของหัวอักษร โดยกำหนดค่าเป็น w_690
(จำเป็นต้องให้แม่นยำ เนื่องจากช่องว่างตรงหัวอักษรมีจำกัดมาก) แล้วจึงกำหนดขนาดความหนาหัวอักษรและรูย้อนกลับอีกที

mirp[rp0,rnd,grey]  w_690   20
mirp[min,rnd,grey]  headstem 43
mirp[rnd,grey]      headstem_plus_hole 49

เกลี่ยจุดเป็นคำสั่งสุดท้าย

IUP[x]

แนวแกน Y
เส้นทาง hint ในแนวแกน Y

ทำงานในแกน Y

SVTCA[y-axis]

เริ่มที่จุด 0

MDAP[rnd]           0

บอกว่าจุด 29 อยู่ในระดับเดียวกัน

ALIGNRP             29

เคลื่อนไปกำหนดจุดสูงสุดด้วยค่า vheight_shoot

mirp[rp0,rnd,grey]  vheight_shoot  36

กำหนดความหนาตรงส่วนนี้ด้วยค่า vstem_curve

mirp[min,rnd,grey]  vstem_curve  5

จากจุด 36 เดิม เคลื่อนไปส่วนบนของหัวอักษรที่จุด 17 เนื่องจากไม่กังวลระยะมากนัก จึงใช้การเคลื่อนแบบไม่อ้างค่า cvt คือคำสั่ง MDRP
(เหตุที่ใช้จุด 36 แทนที่จะเป็นจุด 1 หรือ จุด 0 ในการอ้างอิงสำหรับหัวอักษร ค.ควาย เนื่องจากความสวยงามในการวาดหัวอักษร จะขึ้นกับโค้งด้านบนมากว่าฐานด้านล่าง)

mdrp[rp0,rnd,grey]  17

กำหนดขนาดหัวโดยใช้เทคนิกเหมือนเดิม คือกำหนดความหนาด้านบน เคลื่อนไปกำหนดขนาดรวม และกำหนดความหนาด้านล่าง

mirp[min,rnd,grey]  headstem  46
mirp[rp0,rnd,grey]  headdia  23
mirp[min,rnd,grey]  headstem  40

ย้ายมาที่จุด 29 เพื่อมากำหนดความหนาตรงจุดนั้น

srp0                29
mdrp[min,rnd,grey]  11

เกลี่ยจุดสำคัญ โดยให้ท้องตัวอักษรเป็นจุดเริ่มต้น คือจุด 5 และความหนาของฐานเป็นจุดสิ้นสุด คือจุด 11

srp1                5
srp2                11
sloop               4
ip                  33 8 2 39

เกลี่ยจุดที่เหลือ

IUP[y]

เสร็จแล้ว
แต่ถึงแม้จะป้องกันอย่างไร รูปร่างฟอนต์แบบตัว ค.ควาย จะยังมีปัญหาในตอนทดจุดอยู่ดี
คือที่ขนาด 17 ppem หัวอักษรจะชิดติดกับแกนขวาของอักษร
(สรุปเป็นปอยต์ตามความละเอียดของจอภาพคือ 72dpi=17pt, 85dpi=14.5pt, 96dpi=13pt)
ดูจากเมนู View -> Grid Fit -> Show Grid Fit -> 17pt 72dpi ได้ภาพดังนี้
17ppem gridfit

แก้ด้วยการขยับจุดจำนวน 8 จุด ในแนวแกน X คือ
จุด 2 ขยับไปทางขวา 8px คือ +8px ที่ 17ppem สูตรคือ
+8px ค่าในตาราง delta hint คือ 15

No.of step -8 -7 -6 -5 -4 -3 -2 -1 +1 +2 +3 +4 +5 +6 +7 +8
Selector 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15

17ppem นำมาลบด้วย 9 (ลบด้วย 9 เสมอ) คือ 6
ค่า 6 จะเป็น 4 บิตบน และค่า 15 คือ 4 บิตล่าง รวมกันคือ 6*16+15=143
จะได้คู่ของการทำ delta hint คือ จุด 2 และค่าผลรวม 143

เขียนสรุปย่อดังนี้
ที่ 17 ppem
จุด 2 ขยับ +8 = 2 143
จุด 39 ขยับ +8 = 39 143
จุด 0 ขยับ +8 = 0 143
จุด 1 ขยับ +8 = 1 143
จุด 3 ขยับ +6 = 1 141
จุด 38 ขยับ +6 = 38 141
จุด 37 ขยับ +4 = 37 139
จุด 4 ขยับ +4 = 4 139
นับรวมได้ 8 คู่ จะได้โค๊ดจำลองดังนี้

กำหนดการทำงานในแกน X

SVTCA[x-axis]

ใส่ delta hint โดยใส่จำนวนคู่ไว้เป็นค่าแรก

deltap1             8 2 143 39 143 0 143 1 143 3 141 38 141 37 139 4 139

ภาพหลังการขยับแล้วจะป็นดังนี้
17ppem after gridfit

เสร็จแล้ว
เมื่อนำมาถอดด้วย dfont.py จะได้โค๊ด instruction ดังนี้

PUSHB_1
 49
PUSHW_1
 259
PUSHB_1
 43
PUSHW_1
 258
NPUSHB
 22
 20
 14
 33
 25
 14
 11
 29
 8
 30
 27
 11
 8
 1
 8
 53
 39
 71
 8
 8
 33
 11
 52
SRP0
MIRP[rp0,min,rnd,grey]
MIRP[min,rnd,grey]
MIRP[rp0,rnd,grey]
MDRP[rnd,white]
MIRP[min,rnd,grey]
SRP0
MDRP[rp0,rnd,white]
MDRP[min,rnd,grey]
MDRP[rp0,rnd,grey]
MIRP[min,rnd,grey]
SRP0
MDRP[rp0,rnd,white]
MDRP[min,rnd,grey]
SRP0
MIRP[rp0,rnd,grey]
MIRP[min,rnd,grey]
MIRP[rnd,grey]
IUP[x]
NPUSHB
 10
 39
 2
 8
 33
 4
 11
 5
 11
 29
 40
PUSHW_1
 258
PUSHB_1
 23
PUSHW_1
 257
PUSHB_1
 46
PUSHW_1
 258
PUSHB_7
 17
 5
 185
 36
 184
 29
 0
SVTCA[y-axis]
MDAP[rnd]
ALIGNRP
MIRP[rp0,rnd,grey]
MIRP[min,rnd,grey]
MDRP[rp0,rnd,grey]
MIRP[min,rnd,grey]
MIRP[rp0,rnd,grey]
MIRP[min,rnd,grey]
SRP0
MDRP[min,rnd,grey]
SRP1
SRP2
SLOOP
IP
IUP[y]
NPUSHB
 17
 139
 4
 139
 37
 141
 38
 141
 3
 143
 1
 143
 0
 143
 39
 143
 2
 8
SVTCA[x-axis]
DELTAP1

fonts: บันทึกการทดลองทำ DejaVuSerifThai อีกรุ่น

บันทึกการสร้างฟอนต์ DejaVu Serif Thai

  • ตัวนี้ทดลองทำ hinting เลียนแบบวิธีของ Tahoma คือ
    คงระยะคงที่ช่องไฟหน้าและหลังไว้ และให้ความกว้างของตัวอักษรเป็นตัวรองรับระยะการทดจุดที่เปลี่ยนไปมา ซึ่งบังคับว่าต้องทำ grid fitting (delta hint) เพื่อกำหนดความกว้างของฟอนต์ก่อน แล้วจึงเริ่มเดินจุด
    การทำ delta hint ก่อน จำเป็นมากสำหรับฟอนต์ขนาดเล็ก ซึ่งมีอัตราการเพี้ยนของความกว้างของอักษรมาก ไหน ๆ ก็ต้องทำ delta hint อยู่แล้ว เที่ยวนี้จึงพยายามลงลึกไปทำถึงขนาดหัวอักษรด้วย ผลที่ได้คืออ่านง่ายขึ้น โดยเฉพาะกับฟอนต์ขนาดเล็ก ๆ แต่ก็ต้องแลกมาด้วยการเสียเวลาและสายตาเป็นอย่างมาก
    (การทำงานต้องปรับขนาดจอภาพให้มีความละเอียด 72 dpi เพื่อให้รองรับขนาดฟอนต์ได้ทุก ๆ ppem บวกกับการเพ่งกำหนดจุดที่เปลี่ยน จึงเปลืองสายตามาก)
  • รูปแบบฟอนต์ของ DejaVu Serif ดูไม่เป็น serif จัดนัก จึงนำเอาฟอนต์ Angsima มาแปลงใส่เข้าไป
    เนื่องจากเห็นว่า ภาษาไทยทำ serif แท้ ๆ ไม่สวย จึงพยายามทำเท่าที่ทำได้ คือ hint เฉพาะพยัญชนะ ส่วนสระบนและล่างไม่ hint และไม่แปลงรูปแบบเลย

ผลการทดลอง

  • เนื่องจากมีการทำ grid fitting (เกือบ)เต็มที่ จึงดูดีที่ฟอนต์ขนาดเล็ก ๆ ด้วย (หรือเปล่า?)
  • สระบนล่าง ทำ hint แล้วไม่สวย จึงตัดสินใจไม่ hint เพียงแค่กำหนดระยะในแกน Y เล็กน้อย ผลที่ได้คือแนวเส้นบรรทัดที่เป็นพญัญชนะจะดูคมชัดกว่าแนวสระบนล่าง - ไม่รู้ว่าดีหรือเปล่า รอดูผลการใช้งานต่อไป
  • แบบอักษรดูคล้าย sans ไปหน่อย โดยเฉพาะกับฟอนต์ขนาดเล็ก ยกเว้นสระบนล่างที่เป็นแบบของ Angima ชัดเจน ซึ่งอาจทำให้ดูไม่กลมกลืน

ปรับปรุง
(เนื่องจากเป็นการทำงานไปด้วย ศึกษาไปด้วย การทำ hinting จึงไม่เป็นอันหนึ่งอันเดียวกันในทุก ๆ ฟอนต์ที่ทำ ต้องขออภัยด้วย)

  • 51-05-06 ทำตัวหนาเพิ่มเ