ข้ามไปยังเนื้อหา

โมดูล 0 จาก ทำฟอนต์ไทยให้ขายได้ · เปิดอ่านฟรีทั้งบท

Module 0 — ฟอนต์ตัวแรกของคุณใน 45 นาที

เมื่ออ่านจบคุณจะได้: ไฟล์ .ttf และ .woff2 ที่ใช้ชื่อของคุณเอง ติดตั้งและพิมพ์ได้จริง

ฟรี · ไม่ต้องกรอกอีเมล

ดาวน์โหลดชุดไฟล์เริ่มต้น

9 ไฟล์ — สคริปต์สามตัวที่โมดูลนี้เรียกใช้ ไฟล์ config ที่สคริปต์อ่าน และฟอนต์แปรผัน Anuphan ตัวจริงที่ใช้ทำงานด้วย คำสั่งทุกบรรทัดในหน้านี้ใช้กับชุดนี้ได้ตั้งแต่ต้นจนจบ

ดาวน์โหลด font-factory-starter.zip ↓

ต้องมี Python 3.11 ขึ้นไป — ขั้นที่ 1 ในหน้านี้พาติดตั้งทีละขั้น

จบโมดูลนี้คุณจะมีไฟล์ฟอนต์จริงสี่น้ำหนัก คือ Light Regular Medium และ Bold ทั้งในรูปแบบ .ttf สำหรับติดตั้งบนเครื่องและ .woff2 สำหรับเว็บ โดยชื่อครอบครัวฟอนต์ ชื่อผู้ผลิต ชื่อผู้ออกแบบ URL และ vendor ID ในไฟล์เป็นของคุณ ไม่ใช่ของใคร คุณจะรันชุดตรวจสอบ 52 ข้อที่พิสูจน์ว่าวรรณยุกต์ยังเกาะถูกที่ แล้วติดตั้งฟอนต์นั้นบนเครื่องตัวเองและพิมพ์ภาษาไทยด้วยมัน ทั้งหมดนี้ไม่ต้องเปิดโปรแกรมวาดฟอนต์แม้แต่ตัวเดียว และไม่ต้องเสียค่าซอฟต์แวร์สักบาท

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

ข้อตกลงเรื่องชื่อ บอกครั้งเดียวเพื่อให้ชุดเครื่องมือทั้งชุดอ่านแล้วไม่สับสน ThaiGraph Condensed คือฟอนต์ที่ผมใช้งานจริง เป็นตัวที่มากับ config.json ในชุดเครื่องมือ และเป็นตัวที่ผลลัพธ์จริงของมันปรากฏอยู่ทั่วโมดูล 3 กับโมดูล 4 ส่วน Somsak Display คือตัวอย่างที่ คุณ สร้างในหน้านี้ เพื่อไม่ให้อะไรในโฟลเดอร์ dist/ ของคุณปนกับของผมได้ ตรงไหนที่โมดูลหลัง ๆ ยกชื่อไฟล์อย่าง ThaiGraphCondensed-Regular.ttf ขึ้นมา นั่นคือไฟล์ของผมที่เอามาแสดงเป็นหลักฐาน ของคุณจะถูกตั้งชื่อตามค่าที่คุณใส่ใน ps_family


อ่านก่อนลงมือ: กับดักลิขสิทธิ์ที่จะทำให้คุณเดือดร้อน

ฟอนต์ที่คุณกำลังจะสร้างในโมดูลนี้ ขายเดี่ยว ๆ ไม่ได้ และไม่ใช่เพราะมันไม่ดีพอ แต่เพราะสัญญาอนุญาตห้ามไว้ตรง ๆ ค่าเริ่มต้นในไฟล์ config.json ชี้ไปที่ Anuphan ซึ่งเป็นฟอนต์แปรผัน (variable font) ที่เผยแพร่ภายใต้ SIL Open Font License 1.1 โดย Cadson Demak เมื่อคุณนำมันมาบีบให้แคบลงแล้วเปลี่ยนชื่อ สิ่งที่ได้คือ “ฉบับดัดแปลง” (Modified Version) ของฟอนต์ OFL และ OFL ข้อ 1 เขียนไว้แบบนี้:

“Neither the Font Software nor any of its individual components, in Original or Modified Versions, may be sold by itself.”

SIL Open Font License 1.1, PERMISSION & CONDITIONS ข้อ 1 (เข้าถึง 15 ส.ค. 2569)

แปลเป็นภาษาที่ใช้ตัดสินใจได้: ตัวซอฟต์แวร์ฟอนต์ก็ดี ส่วนประกอบใด ๆ ของมันก็ดี ไม่ว่าจะเป็นฉบับต้นทางหรือฉบับที่คุณดัดแปลงแล้ว จะเอาไป “ขายโดยลำพัง” ไม่ได้ ห้ามลงขายบน MyFonts ห้ามลง Creative Market ห้ามตั้งราคาบน Gumroad ในฐานะไฟล์ฟอนต์เปล่า ๆ

สิ่งที่คุณ ทำได้ คือแจกฟรี แนบไปกับซอฟต์แวร์หรือผลิตภัณฑ์ ฝังลงในเว็บไซต์หรือแอปเชิงพาณิชย์ และใช้ทำงานลูกค้าได้เต็มที่ นั่นทำให้มันเป็น lead magnet ที่ดีมาก แต่เป็นสินค้าไม่ได้

scripts/build.py พิมพ์คำเตือนนี้ทุกครั้งที่รัน เพื่อไม่ให้ใครมารู้ตัวหลังจากกดลงขายไปแล้ว ถ้าเป้าหมายของคุณคือ “ฟอนต์ที่ขายได้” คุณต้องมี outline ที่คุณสร้างเองทั้งหมด ซึ่งเป็นเรื่องของ Module 6 และเหตุผลทางกฎหมายทั้งหมดอยู่ใน Module 2 อ่านสองโมดูลนั้นก่อนตั้งราคาอะไรทั้งสิ้น


สิ่งที่ต้องมีก่อนเริ่ม

คุณต้องการแค่สามอย่าง: เครื่องที่ต่อเน็ตได้ โฟลเดอร์ font-factory/ จากชุดเครื่องมือนี้ที่แตกไฟล์ไว้ในที่ที่คุณหาเจออีกครั้ง และความอดทนกับหน้าจอดำ ไม่ต้องมี Glyphs ไม่ต้องมี FontLab ไม่ต้องรู้จัก Python มาก่อน คำสั่งทุกบรรทัดในหน้านี้ก๊อปวางได้ตรง ๆ

ขั้นที่ 0 — ดาวน์โหลดและแตกไฟล์ชุดเครื่องมือ

กดปุ่มดาวน์โหลดบนหน้านี้ หรือถ้าคุณเปิดหน้านี้มาจากหน้าสารบัญของชุดเครื่องมือ ก็กดปุ่มบนหน้านั้น ไฟล์ zip มีชุดเดียว จะได้ชุดไหนขึ้นอยู่กับว่าคุณอยู่ตรงไหน:

  • ชุดเริ่มต้นฟรีfont-factory-starter.zip เก้าไฟล์ ได้แก่ สคริปต์สามตัวที่โมดูลนี้เรียกใช้ ไฟล์ config ที่สคริปต์อ่าน และฟอนต์แปรผันตัวจริงที่ใช้ทำงานด้วย ไม่ต้องซื้อ ไม่ต้องกรอกอีเมล คำสั่งทุกบรรทัดในหน้านี้ใช้กับชุดนี้ได้ตั้งแต่ต้นจนจบ
  • Corefont-factory-core.zip ซึ่งเพิ่ม publish-webfont.py, apply-fea.py และ check-shaping.py
  • Profont-factory-pro.zip ซึ่งบรรจุทุกอย่างที่อยู่ในชุด Core บวกเอนจินออกแบบต้นฉบับและเทมเพลตสัญญาอนุญาต

