โมดูล 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 ที่สคริปต์อ่าน และฟอนต์แปรผันตัวจริงที่ใช้ทำงานด้วย ไม่ต้องซื้อ ไม่ต้องกรอกอีเมล คำสั่งทุกบรรทัดในหน้านี้ใช้กับชุดนี้ได้ตั้งแต่ต้นจนจบ - Core —
font-factory-core.zipซึ่งเพิ่มpublish-webfont.py,apply-fea.pyและcheck-shaping.py - Pro —
font-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 เวอร์ชันเก่าเกินไป ลำดับที่ควรลอง:
python -m pip install --upgrade pipแล้วลองใหม่- ถ้าอยู่ในเน็ตออฟฟิศหรือใช้ VPN ให้สลับไปใช้เน็ตมือถือแล้วลองใหม่ วิธีนี้แก้ได้บ่อยกว่าที่คิด
- ทางสุดท้ายเท่านั้น ถ้าจำเป็นจริง ๆ ใช้
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/ที่ importskia-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_url | URL ของคุณ (name ID 11) |
modified_by, modified_year | ใครดัดแปลงและปีไหน ถูกประกอบเข้าไปในบรรทัดลิขสิทธิ์ และ QA จะตรวจว่าชื่อนี้ปรากฏจริง |
license_model | ofl-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 และ GPOS → rebrand เขียนตาราง 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 ที่พังทั้งที่เครื่องมือครบแล้ว อาการมีแค่สองรูปแบบ และคุณแยกออกได้จากคำแรกของข้อความที่ขึ้นมา
- บรรทัดที่ขึ้นต้นด้วย
CONFIG ERROR:— สคริปต์อ่านconfig.jsonของคุณแล้วเจอบางอย่างที่ทำงานด้วยไม่ได้ จึงหยุดก่อนเขียนแม้แต่ไบต์เดียว มันจบด้วย exit code 2 ไม่มีอะไรถูกสร้าง ไม่มีอะไรเสียหาย ข้อความจะระบุปัญหาให้ตรงตัว คือคีย์ที่ขาด JSON ที่ผิดรูปvendor_idยาวเกินสี่ตัวlicense_modelที่มันไม่รู้จัก หรือพาธsourceที่ชี้ไปไม่ถึง ภาคผนวก A หัวข้อ A6 รวมข้อความเหล่านี้ไว้ครบทุกข้อความที่ไปป์ไลน์นี้ขึ้นได้ พร้อมสาเหตุและวิธีแก้ข้าง ๆ กัน และให้รู้ไว้ว่าการตรวจทำทีละข้อแล้วหยุดที่ข้อแรกที่ไม่ผ่าน ดังนั้นการที่แก้ข้อหนึ่งแล้วเจอข้อถัดไปทันทีคือระบบทำงานถูก ไม่ใช่ระบบแกล้งคุณ- 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 แคชรายการฟอนต์ไว้ และโปรแกรมอ่านแคชนั้นตอนเปิดโปรแกรม ลำดับที่ควรทำ:
- ปิดโปรแกรมให้สนิทแล้วเปิดใหม่ — Figma desktop, Illustrator และ Word ล้วนอ่านรายการฟอนต์ครั้งเดียวตอนสตาร์ต ข้อนี้แก้ได้เกินครึ่ง
- ถ้ายังไม่ขึ้น ให้ล้างแคชระบบ เปิด PowerShell แบบ Run as administrator แล้วสั่ง:
net stop FontCache net start FontCache
- ถ้าคุณ 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.850gid 401 -> 402และบรรทัด mkmk -
dist/specimen.htmlเปิดได้ และห้าคำทดสอบไทยเรนเดอร์ถูกทุกคำ - ฟอนต์ติดตั้งบนเครื่องแล้ว และคุณพิมพ์ประโยคภาษาไทยด้วยมันได้
- คุณรู้ว่าไฟล์นี้ แจกได้ แนบได้ ฝังได้ แต่ขายเดี่ยวไม่ได้
ขั้นต่อไป: Module 1 — ฟอนต์ทำเงินได้อย่างไรจริง ๆ ซึ่งเป็นตัวอย่างฟรีที่ผู้ซื้อส่วนใหญ่อ่านไปแล้ว ถ้าคุณอ่านแล้ว ให้กวาดตาดูสี่คำถามท้ายโมดูลนั้นอีกรอบ แล้วข้ามไป Module 2 — ลิขสิทธิ์: อะไรขายได้ อะไรขายไม่ได้ ได้เลย โมดูลนั้นจะทำให้คุณตัดสินใจได้เองทุกครั้ง ว่าไฟล์ที่อยู่ตรงหน้าเป็นสินค้าหรือเป็นของแจก
Sources
- SIL OFL 1.1 ข้อ 1 ห้ามขายฟอนต์หรือส่วนประกอบของฟอนต์โดยลำพัง ทั้งฉบับต้นทางและฉบับดัดแปลง—SIL Open Font License 1.1 — official text (accessed Aug 15, 2026)
- Anuphan เผยแพร่ภายใต้ OFL 1.1 โดย Cadson Demak และบรรทัดลิขสิทธิ์ไม่มีข้อความ with Reserved Font Name—Anuphan — OFL.txt, cadsondemak/Anuphan (accessed Aug 15, 2026)
- Python เวอร์ชันเสถียรล่าสุดที่หน้าดาวน์โหลดเสนอคือ 3.14.7 พร้อมตัวติดตั้ง python-3.14.7-amd64.exe สำหรับ Windows และ python-3.14.7-macos11.pkg สำหรับ macOS—Python.org — Downloads (accessed Aug 15, 2026)
- ไปป์ไลน์ต้องการ Python 3.11 ขึ้นไป และถูกทดสอบบน 3.13.2 — brotli ไม่มีโค้ดไหน import ตรง ๆ แต่ fontTools เรียกใช้ตอนเซฟ .woff2—font-factory/README.md และ font-factory/requirements.txt (ในชุดเครื่องมือนี้) (accessed Aug 15, 2026)
- 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—font-factory/requirements.txt — อ่านจากไฟล์จริงที่ส่งมาในชุด (accessed Aug 16, 2026)
- 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)
- qa.py รัน 13 การตรวจต่อหนึ่งน้ำหนัก และรายงาน ALL 52 CHECKS PASSED เมื่อครบสี่น้ำหนัก โดยมีค่า ratio=0.850 และ gid 401 -> 402—font-factory/scripts/qa.py — รันจริง 2026-08-15 (accessed Aug 15, 2026)
- ฟีเจอร์ mark (GPOS) และ mkmk (GPOS) ถูกระบุว่าจำเป็นสำหรับสคริปต์ไทย—Microsoft — Developing OpenType Fonts for Thai Script (accessed Aug 15, 2026)
- HarfBuzz แยก SARA AM ออกเป็น NIKHAHIT + SARA AA แล้วเลื่อน NIKHAHIT ย้อนกลับข้ามมาร์กบน—HarfBuzz — src/hb-ot-shaper-thai.cc (accessed Aug 15, 2026)
- FontForge อธิบายตัวเองว่าเป็น 'a free and open source font editor' และมีหน้าติดตั้งสำหรับ Windows, Mac OS และ GNU+Linux—FontForge — หน้าแรกของโปรเจกต์ (accessed Aug 16, 2026)