Skip to content

Instantly share code, notes, and snippets.

@jyxjjj
Last active March 2, 2026 09:22
Show Gist options
  • Select an option

  • Save jyxjjj/ddc317a6b0a27ade3f02817d162cb5fe to your computer and use it in GitHub Desktop.

Select an option

Save jyxjjj/ddc317a6b0a27ade3f02817d162cb5fe to your computer and use it in GitHub Desktop.
Convert PingFang -> PingFang.ttc -> PingFangSC.ttx -> PingFangSC.otf -> PingFangSC.ttf & PingFangSC.woff2

⚠️ WARNING ⚠️

THESE FONTS ARE PROPRIETARY TO APPLE INC. AND MAY BE COPYRIGHTED OR OTHERWISE PROTECTED. THEY ARE NOT OPEN SOURCE AND MAY NOT BE USED FOR COMMERCIAL DISTRIBUTION, REDISTRIBUTION, OR UNAUTHORIZED EMBEDDING INTO PRODUCTS. FOR MORE INFORMATION, SEE:

⚠️ DISCLAIMER & DMCA ⚠️

THESE CODES ARE INTENDED FOR PERSONAL USE ONLY. THERE IS NO INTENTION OF REDISTRIBUTION, COMMERCIAL PACKAGING, OR SECONDARY DISTRIBUTION. IF ANY CONTENT IS FOUND TO INFRINGE COPYRIGHT, I WILL PROMPTLY REMOVE THE RELEVANT CODE UPON NOTIFICATION FROM THE RIGHTFUL COPYRIGHT OWNER OR AUTHORIZED OFFICIAL REPRESENTATIVES.