ผู้ซื้อ Pro ดาวน์โหลดไฟล์เดียว ไม่ใช่สองไฟล์ และไม่มีอะไรในโมดูลนี้ที่ต้องใช้ของที่มีเฉพาะในชุด Core หรือ Pro — นั่นคือเหตุผลที่โมดูลนี้เป็นโมดูลฟรี

แตกไฟล์ออกมา เพื่อให้คำสั่งทุกบรรทัดในหน้านี้ก๊อปวางได้ตรง ๆ ให้แตกลงในโฟลเดอร์ Documents แล้วคุณจะได้แบบนี้:

Documents/
  font-factory/
    README.md
    requirements.txt
    config.json
    scripts/       build.py, qa.py, specimen.py
                   (ชุด Core และ Pro เพิ่ม publish-webfont.py,
                    apply-fea.py และ check-shaping.py มาด้วย
                    ซึ่งโมดูลนี้ไม่ได้ใช้สักตัว)
    sources/       Anuphan[wght].ttf, OFL-Anuphan.txt
  LICENCE-toolkit-code.txt

นั่นคือสิบสองไฟล์ และหกไฟล์ในนั้นเป็นสคริปต์ สี่ตัวคุณจะได้ใช้ในโมดูลนี้ อีกสองตัวเป็นของเส้นทางฟอนต์ต้นฉบับ apply-fea.py แนบฟีเจอร์ OpenType ภาษาไทยเข้ากับฟอนต์ที่คุณวาดเอง ส่วน check-shaping.py พิสูจน์ว่ามันทำงานจริง โมดูล 6 และโมดูล 7 เป็นตัวที่ใช้สองไฟล์นี้ ทั้งคู่อยู่ในชุด zip ของ Core ไม่ใช่เฉพาะชุด Pro เพราะเส้นทางไปสู่ฟอนต์ที่คุณขายได้ต้องครบตั้งแต่ชั้น Core

Windows — คลิกขวาที่ไฟล์ .zip ที่ดาวน์โหลดมา เลือก Extract All… แล้วตั้งปลายทางเป็น C:\Users\<ชื่อผู้ใช้ของคุณ>\Documents อย่าข้ามขั้นแตกไฟล์แล้วทำงานในหน้าต่างพรีวิวของ zip เพราะ Windows ยอมให้คุณเปิดไฟล์ในนั้นได้ก็จริง แต่ Python เขียนลงไปไม่ได้ และ build จะพังในแบบที่ดูเหมือนปัญหาของ Python

macOS — ดับเบิลคลิกไฟล์ .zip ใน ~/Downloads แล้วลากโฟลเดอร์ font-factory ที่ได้ไปวางใน ~/Documents

ถ้าคุณวางไว้ที่อื่นก็ไม่เป็นไร คำสั่ง cd ทุกบรรทัดข้างล่างแค่ต้องชี้ไปยังที่ที่คุณวางไว้จริง

scripts/ คือไปป์ไลน์ build ครบชุด มันแปลงฟอนต์ต้นทางให้กลายเป็นผลลัพธ์ที่ตั้งชื่อแล้ว บีบแล้ว ผ่าน QA แล้ว และแพ็กพร้อมส่ง และมันอยู่ในชุด zip ทั้งสองชุด คำสั่งทุกคำสั่งในโมดูลนี้รันกับมัน โมดูล 3 และโมดูล 4 ก็เช่นกัน ส่วนชุด Pro เพิ่มโฟลเดอร์ที่สองชื่อ original/ ซึ่งเป็นเอนจินพารามิเตอร์สำหรับเขียนเส้นขอบขึ้นใหม่ในรูปของข้อมูล มันเป็นตัวช่วยเร่งของโมดูล 6 ไม่ใช่ชิ้นส่วนที่ขาดหายไป ตรงไหนที่โมดูลหลัง ๆ หยิบไฟล์ใน original/ มาใช้ มันจะบอกไว้ในกล่องที่ทำเครื่องหมายไว้ พร้อมระบุเส้นทางฝั่ง Core ที่ไปถึงผลลัพธ์เดียวกัน ไม่มีอะไรในหน้านี้ที่ต้องใช้มัน

จริง ๆ แล้วใช้เวลาเท่าไร — อ่านก่อนเริ่มจับเวลา

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

เวลาที่เพิ่มมาไปอยู่ตรงไหน หน้านี้เขียนไว้ครบทุกจุด: การดาวน์โหลดและติดตั้ง Python เอง (10–20 นาที ส่วนใหญ่ขึ้นกับความเร็วเน็ตของคุณ), ทางอ้อมเรื่อง Microsoft Store alias บน Windows ในขั้นที่ 1 (5–15 นาที), ทางอ้อมเรื่อง execution policy ของ PowerShell ในขั้นที่ 2 (5 นาที) และ pip install ที่ต้องคอมไพล์ wheel เองแทนที่จะโหลดตัวสำเร็จรูปในขั้นที่ 3 (5–15 นาที และเกิดเฉพาะบางเครื่อง)

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


ขั้นที่ 1 — ติดตั้ง Python และยืนยันว่ามันอยู่ใน PATH

นี่คือขั้นตอนที่คนพังมากที่สุด และเกือบทุกครั้งไม่ใช่เพราะติดตั้งไม่สำเร็จ แต่เพราะเทอร์มินัลหาโปรแกรมที่ติดตั้งไปแล้วไม่เจอ ไปป์ไลน์นี้ต้องการ Python 3.11 ขึ้นไป และถูกพัฒนาและทดสอบบน 3.13.2

ดาวน์โหลดจาก python.org/downloads เวอร์ชันเสถียรที่หน้านั้นเสนอ ณ วันที่ 15 ส.ค. 2569 คือ 3.14.7 (python-3.14.7-amd64.exe สำหรับ Windows, python-3.14.7-macos11.pkg สำหรับ macOS) ถ้าคุณอยากเดินทางที่ราบรื่นที่สุด ให้เลือกสาย 3.13.x จากหน้า All releases เพราะนั่นคือเวอร์ชันที่ dependency ทุกตัวถูกปักหมุดไว้และทดสอบแล้ว

Windows — ตอนติดตั้ง ต้องติ๊ก “Add python.exe to PATH” ที่หน้าจอแรก ถ้าพลาดตรงนี้ ให้รันตัวติดตั้งใหม่แล้วเลือก Modify

ตรวจสอบ:

python --version
python3 --version

ต้องได้บรรทัดเดียวหน้าตาแบบ Python 3.13.2 ถ้าเลข minor ต่ำกว่า 11 ให้ติดตั้งใหม่

ถ้าติดตรงนี้ — Windows เด้งไป Microsoft Store แทนที่จะบอกเวอร์ชัน

Windows มี “app execution alias” ปลอมชื่อ python.exe ที่พาไปหน้าร้านค้า ปิดมันที่ Settings → Apps → Advanced app settings → App execution aliases แล้วปิดสวิตช์ python.exe และ python3.exe จากนั้น ปิดหน้าต่าง PowerShell แล้วเปิดใหม่ เพราะ PATH จะโหลดใหม่ตอนเปิดหน้าต่างเท่านั้น

ถ้ายังไม่ได้ ลอง py --version — คำสั่ง py คือ Python launcher ที่ตัวติดตั้งวางไว้ให้เสมอ ถ้า py ทำงานแต่ python ไม่ทำงาน ให้ใช้ py แทน python ในขั้นที่ 2 เท่านั้น หลังจากนั้นเราจะเรียกผ่าน venv ซึ่งไม่เกี่ยวกับ PATH อีกแล้ว


ขั้นที่ 2 — สร้างและเปิดใช้งาน venv

venv คือกล่องแยกที่เก็บไลบรารีของโปรเจกต์นี้ไว้ต่างหาก ไม่ให้ปนกับ Python ของระบบ ประโยชน์จริงไม่ใช่ความสะอาด แต่คือความสามารถในการทำซ้ำ เมื่อไฟล์ requirements.txt ปักหมุดเวอร์ชันไว้แล้ว venv รับประกันว่าเครื่องคุณกับเครื่องที่ทดสอบไปป์ไลน์นี้รันโค้ดชุดเดียวกันเป๊ะ

Windows (PowerShell)

cd $HOME\Documents\font-factory
python -m venv .venv
.\.venv\Scripts\Activate.ps1

macOS

cd ~/Documents/font-factory
python3 -m venv .venv
source .venv/bin/activate

สองบรรทัด cd นั้นตั้งอยู่บนสมมติฐานว่าคุณแตกไฟล์ลง Documents ตามที่บอกไว้ในขั้นที่ 0 ถ้าคุณวางโฟลเดอร์ไว้ที่อื่น ให้เปลี่ยนเป็นพาธนั้น ตั้งแต่ตรงนี้ไปทุกอย่างรันโดยมี font-factory/ เป็นโฟลเดอร์ปัจจุบัน และทุกโมดูลหลังจากนี้ก็ถือแบบเดียวกัน

เมื่อเปิดใช้งานสำเร็จ บรรทัดพรอมป์ต์จะขึ้นต้นด้วย (.venv) นั่นคือสัญญาณเดียวที่คุณต้องดู

ถ้าติดตรงนี้ — PowerShell ปฏิเสธด้วยข้อความ “running scripts is disabled on this system”

นั่นคือ Execution Policy ของ Windows ไม่ใช่ปัญหาของ Python สั่งบรรทัดนี้ก่อนแล้วค่อยสั่ง activate อีกครั้ง:

Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass

-Scope Process แปลว่ามีผลเฉพาะหน้าต่างนี้ ปิดหน้าต่างแล้วค่าเดิมกลับมาเอง เป็นทางที่ปลอดภัยที่สุด อย่าไปตั้ง -Scope LocalMachine

ทางเลี่ยงที่ง่ายกว่า: ไม่ต้อง activate เลย เรียก Python ในกล่องตรง ๆ ทุกครั้ง เช่น .\.venv\Scripts\python scripts\build.py ผลลัพธ์เหมือนกันทุกประการ


ขั้นที่ 3 — ติดตั้ง dependencies และทำไม brotli ถึงสำคัญ

คำสั่งเดียวจบ แต่ให้อ่านสิ่งที่มันติดตั้งด้วย เพราะหนึ่งในนั้นคือกับดักที่หลอกคนได้เก่งที่สุดในไปป์ไลน์นี้ ไฟล์ requirements.txt อยู่ในโฟลเดอร์ที่คุณแตกไว้ในขั้นที่ 0 (ทั้งสามชุดส่งไฟล์เดียวกันมาให้) คำสั่งนี้จึงทำงานได้โดยไม่ต้องดาวน์โหลดอะไรเพิ่ม

python -m pip install --upgrade pip
pip install -r requirements.txt
python -m pip install --upgrade pip
pip install -r requirements.txt

ขั้นที่ 2 อ้างไว้ว่า venv ทำให้เครื่องคุณรันโค้ดชุดเดียวกับเครื่องที่ทดสอบไปป์ไลน์นี้ นี่คือคำอ้างนั้นทั้งหมดในรูปที่ตรวจสอบได้ คือหมุดเวอร์ชันที่คัดออกมาจาก requirements.txt ตัวจริงที่ส่งมาให้คุณ:

fonttools==4.63.0
brotli==1.2.0
uharfbuzz==0.56.0
skia-pathops==0.9.2
freetype-py==2.5.1
pillow==12.3.0

== แปลว่าเวอร์ชันนั้นเป๊ะ ๆ ไม่ใช่ “เวอร์ชันนั้นหรือใหม่กว่า” ให้เปิด requirements.txt ด้วยตัวเองแล้วเทียบตัวเลขกับรายการนี้ก่อนติดตั้ง ถ้าไม่ตรงกัน ให้เชื่อไฟล์ในโฟลเดอร์ของคุณมากกว่าหน้านี้ เพราะไฟล์นั้นคือสิ่งที่ pip อ่าน

ถ้าวันหนึ่ง requirements.txt หายไป หรือคุณอยากติดตั้งลง venv อีกที่หนึ่งโดยไม่ต้องคัดลอกไฟล์ไปมา บรรทัดเดียวนี้ติดตั้งชุดเดียวกัน:

pip install fonttools==4.63.0 brotli==1.2.0 uharfbuzz==0.56.0 skia-pathops==0.9.2 freetype-py==2.5.1 pillow==12.3.0
pip install fonttools==4.63.0 brotli==1.2.0 uharfbuzz==0.56.0 skia-pathops==0.9.2 freetype-py==2.5.1 pillow==12.3.0

สิ่งที่ลงไปมีห้าตัวหลัก:

  • fontTools — อ่าน แปลง และเขียนไฟล์ฟอนต์ ทั้งไปป์ไลน์วางอยู่บนตัวนี้
  • uharfbuzz — HarfBuzz เอนจินจัดรูปข้อความ ใช้ใน qa.py เพื่อพิสูจน์ว่าวรรณยุกต์เกาะจริง
  • skia-pathops — รวมเส้นแบบบูลีน ใช้เฉพาะไปป์ไลน์ original/ ที่อยู่ในชุด Pro
  • freetype-py และ pillow — เรนเดอร์แผ่นพิสูจน์ภาพ ใช้เฉพาะ original/ เช่นกัน
  • brotli — บีบอัด WOFF2

brotli คือกับดัก ไม่มีไฟล์ไหนในไปป์ไลน์ import brotli เลยสักบรรทัด เอดิเตอร์ของคุณจะบอกว่ามันไม่ถูกใช้ และคุณจะอยากลบมันทิ้ง อย่าลบ fontTools ไปเรียกใช้มันเองตอนที่คุณสั่งเซฟไฟล์ .woff2 ถ้าไม่มี brotli งานจะพังที่ขั้นตอนเซฟ ไม่ใช่ขั้นตอนติดตั้ง และข้อความผิดพลาดที่ได้จะไม่บอกคุณว่าปัญหาคือ brotli

ยืนยันทั้งหมดด้วยบรรทัดเดียว:

python -c "import fontTools, uharfbuzz, brotli; print('ok')"
python -c "import fontTools, uharfbuzz, brotli; print('ok')"

ถ้าได้ ok แปลว่าพร้อม ถ้าไม่ได้ ให้แก้ตอนนี้ ไม่ใช่ตอนที่ build พัง

ถ้าติดตรงนี้ — pip ล้มด้วย SSLError / CERTIFICATE_VERIFY_FAILED

เกือบทั้งหมดเกิดจากเน็ตองค์กรที่มี proxy คั่นกลาง หรือ pip เวอร์ชันเก่าเกินไป ลำดับที่ควรลอง:

  1. python -m pip install --upgrade pip แล้วลองใหม่
  2. ถ้าอยู่ในเน็ตออฟฟิศหรือใช้ VPN ให้สลับไปใช้เน็ตมือถือแล้วลองใหม่ วิธีนี้แก้ได้บ่อยกว่าที่คิด
  3. ทางสุดท้ายเท่านั้น ถ้าจำเป็นจริง ๆ ใช้ pip install -r requirements.txt --trusted-host pypi.org --trusted-host files.pythonhosted.org ให้เข้าใจว่านี่คือการปิดการตรวจใบรับรอง ใช้เฉพาะกับเครื่องที่คุณควบคุมและอย่าให้มันกลายเป็นนิสัย

ถ้าติดตรงนี้ — pip ฟ้องว่า build wheel ไม่สำเร็จสำหรับ skia-pathops หรือ pillow

มักเกิดเมื่อคุณใช้ Python ที่ใหม่มากจนยังไม่มี wheel สำเร็จรูป ทางแก้ที่ตรงที่สุดคือถอยไปใช้ Python 3.13.x ซึ่งเป็นเวอร์ชันที่หมุดเวอร์ชันเหล่านี้ถูกทดสอบไว้ อีกทางคือแก้เฉพาะบรรทัดที่พังใน requirements.txt จาก == เป็น >= แล้วรัน python scripts/qa.py ผลของ QA คือสิ่งที่บอกว่าไปป์ไลน์ยังทำงานถูก ไม่ใช่ log ของการติดตั้ง

ยังมีทางลัดอีกทางที่ควรรู้ เพราะมันตัดทางอ้อมทั้งหมดทิ้งได้เลย ไม่มีไฟล์ไหนใน scripts/ ที่ import skia-pathops, freetype-py หรือ pillow: build.py, qa.py, specimen.py และ publish-webfont.py รวมกันแล้วต้องการแค่ fontTools, brotli และ uharfbuzz สามแพ็กเกจที่เหลือมีไว้ให้เอนจิน original/ ในชุด Pro ดังนั้นถ้าตัวไหนคอมไพล์ไม่ผ่านและวันนี้คุณยังไม่ได้ทำงานใน original/ ให้ลบบรรทัดของมันออกจาก requirements.txt แล้วติดตั้งใหม่ คุณไม่ได้เสียอะไรที่โมดูลนี้ใช้เลย


ขั้นที่ 4 — แก้ config.json ให้ฟอนต์เป็นของคุณ

scripts/build.py ไม่มีสตริงที่ระบุตัวตนฟอนต์อยู่ในไฟล์เลยแม้แต่ตัวเดียว ทุกอย่างที่บอกว่าฟอนต์นี้ชื่ออะไรและเป็นของใครอยู่ใน config.json ล้วน ๆ นั่นคือเหตุผลที่การชี้สคริปต์ไปที่ config ของคุณได้ฟอนต์ของคุณ ไม่ใช่ของคนอื่นที่ถูกแปะป้ายใหม่

เปิด config.json (มันอยู่ชั้นบนสุดของโฟลเดอร์ที่คุณแตกไฟล์ไว้ ข้าง ๆ README.md) แล้วแก้ทีละบรรทัด

ในตารางข้างล่างมีศัพท์สองคำที่ตอนนี้คุณต้องการแค่คำนิยามพอใช้งาน name ID คือช่องที่มีหมายเลขกำกับในตาราง name ของฟอนต์ ซึ่งเป็นส่วนของไฟล์ที่เก็บข้อความเกี่ยวกับฟอนต์: ID 0 คือบรรทัดลิขสิทธิ์ ID 6 คือชื่อ PostScript ID 13 คือข้อความสัญญาอนุญาต และไล่ไปเรื่อย ๆ หมายเลขเหล่านี้ถูกตรึงไว้โดยฟอร์แมต OpenType ทุกโปรแกรมบนโลกจึงมองหาสิ่งเดียวกันในช่องเดียวกัน ส่วน achVendID คือฟิลด์ขนาดสี่ไบต์ในอีกตารางหนึ่งชื่อ OS/2 เก็บรหัสสั้น ๆ ที่ระบุว่าใครทำฟอนต์นี้ ทั้งสองตารางจะถูกแกะอย่างละเอียดใน Module 3 สำหรับขั้นนี้ให้มองว่ามันคือกล่องที่ติดป้ายไว้ และ build.py เป็นคนกรอกของลงไปจาก config ของคุณ

นี่คือความหมายของทุกคีย์และผลที่มันมีต่อไฟล์ฟอนต์จริง:

คีย์มันทำอะไรกับฟอนต์
sourceพาธไปยังฟอนต์แปรผันต้นทาง อ้างอิงจากตำแหน่งของไฟล์ config เอง ตอนนี้มันชี้ไปที่ฟอนต์ที่มากับ zip ของคุณอยู่แล้ว อย่าไปแตะจนกว่าคุณจะตั้งใจเปลี่ยนต้นทาง
source_projectบรรทัดให้เครดิตต้นทาง ถูกฝังลงในบรรทัดลิขสิทธิ์ (name ID 0) และหัวไฟล์ OFL.txt
license_sourceพาธไปยังไฟล์ OFL ของโปรเจกต์ต้นทาง ค่าเริ่มต้น sources/OFL-Anuphan.txt
family_nameชื่อที่คนเห็นในเมนูฟอนต์ (name ID 16 และ 1) ใช้ภาษาไทยได้
ps_familyชื่อ PostScript ต้องเป็น ASCII และห้ามมีช่องว่าง ไปโผล่ที่ name ID 6 และ 3 รวมถึงเป็นชื่อไฟล์ที่ถูกเซฟออกมา
versionเลขเวอร์ชัน เช่น 1.000 ไปโผล่ที่ name ID 5 และ 3
condense_factorอัตราบีบแนวนอน 0.85 คือแคบลง 15% และเป็นค่าที่ QA จะเอาไปตรวจ
manufacturerชื่อโรงหล่อ/ผู้ผลิต (name ID 8)
designerเครดิตผู้ออกแบบ (name ID 9)
vendor_idรหัสผู้ผลิต 4 ตัวอักษรใน OS/2 achVendID ยาวเกิน 4 ตัวเมื่อไหร่ build หยุดทันที
vendor_urlURL ของคุณ (name ID 11)
modified_by, modified_yearใครดัดแปลงและปีไหน ถูกประกอบเข้าไปในบรรทัดลิขสิทธิ์ และ QA จะตรวจว่าชื่อนี้ปรากฏจริง
license_modelofl-derivative หรือ proprietary เท่านั้น
license_description, license_urlจำเป็นเมื่อ license_model เป็น proprietary ไม่ใส่แล้ว build ไม่ผ่าน
copyrightใส่หรือไม่ใส่ก็ได้ ถ้าใส่จะทับบรรทัดลิขสิทธิ์ที่ระบบสร้างให้
weightsรายการน้ำหนัก แต่ละตัวมี name, target, source_wght

สามคีย์ที่คนมักตั้งผิดที่สุด:

ps_family — ถ้าใส่ภาษาไทยหรือใส่ช่องว่าง ฟอนต์จะทำงานประหลาดในโปรแกรมที่อ้างชื่อ PostScript ให้ใช้ ASCII ล้วนติดกัน เช่น SomsakDisplay

vendor_id — สี่ตัวอักษรคือเพดานจริงของฟอร์แมต ไม่ใช่ข้อเสนอแนะ build.py ตรวจตั้งแต่โหลด config และหยุดพร้อมข้อความว่าทำไม เลือกตัวย่อของคุณเอง อย่าปล่อยเป็นของคนอื่น เพราะการทิ้ง vendor ID ต้นทางไว้บนงานดัดแปลงคือการให้เครดิตผิดคน

weights — สังเกตว่า source_wght สูงกว่า target เสมอ เช่น น้ำหนัก 400 ดึงมาจากต้นทางที่ 490 นั่นคือการชดเชยก้าน (stem compensation) เมื่อคุณบีบตัวอักษรให้แคบลง เส้นแนวตั้งจะบางลงตามไปด้วย จึงต้องเริ่มจากน้ำหนักที่หนากว่าเป้าหมายเพื่อให้ผลลัพธ์ดูถูกน้ำหนัก

ตัวอย่าง config ที่แก้แล้ว:

{
  "source": "sources/Anuphan[wght].ttf",
  "source_project": "Anuphan (Copyright 2019 The Anuphan Project Authors, https://github.com/cadsondemak/Anuphan)",
  "family_name": "Somsak Display",
  "ps_family": "SomsakDisplay",
  "version": "1.000",
  "condense_factor": 0.85,
  "manufacturer": "Somsak Type",
  "designer": "Derived from Anuphan by Cadson Demak",
  "vendor_id": "SMSK",
  "vendor_url": "https://example.com",
  "modified_by": "Somsak",
  "modified_year": 2026,
  "license_model": "ofl-derivative",
  "weights": [
    { "name": "Light",   "target": 300, "source_wght": 380 },
    { "name": "Regular", "target": 400, "source_wght": 490 },
    { "name": "Medium",  "target": 500, "source_wght": 590 },
    { "name": "Bold",    "target": 700, "source_wght": 700 }
  ]
}

เรื่องบรรทัดแรกนั้น "source": "sources/Anuphan[wght].ttf" ชี้ถูกอยู่แล้ว เพราะ Anuphan[wght].ttf อยู่ในไฟล์ zip ที่คุณแตกไว้ในขั้นที่ 0 พร้อมกับ OFL-Anuphan.txt ซึ่งเป็นสัญญาอนุญาตของมัน ไม่มีอะไรต้องดาวน์โหลดและไม่มีอะไรต้องตามหา ไปป์ไลน์ตั้งใจให้รันได้ทันทีตั้งแต่แกะกล่อง ถ้าอยากรู้ว่ามันมาจากไหน โปรเจกต์ต้นทางคือ cadsondemak/Anuphan คือ Anuphan, Copyright 2019 The Anuphan Project Authors เผยแพร่โดย Cadson Demak ภายใต้ SIL Open Font License 1.1 มันถูกแนบมาเพื่อให้ไปป์ไลน์มีของจริงให้ทำงานด้วยตั้งแต่คำสั่งแรกของคุณ และทุกอย่างที่คุณต่อยอดจากมันสืบทอดสัญญาอนุญาตนั้นไปด้วย ซึ่งเป็นเนื้อหาทั้งหมดของหัวข้อที่คุณอ่านไปตอนต้นหน้านี้

อย่าเปลี่ยน license_model เป็น proprietary เพื่อหวังจะขาย ถ้า outline ยังมาจากฟอนต์คนอื่น การติดป้ายว่าเป็นกรรมสิทธิ์ของคุณคือการละเมิดสัญญาอนุญาต ไม่ใช่ช่องโหว่ build.py พิมพ์ประโยคนี้ออกมาเองด้วยซ้ำ


ขั้นที่ 5 — สั่ง build

คำสั่งเดียว ได้ไฟล์แปดไฟล์ บวกไฟล์สัญญาอนุญาตอีกหนึ่ง

python scripts\build.py
python scripts/build.py

ถ้าคุณเก็บหลายครอบครัวไว้ข้าง ๆ กัน สคริปต์รับ --config และ --out:

python scripts\build.py --config families\myface.json
python scripts\build.py --out build\
python scripts/build.py --config families/myface.json
python scripts/build.py --out build/

สองแฟล็กนี้คือทั้งหมดที่ build.py รับ ไม่มีอย่างอื่น ค่าเริ่มต้นของ --config คือ config.json ระดับเดียวกับโฟลเดอร์ scripts/ และของ --out คือ dist/ ข้าง ๆ ไฟล์ config

มีกับดักหนึ่งที่มากับ --config บอกไว้ก่อนให้รู้ตัว: พาธ source และ license_source ที่อยู่ใน config ถูกอ้างอิงจาก โฟลเดอร์ของไฟล์ config นั้นเอง ไม่ใช่จากที่ที่คุณกำลังยืนอยู่ ถ้าคุณย้าย config ลงไปในโฟลเดอร์ families/ โดยไม่แก้สองพาธนั้น build จะหยุดพร้อมข้อความ CONFIG ERROR: source font not found โมดูล 4 จะจัดเรื่องนี้ให้เรียบร้อย ตอนนี้ให้ปล่อย config.json ไว้ที่เดิมและไม่ต้องสนใจสองแฟล็กนี้

สิ่งที่เกิดขึ้นข้างในมีสี่ขั้น: instance ตรึงฟอนต์แปรผันไว้ที่น้ำหนักต้นทางที่กำหนด → condense ย่อพิกัดแนวนอนทุกจุด ทั้งใน glyf, hmtx และ GPOSrebrand เขียนตาราง name ใหม่ทั้งตารางจาก config ของคุณ → เซฟเป็น TTF แล้วแปลงเป็น WOFF2

มีสองคำในนั้นที่ต้องแกะให้ฟังตอนนี้ และทั้งคู่จะถูกอธิบายเต็ม ๆ ใน Module 3 ฟอนต์แปรผัน (variable font) คือไฟล์เดียวที่เก็บช่วงต่อเนื่องทั้งช่วงไว้ แทนที่จะเก็บดีไซน์ตายตัวแบบเดียว Anuphan มีแกนน้ำหนักวิ่งจาก 100 ถึง 700 และตัวเลขไหนก็ตามที่อยู่ระหว่างนั้นคือน้ำหนักที่ใช้ได้จริง ส่วนการ instance หรือที่เรียกว่า pinning คือการเลือกตัวเลขหนึ่งจุดบนแกนนั้นแล้วตรึงมันให้เป็นฟอนต์คงที่ธรรมดาที่ไม่แปรผันอีก นั่นคือสิ่งที่ build.py ทำสี่ครั้ง ครั้งละหนึ่งน้ำหนักตาม config ของคุณ ซึ่งเป็นเหตุผลที่คุณได้ไฟล์ .ttf แยกกันสี่ไฟล์ ไม่ใช่ไฟล์เดียว ส่วน glyf, hmtx, GPOS และ name คือตารางไบนารีสี่ตารางภายในไฟล์ฟอนต์ที่ขั้นตอนนี้เขียนทับ โมดูล 3 จะเปิดทีละตารางแล้วให้ดูว่าผู้ใช้เห็นอะไรบนหน้าจอเมื่อตารางไหนผิด

ขั้น GPOS คือขั้นที่ตัดสินว่าฟอนต์ไทยของคุณใช้ได้หรือใช้ไม่ได้ จุดยึดของ mark และ mkmk อยู่ในตารางนั้น ถ้าคุณบีบ outline แต่ไม่บีบจุดยึด วรรณยุกต์ทุกตัวจะหลุดออกจากพยัญชนะไปทางขวา และไม่มีทางที่คุณจะเห็นมันจากการดูชื่อไฟล์

ผลลัพธ์ที่ควรได้:

building Somsak Display v1.000 -> ...\font-factory\dist  (condense 0.85)
built SomsakDisplay-Light.ttf  (...)
built SomsakDisplay-Light.woff2  (...)
built SomsakDisplay-Regular.ttf  (...)
...
built OFL.txt

  ------------------------------------------------------------------
  LICENSING - READ BEFORE YOU LIST THIS ANYWHERE
  ...

สังเกตว่า OFL.txt ถูกเขียนออกมาข้าง ๆ ไบนารีเสมอ นั่นไม่ใช่ของแถม OFL บังคับให้แนบตัวบทสัญญาไปกับทุกสำเนา และ QA จะตรวจว่าไฟล์นี้มีจริง

ถ้าติดตรงนี้ — ตัว build เองขึ้น error แทนที่จะพิมพ์บรรทัด built ...

ทุกอย่างที่ผ่านมาเป็นเรื่องของการติดตั้งเครื่องมือ กล่องนี้ว่าด้วย build ที่พังทั้งที่เครื่องมือครบแล้ว อาการมีแค่สองรูปแบบ และคุณแยกออกได้จากคำแรกของข้อความที่ขึ้นมา

  1. บรรทัดที่ขึ้นต้นด้วย CONFIG ERROR: — สคริปต์อ่าน config.json ของคุณแล้วเจอบางอย่างที่ทำงานด้วยไม่ได้ จึงหยุดก่อนเขียนแม้แต่ไบต์เดียว มันจบด้วย exit code 2 ไม่มีอะไรถูกสร้าง ไม่มีอะไรเสียหาย ข้อความจะระบุปัญหาให้ตรงตัว คือคีย์ที่ขาด JSON ที่ผิดรูป vendor_id ยาวเกินสี่ตัว license_model ที่มันไม่รู้จัก หรือพาธ source ที่ชี้ไปไม่ถึง ภาคผนวก A หัวข้อ A6 รวมข้อความเหล่านี้ไว้ครบทุกข้อความที่ไปป์ไลน์นี้ขึ้นได้ พร้อมสาเหตุและวิธีแก้ข้าง ๆ กัน และให้รู้ไว้ว่าการตรวจทำทีละข้อแล้วหยุดที่ข้อแรกที่ไม่ผ่าน ดังนั้นการที่แก้ข้อหนึ่งแล้วเจอข้อถัดไปทันทีคือระบบทำงานถูก ไม่ใช่ระบบแกล้งคุณ
  2. traceback ของ Python — บรรทัดย่อหน้าหลายบรรทัดที่จบด้วยอะไรทำนอง ModuleNotFoundError หรือ error ที่ถูกโยนออกมาจากข้างใน fontTools นั่นแปลว่าปัญหาอยู่ที่สภาพแวดล้อม ไม่ใช่ที่ config ให้ย้อนกลับไปที่บรรทัดยืนยันในขั้นที่ 3 คือ python -c "import fontTools, uharfbuzz, brotli; print('ok')" แล้วรันอีกครั้ง traceback ที่โผล่เฉพาะตอนเซฟ .woff2 หลังจากไฟล์ .ttf ถูกเขียนไปแล้ว เป็น brotli แทบทุกครั้ง ภาคผนวก A หัวข้อ A4 ครอบคลุมกรณีนั้นโดยตรง

ถ้าอยากอ่าน exit code เอง ให้สั่ง echo $LASTEXITCODE ใน PowerShell หรือ echo $? ใน bash ทันทีหลังคำสั่ง 0 คือสำเร็จ 2 คือ config ผิด และเลขอื่นคือโปรแกรมพัง


ขั้นที่ 6 — รัน QA และอ่านผล 52 ข้อให้เป็น

การเปิดฟอนต์ดูในโปรแกรมเดียวไม่ใช่ QA ฟอนต์ไทยตัวหนึ่งอาจดูสมบูรณ์แบบใน Notepad แล้วทิ้งวรรณยุกต์หายหมดใน InDesign เพราะโปรแกรมแก้ข้อความกับเอนจินจัดหน้าตัดสินใจไม่ตรงกันว่าจะรันฟีเจอร์ OpenType ตัวไหน qa.py จึงส่งข้อความไทยจริงเข้า HarfBuzz แล้วยืนยันผลด้วยตัวเลข

python scripts\qa.py
python scripts/qa.py

qa.py รับสองแฟล็ก: --config และ --dist (ไม่ใช่ --out คนละสคริปต์ คนละชื่อ)

python scripts\qa.py --config families\myface.json
python scripts\qa.py --dist build\
python scripts/qa.py --config families/myface.json
python scripts/qa.py --dist build/

ผลลัพธ์ที่ผ่านครบจะจบด้วยบรรทัดนี้:

ALL 52 CHECKS PASSED

52 ไม่ใช่ตัวเลขที่เสกขึ้นมาลอย ๆ มันคือ 13 การตรวจ คูณ 4 น้ำหนัก ถ้าคุณเพิ่มน้ำหนักที่ห้าใน config ตัวเลขจะกลายเป็น 65 เอง การรู้ที่มาของเลขนี้สำคัญ เพราะถ้าวันหนึ่งคุณเห็น ALL 39 CHECKS PASSED แปลว่ามีน้ำหนักหนึ่งหายไปเงียบ ๆ ไม่ใช่ว่าทุกอย่างเรียบร้อย

สิบสามข้อต่อหนึ่งน้ำหนัก แบ่งเป็นสองกลุ่ม กลุ่มแรกคือ การจัดรูปข้อความ หกข้อ กลุ่มที่สองคือ ตัวตนของไฟล์ เจ็ดข้อ

บรรทัดที่ต้องอ่านจริง ๆ ไม่ใช่แค่เห็นคำว่า PASS มีสามบรรทัด:

หนึ่ง — อัตราส่วนความกว้าง

  PASS  advance width ratio ~0.85  [ratio=0.850]

ตัวเลขในวงเล็บคือความกว้างรวมจริงของข้อความทดสอบในฟอนต์คุณ หารด้วยความกว้างเดียวกันในฟอนต์ต้นทาง มันต้องเท่ากับ condense_factor ที่คุณตั้ง ถ้าคุณตั้ง 0.85 แล้วได้ ratio=1.000 แปลว่าการบีบไม่เกิดขึ้นเลย ถ้าได้ ratio=0.720 แปลว่ามีบางอย่างถูกบีบสองรอบ ตัวเลขนี้จับข้อผิดพลาดที่ตาเปล่ามองไม่เห็น

สอง — ccmp สลับรูปพยัญชนะไม่มีเชิง

  PASS  ccmp descless alternate fires for yo ying + sara u  [gid 401 -> 402]

ในบรรทัดเดียวนั้นมีคำย่อสองคำ ccmp คือแท็กฟีเจอร์ของ OpenType เป็นชื่อสี่ตัวอักษรของชุดกฎที่อยู่ในฟอนต์ กรณีนี้ย่อมาจาก “glyph composition and decomposition” คือฟีเจอร์ที่สลับกลิฟหนึ่งเป็นอีกกลิฟเมื่ออักขระรอบข้างเรียกให้สลับ ส่วน gid ย่อมาจาก glyph ID คือหมายเลขที่ฟอนต์ใช้อ้างถึงกลิฟภายในตัวเอง มันไม่เกี่ยวอะไรกับรหัส Unicode และเป็นเลขเฉพาะของไฟล์นี้ไฟล์เดียว Module 3 จะแกะทั้งสองคำอย่างละเอียด พร้อมกับตารางที่มันอาศัยอยู่

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

สาม — mkmk ซ้อนมาร์ก

  PASS  mkmk stacking matches source (y kept, x condensed)  [src=... dst=...]

บรรทัดนี้เทียบตำแหน่งมาร์กในฟอนต์คุณกับในฟอนต์ต้นทางตัวต่อตัว เกณฑ์คือค่า y ต้องเท่าเดิม (การบีบแนวนอนไม่ควรแตะความสูง) และค่า x ต้องถูกคูณด้วย condense_factor พอดี นี่คือข้อที่พิสูจน์ว่าขั้น GPOS ทำงาน Microsoft ระบุ mark และ mkmk ไว้ในตารางฟีเจอร์ของสคริปต์ไทยว่าเป็นฟีเจอร์ที่ จำเป็น ไม่ใช่ทางเลือก

อีกสามข้อในกลุ่มการจัดรูป ตรวจว่าไม่มี .notdef โผล่ในตัวอย่างไทยผสมละติน ตรวจว่ามาร์กมีการเลื่อนตำแหน่งจริง และตรวจว่า สระอำ ถูกแยกออกเป็นนิคหิตกับสระอา ซึ่งเป็นพฤติกรรมที่ HarfBuzz ทำเองในโค้ด shaper ของสคริปต์ไทย

เจ็ดข้อกลุ่มตัวตน คือข้อที่ดักความผิดพลาดที่แพงที่สุดในไปป์ไลน์นี้ นั่นคือการส่งฟอนต์ออกไปทั้งที่ยังมีชื่อคนอื่นติดอยู่ ชื่อครอบครัวเปลี่ยนแล้วจริงไหม ชื่อ PostScript ยังมีคำจากต้นทางหลงเหลือไหม usWidthClass เป็น condensed แล้วไหม vendor ID เป็นของคุณไหม บรรทัดลิขสิทธิ์เอ่ยชื่อคุณไหม มีข้อความสัญญาอนุญาตไหม และ OFL.txt ถูกวางไว้ข้างไบนารีไหม

ถ้ามีข้อไหนไม่ผ่าน สคริปต์จะจบด้วย exit code 1 และพิมพ์สรุปว่าข้อไหนพัง อย่าข้ามไปขั้นถัดไป กลับไปแก้ config หรือ build ใหม่ก่อน


ขั้นที่ 7 — สร้าง specimen แล้วเปิดดู

specimen ไม่ใช่ของประดับ มันคือไฟล์ที่คุณส่งให้ลูกค้า แนบไปกับหน้าขาย และใช้ตรวจงานตัวเองที่ขนาดจริง

python scripts\specimen.py
python scripts/specimen.py

รับ --config และ --dist เหมือน qa.py:

python scripts\specimen.py --config families\myface.json --dist build\
python scripts/specimen.py --config families/myface.json --dist build/

ผลคือไฟล์ dist/specimen.html ที่อ้างอิงไฟล์ .woff2 ที่นั่งอยู่ข้าง ๆ ดังนั้นทั้งโฟลเดอร์ zip ส่งให้ใครก็เปิดได้ ไม่ต้องมีเซิร์ฟเวอร์ ไม่ต้องต่อเน็ต

เปิดมัน:

start dist\specimen.html
open dist/specimen.html

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


ขั้นที่ 8 — ติดตั้งฟอนต์แล้วพิมพ์ภาษาไทยด้วยมัน

นี่คือขั้นที่ทำให้ทั้งหมดเป็นจริง เปิด Word, Figma หรือ Illustrator แล้วเห็นชื่อฟอนต์ที่คุณตั้งเองอยู่ในเมนู

Windows — เปิดโฟลเดอร์ dist\ เลือกไฟล์ .ttf ทั้งสี่ไฟล์ คลิกขวา → Install for all users (ถ้าเลือกแค่ Install มันจะลงเฉพาะผู้ใช้ปัจจุบันไปที่ %LOCALAPPDATA%\Microsoft\Windows\Fonts ซึ่งบางโปรแกรมเก่ามองไม่เห็น)

macOS — ดับเบิลคลิกไฟล์ .ttf แล้วกด Install Font ใน Font Book หรือลากทั้งสี่ไฟล์ไปวางที่ ~/Library/Fonts

จากนั้นเปิดโปรแกรมที่คุณใช้ทำงานจริง เลือกฟอนต์ชื่อที่คุณตั้งไว้ใน family_name แล้วพิมพ์:

ผู้ใหญ่หาผ้าใหม่ ให้สะใภ้ใช้คล้องคอ
น้ำใจ ญุ ฐู ปั่น นิ่ง

ถ้าวรรณยุกต์เกาะถูกที่ทุกตัว คุณเพิ่งสร้างและติดตั้งฟอนต์ไทยตัวแรกของคุณสำเร็จ

ถ้าติดตรงนี้ — ติดตั้งแล้วแต่ฟอนต์ไม่โผล่ในเมนู หรือโผล่แต่ยังเป็นรูปเก่า

Windows แคชรายการฟอนต์ไว้ และโปรแกรมอ่านแคชนั้นตอนเปิดโปรแกรม ลำดับที่ควรทำ:

  1. ปิดโปรแกรมให้สนิทแล้วเปิดใหม่ — Figma desktop, Illustrator และ Word ล้วนอ่านรายการฟอนต์ครั้งเดียวตอนสตาร์ต ข้อนี้แก้ได้เกินครึ่ง
  2. ถ้ายังไม่ขึ้น ให้ล้างแคชระบบ เปิด PowerShell แบบ Run as administrator แล้วสั่ง:
net stop FontCache
net start FontCache
  1. ถ้าคุณ build ซ้ำโดยใช้ family_name เดิม Windows อาจยังจำรูปเก่าไว้ ให้ ถอนฟอนต์เก่าออกก่อน (Settings → Personalization → Fonts → เลือก → Uninstall) แล้วค่อยติดตั้งใหม่ ตอนพัฒนาให้เพิ่มเลข version ทุกครั้งที่ build จะช่วยลดอาการนี้

บน macOS: ถ้า Font Book ขึ้นเตือนว่าฟอนต์ซ้ำ ให้เลือก Resolve Automatically แล้วปิดเปิดโปรแกรมปลายทางใหม่


กลับมาที่เรื่องลิขสิทธิ์ ก่อนคุณจะทำอะไรกับไฟล์นี้ต่อ

ตอนนี้คุณถือฟอนต์ที่ใช้ได้จริงอยู่ในมือ และมันยังขายเดี่ยวไม่ได้เหมือนเดิม ไม่มีอะไรในหนึ่งถึงสองชั่วโมงที่ผ่านมาเปลี่ยนสถานะทางกฎหมายของมัน เพราะ outline ทุกเส้นยังสืบทอดมาจาก Anuphan

สิ่งที่คุณทำได้ทันทีและถูกต้องทุกประการ:

  • แจกฟรีเป็น lead magnet บนเว็บของคุณ พร้อมแนบ OFL.txt
  • ฝังลงเว็บไซต์ลูกค้าเป็นเว็บฟอนต์ รวมถึงงานเชิงพาณิชย์
  • แนบไปกับซอฟต์แวร์ เทมเพลต หรือผลิตภัณฑ์ที่คุณขาย — OFL ข้อ 2 อนุญาตไว้ชัด โดยมีเงื่อนไขว่าทุกสำเนาต้องมีบรรทัดลิขสิทธิ์และตัวบทสัญญาแนบไป
  • ใช้เป็นฟอนต์ประจำแบรนด์ตัวเอง

สิ่งที่ทำไม่ได้ ต่อให้เปลี่ยนชื่อแล้ว บีบแล้ว และใส่ vendor ID ตัวเองแล้ว: ตั้งราคาขายไฟล์ฟอนต์นี้โดยลำพัง

โชคดีอย่างหนึ่งคือ Anuphan ไม่ได้ประกาศ Reserved Font Name ไว้ในบรรทัดลิขสิทธิ์ของมัน การเปลี่ยนชื่อของคุณจึงสะอาด ไม่ต้องขออนุญาตใคร แต่ถ้าวันหนึ่งคุณสลับไปใช้ฟอนต์ต้นทางตัวอื่น เรื่องนี้อาจไม่จริงอีกต่อไป วิธีตรวจอยู่ใน Module 2

ถ้าคุณต้องการฟอนต์ที่ขายได้จริง คำตอบไม่ใช่การแก้ config ให้เนียนขึ้น แต่คือการเป็นเจ้าของ outline ทุกเส้น นั่นคือ Module 6 และไปถึงได้ทั้งสองระดับ

ตัวช่วย Pro — เส้นทางพารามิเตอร์ใช้ original/design.py, original/glyphs.py และ original/build.py ซึ่งอยู่ในชุดไฟล์ Pro

ผู้ใช้ Core ไปถึงผลลัพธ์เดียวกันได้ด้วยการวาดเส้นขอบใน FontForge ซึ่งเป็น “a free and open source font editor” ที่มีตัวติดตั้งทั้ง Windows, macOS และ GNU+Linux แล้วส่งออกเป็น TTF จากนั้นเอา TTF ตัวนั้นเข้าไปป์ไลน์ scripts/ ชุดเดียวกับที่คุณเพิ่งใช้ ทั้งการตั้งชื่อ เมตริก QA specimen และการแพ็ก โมดูล 6 เขียนเส้นทางนั้นไว้ครบ ใช้เวลานานกว่า แต่ไม่ได้ต้องใช้ชุดเครื่องมือคนละชุด และฟอนต์ที่ได้คือฟอนต์ที่คุณขายได้


เช็กลิสต์ — ตอนนี้คุณมีอะไรอยู่ในมือ

  • Python 3.11+ ติดตั้งแล้วและ python --version ตอบได้จากเทอร์มินัลเปล่า
  • venv สร้างแล้วและ import fontTools, uharfbuzz, brotli พิมพ์ ok
  • config.json มี family_name, ps_family, manufacturer, designer, vendor_id, vendor_url, modified_by เป็นของคุณทั้งหมด
  • โฟลเดอร์ dist/ มีไฟล์ .ttf สี่ไฟล์ .woff2 สี่ไฟล์ และ OFL.txt
  • python scripts/qa.py จบด้วย ALL 52 CHECKS PASSED
  • คุณอ่านและเข้าใจสามบรรทัด ratio=0.850 gid 401 -> 402 และบรรทัด mkmk
  • dist/specimen.html เปิดได้ และห้าคำทดสอบไทยเรนเดอร์ถูกทุกคำ
  • ฟอนต์ติดตั้งบนเครื่องแล้ว และคุณพิมพ์ประโยคภาษาไทยด้วยมันได้
  • คุณรู้ว่าไฟล์นี้ แจกได้ แนบได้ ฝังได้ แต่ขายเดี่ยวไม่ได้

ขั้นต่อไป: Module 1 — ฟอนต์ทำเงินได้อย่างไรจริง ๆ ซึ่งเป็นตัวอย่างฟรีที่ผู้ซื้อส่วนใหญ่อ่านไปแล้ว ถ้าคุณอ่านแล้ว ให้กวาดตาดูสี่คำถามท้ายโมดูลนั้นอีกรอบ แล้วข้ามไป Module 2 — ลิขสิทธิ์: อะไรขายได้ อะไรขายไม่ได้ ได้เลย โมดูลนั้นจะทำให้คุณตัดสินใจได้เองทุกครั้ง ว่าไฟล์ที่อยู่ตรงหน้าเป็นสินค้าหรือเป็นของแจก

Sources

  1. SIL OFL 1.1 ข้อ 1 ห้ามขายฟอนต์หรือส่วนประกอบของฟอนต์โดยลำพัง ทั้งฉบับต้นทางและฉบับดัดแปลงSIL Open Font License 1.1 — official text (accessed Aug 15, 2026)
  2. Anuphan เผยแพร่ภายใต้ OFL 1.1 โดย Cadson Demak และบรรทัดลิขสิทธิ์ไม่มีข้อความ with Reserved Font NameAnuphan — OFL.txt, cadsondemak/Anuphan (accessed Aug 15, 2026)
  3. Python เวอร์ชันเสถียรล่าสุดที่หน้าดาวน์โหลดเสนอคือ 3.14.7 พร้อมตัวติดตั้ง python-3.14.7-amd64.exe สำหรับ Windows และ python-3.14.7-macos11.pkg สำหรับ macOSPython.org — Downloads (accessed Aug 15, 2026)
  4. ไปป์ไลน์ต้องการ Python 3.11 ขึ้นไป และถูกทดสอบบน 3.13.2 — brotli ไม่มีโค้ดไหน import ตรง ๆ แต่ fontTools เรียกใช้ตอนเซฟ .woff2font-factory/README.md และ font-factory/requirements.txt (ในชุดเครื่องมือนี้) (accessed Aug 15, 2026)
  5. requirements.txt ปักหมุดไว้ที่ fonttools==4.63.0, brotli==1.2.0, uharfbuzz==0.56.0, skia-pathops==0.9.2, freetype-py==2.5.1 และ pillow==12.3.0font-factory/requirements.txt — อ่านจากไฟล์จริงที่ส่งมาในชุด (accessed Aug 16, 2026)
  6. font-factory-core.zip บรรจุ 12 ไฟล์ ได้แก่ LICENCE-toolkit-code.txt, font-factory/README.md, font-factory/requirements.txt, font-factory/config.json, font-factory/scripts/{build.py, qa.py, specimen.py, publish-webfont.py, apply-fea.py, check-shaping.py}, font-factory/sources/Anuphan[wght].ttf และ font-factory/sources/OFL-Anuphan.txt ส่วน font-factory-pro.zip เป็นซูเปอร์เซ็ตของชุดนี้ โดยเพิ่ม original/* และเทมเพลต EULA สองไฟล์ชุดเครื่องมือ ThaiGraph — รายการไฟล์ที่อ่านจาก public/toolkit-5bb8dac6d47353b9/assets/font-factory-core.zip ที่ build แล้ว ตรวจทานกับ manifest ใน scripts/build-product-assets.ts (accessed Aug 18, 2026)
  7. qa.py รัน 13 การตรวจต่อหนึ่งน้ำหนัก และรายงาน ALL 52 CHECKS PASSED เมื่อครบสี่น้ำหนัก โดยมีค่า ratio=0.850 และ gid 401 -> 402font-factory/scripts/qa.py — รันจริง 2026-08-15 (accessed Aug 15, 2026)
  8. ฟีเจอร์ mark (GPOS) และ mkmk (GPOS) ถูกระบุว่าจำเป็นสำหรับสคริปต์ไทยMicrosoft — Developing OpenType Fonts for Thai Script (accessed Aug 15, 2026)
  9. HarfBuzz แยก SARA AM ออกเป็น NIKHAHIT + SARA AA แล้วเลื่อน NIKHAHIT ย้อนกลับข้ามมาร์กบนHarfBuzz — src/hb-ot-shaper-thai.cc (accessed Aug 15, 2026)
  10. FontForge อธิบายตัวเองว่าเป็น 'a free and open source font editor' และมีหน้าติดตั้งสำหรับ Windows, Mac OS และ GNU+LinuxFontForge — หน้าแรกของโปรเจกต์ (accessed Aug 16, 2026)