#!/usr/bin/env python3
import sys
import os
import re
import fontforge
from fontTools.ttLib import TTFont
OS2_WEIGHT_CLASS_MAP = {
100: "Thin",
200: "ExtraLight",
300: "Light",
400: "Regular",
500: "Medium",
600: "SemiBold",
700: "Bold",
800: "ExtraBold",
900: "Black",
}
def extract_string_from_tuple(tup):
if not tup:
return None
s = tup[-1]
if isinstance(s, bytes):
try:
return s.decode('utf-8')
except Exception:
try:
return s.decode('latin-1')
except Exception:
return repr(s)
return str(s)
def tuple_has_nameid(tup, target):
for el in tup:
if isinstance(el, int) and el == target:
return True
return False
def get_weight_name(font):
return OS2_WEIGHT_CLASS_MAP.get(font.os2_weight, 'Normal')
def convert(path):
print('Converting', path)
f = fontforge.open(path)
if f.cidweight == "Light":
f.os2_weight = 300
# attrs = [a for a in dir(f)
# if not a.startswith('__')
# and not callable(getattr(f, a, None))]
# for a in attrs:
# print(f"{a}: {getattr(f, a)}")
f.cidcopyright = "Apple Inc."
f.cidweight = get_weight_name(f)
f.cidfontname = f.cidfamilyname.replace(" ", "") + "-" + f.cidweight
f.cidfullname = f.cidfontname
f.fontname = f.cidfontname
f.fullname = f.cidfontname
f.familyname = f.cidfamilyname
new_names = [
(1033, 0, f.cidcopyright),
(1033, 3, f.cidfontname),
(1033, 5, "21.0d1e1"),
(1033, 6, f.cidfontname),
(1033, 7, f.familyname + " is a trademark of Apple Inc."),
(1033, 8, "Apple Inc."),
(1033, 12, "https://www.apple.com/"),
(2052, 3, f.cidfontname.replace("SC", "-简体中文").replace("PingFang", "苹方").replace("Thin", "纤细").replace("ExtraLight", "特细").replace("Light", "细体").replace("Regular", "常规").replace("Medium", "中等").replace("SemiBold", "中粗").replace("Bold", "粗体").replace("ExtraBold", "特粗").replace("Black", "黑体")),
(2052, 7, f.familyname.replace(" SC", "").replace("PingFang", "苹方") + "是 Apple Inc. 的商标"),
]
f.sfnt_names = new_names
out = os.path.splitext(path)[0] + '.ttf'
try:
f.generate(out)
print(' Generated', out)
except Exception as e:
print(' Failed to generate:', e)
finally:
f.close()
if __name__ == '__main__':
if len(sys.argv) < 2:
print('Usage: convert_fonts.py file.otf')
sys.exit(1)
convert(sys.argv[1])
#!/usr/bin/env bash
set -e
rm -f PingFangSC-*.ttf
rm -f PingFangSC-*.woff2
python3 convert_fonts.py PingFangSC-Thin.otf 2>/dev/null &
python3 convert_fonts.py PingFangSC-ExtraLight.otf 2>/dev/null &
python3 convert_fonts.py PingFangSC-Light.otf 2>/dev/null &
python3 convert_fonts.py PingFangSC-Regular.otf 2>/dev/null &
python3 convert_fonts.py PingFangSC-Medium.otf 2>/dev/null &
python3 convert_fonts.py PingFangSC-SemiBold.otf 2>/dev/null &
wait
# python3 list_fonts.py PingFangSC-Thin.ttf
# python3 list_fonts.py PingFangSC-ExtraLight.ttf
# python3 list_fonts.py PingFangSC-Light.ttf
# python3 list_fonts.py PingFangSC-Regular.ttf
# python3 list_fonts.py PingFangSC-Medium.ttf
# python3 list_fonts.py PingFangSC-SemiBold.ttf
# wait
woff2_compress PingFangSC-Thin.ttf &
woff2_compress PingFangSC-ExtraLight.ttf &
woff2_compress PingFangSC-Light.ttf &
woff2_compress PingFangSC-Regular.ttf &
woff2_compress PingFangSC-Medium.ttf &
woff2_compress PingFangSC-SemiBold.ttf &
wait
import Foundation
import CoreText
let pattern = "(?i)(PingFang|苹方|平方)"
let families = CTFontManagerCopyAvailableFontFamilyNames() as? [String] ?? []
let fileManager = FileManager.default
let currentPath = fileManager.currentDirectoryPath
for family in families {
guard family.range(of: pattern, options: .regularExpression) != nil else {
continue
}
let attributes: [CFString: Any] = [
kCTFontFamilyNameAttribute: family
]
let descriptor = CTFontDescriptorCreateWithAttributes(attributes as CFDictionary)
guard let descriptors = CTFontDescriptorCreateMatchingFontDescriptors(
descriptor,
nil
) as? [CTFontDescriptor] else {
continue
}
for desc in descriptors {
if let url = CTFontDescriptorCopyAttribute(desc, kCTFontURLAttribute) as? URL {
let dest = URL(fileURLWithPath: currentPath)
.appendingPathComponent(url.lastPathComponent)
if !fileManager.fileExists(atPath: dest.path) {
try? fileManager.copyItem(at: url, to: dest)
print("Export:", dest.lastPathComponent)
}
}
}
}
print("Done")
#!/usr/bin/env python3
import os
import sys
import fontforge
from fontTools.ttLib import TTFont
NAME_ID_MAP = {
0: "Copyright",
1: "Font Family",
2: "Font Subfamily",
3: "Unique Subfamily Identification",
4: "FullName",
5: "Version",
6: "PostScriptName",
7: "Trademark",
8: "Manufacturer",
9: "Designer",
10: "Description",
11: "Vendor URL",
12: "Designer URL",
13: "License",
14: "License URL",
15: "Reserved",
16: "Preferred Family",
17: "Preferred Subfamily",
}
def dump_font(path):
print('='*60)
f = fontforge.open(path)
print(f.os2_weight)
f.close()
try:
font = TTFont(path, 0, allowVID=0, ignoreDecompileErrors=True)
except Exception as e:
print(' Failed to open:', e)
return
# Glyph count
try:
glyphs = font.getGlyphOrder()
print(' Glyph count:', len(glyphs))
except Exception:
print(' Glyph count: ?')
if 'name' in font:
records = font['name'].names
if records:
# 保持稳定排序(先语言,再 nameID)
records.sort(key=lambda r: (r.langID, r.nameID))
print(' Name table:')
for record in records:
nameid = record.nameID
label = NAME_ID_MAP.get(nameid, f'NameID {nameid}')
try:
text = record.toUnicode()
except Exception:
try:
text = record.string.decode('utf-8', 'ignore')
except Exception:
text = repr(record.string)
print(f" {label} ({nameid}) [{record.platformID},{record.platEncID},{record.langID}]: {text}")
font.close()
def main():
if len(sys.argv) < 2:
print('Usage: python3 list_fonts.py <font-file>')
return
filename = sys.argv[1]
if os.path.exists(filename):
dump_font(filename)
else:
print('='*60)
print('File:', filename)
print(' Not found in current directory.')
if __name__ == '__main__':
main()
#!/bin/bash
for i in $(seq 0 23); do
ttx -y $i -o PingFang_$i.ttx PingFang.ttc &
done
#!/bin/bash
for i in $(seq 0 23); do
ttx PingFang_$i.ttx &
done
@jyxjjj
Copy link
Author

jyxjjj commented Mar 2, 2026

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment