202 lines
5.6 KiB
Python
Executable File
202 lines
5.6 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
# coding=utf8
|
|
|
|
import sys
|
|
import os
|
|
import re
|
|
import subprocess
|
|
import fontforge
|
|
|
|
# Double-quotes required here, for version-bump.sh:
|
|
version = "2.3.3"
|
|
|
|
archive = "v18.3.0.tar.gz"
|
|
|
|
vectorsdir = "icons"
|
|
fontdir = "."
|
|
fontfile = "octicons.ttf"
|
|
glyphsetfile = "i_oct.sh"
|
|
glyphsetsdir = "../../../bin/scripts/lib"
|
|
|
|
subset = "-16" # use 16 px subset if possible
|
|
subset_other = "-24" # use 24 px subset otherwise
|
|
|
|
|
|
def renamer(old_name):
|
|
"""Return new equivalent icon name"""
|
|
return {
|
|
"trashcan": "trash",
|
|
"cloud-download": "download",
|
|
"cloud-upload": "upload",
|
|
"clippy": "paste",
|
|
"mail-read": "read",
|
|
"primitive-dot": "dot-fill",
|
|
"primitive-square": "square-fill",
|
|
"settings": "sliders",
|
|
"dashboard": "meter",
|
|
"paintcan": "paintbrush",
|
|
}.get(old_name, old_name)
|
|
|
|
|
|
def addIcon(codepoint, name, filename):
|
|
"""Add one outline file and rescale/move"""
|
|
dBB = [120, 0, 1000 - 120, 900] # just some nice sizes
|
|
filename = os.path.join(vectorsdir, filename)
|
|
glyph = font.createChar(codepoint, name)
|
|
glyph.importOutlines(filename)
|
|
glyph.manualHints = True
|
|
|
|
|
|
def createGlyphInfo(icon_datasets, filepathname, into):
|
|
"""Write the glyphinfo file"""
|
|
with open(filepathname, "w", encoding="utf8") as f:
|
|
f.write("#!/usr/bin/env bash\n")
|
|
f.write(intro)
|
|
f.write("# Script Version: (autogenerated)\n")
|
|
f.write('test -n "$__i_oct_loaded" && return || __i_oct_loaded=1\n')
|
|
for _, codepoint, name in icon_datasets:
|
|
codepoint = int(codepoint, 16)
|
|
f.write(
|
|
"i='{}' i_oct_{}=$i\n".format(chr(codepoint), name.replace("-", "_"))
|
|
)
|
|
f.write("unset i\n")
|
|
|
|
|
|
print("\nReading mapping file")
|
|
old_mapping = []
|
|
with open("mapping", "r") as f:
|
|
for line in f.readlines():
|
|
if line.startswith("#"):
|
|
continue
|
|
old_mapping.append(tuple(re.split(" +", line.strip())))
|
|
print("Found {} entries".format(len(old_mapping)))
|
|
old_mapping.sort(key=(lambda x: x[0]))
|
|
|
|
print('Fetching octicons archive "{}"\n'.format(archive))
|
|
if subprocess.call(
|
|
"curl -OL https://github.com/primer/octicons/archive/" + archive, shell=True
|
|
):
|
|
sys.exit("Error fetching octicons archive")
|
|
print("\nUnpacking octicons archive")
|
|
if subprocess.call(
|
|
"rm -rf icons octicons-* && tar zxf *.gz && mv octicons-*/icons . && rm -rf octicons-*",
|
|
shell=True,
|
|
):
|
|
sys.exit("Error unpacking archive")
|
|
|
|
svgs = os.listdir(vectorsdir)
|
|
print("Found {} svgs".format(len(svgs)))
|
|
names = {
|
|
s[0 : -len("-xx.svg")]
|
|
for s in svgs
|
|
if s.endswith(subset + ".svg") or s.endswith(subset_other + ".svg")
|
|
}
|
|
print("Found {} icons after de-duplicating\n".format(len(names)))
|
|
|
|
num_found = 0
|
|
num_missing = 0
|
|
misslist = ""
|
|
renamelist = ""
|
|
freeslots = []
|
|
|
|
new_mapping = []
|
|
for i, j, old_n in old_mapping:
|
|
if old_n in names:
|
|
names.remove(old_n)
|
|
new_mapping.append((i, j, old_n))
|
|
num_found += 1
|
|
continue
|
|
new_n = renamer(old_n)
|
|
if new_n in names:
|
|
renamelist += "Renamed {} -> {}\n".format(old_n, new_n)
|
|
names.remove(new_n)
|
|
new_mapping.append((i, j, new_n))
|
|
num_found += 1
|
|
continue
|
|
misslist += "Missing {}\n".format(old_n)
|
|
freeslots.append((i, j))
|
|
num_missing += 1
|
|
|
|
print(renamelist)
|
|
print(misslist)
|
|
print(
|
|
"Found {} (of {}, missing {}) and new {}".format(
|
|
num_found, len(old_mapping), num_missing, len(names)
|
|
)
|
|
)
|
|
|
|
names = list(names)
|
|
names.sort()
|
|
for n in list(names):
|
|
if len(freeslots) == 0:
|
|
break
|
|
i, j = freeslots[0]
|
|
new_mapping.append((i, j, n))
|
|
names.remove(n)
|
|
freeslots = freeslots[1:]
|
|
|
|
print("Filled in missing, remaining new {}".format(len(names)))
|
|
|
|
i_max = 0
|
|
j_max = 0
|
|
for i, j, _ in new_mapping:
|
|
i = int(i, 16)
|
|
j = int(j, 16)
|
|
if i > i_max:
|
|
i_max = i
|
|
if j > j_max:
|
|
j_max = j
|
|
|
|
for n in names:
|
|
i_max += 1
|
|
j_max += 1
|
|
new_mapping.append(("{:X}".format(i_max), "{:X}".format(j_max), n))
|
|
|
|
print("Appended remaining new, total new mapping {}".format(len(new_mapping)))
|
|
|
|
new_mapping.sort(key=(lambda x: x[0]))
|
|
with open("mapping", "w") as f:
|
|
for i, j, n in new_mapping:
|
|
f.write("{} {} {}\n".format(i, j, n))
|
|
|
|
font = fontforge.font()
|
|
font.fontname = "OcticonsNerdFont-Regular"
|
|
font.fullname = "Octicons Nerd Font Regular"
|
|
font.familyname = "Octicons Nerd Font"
|
|
font.em = 2048
|
|
font.encoding = "UnicodeFull"
|
|
|
|
# Add valid space glyph to avoid "unknown character" box on IE11
|
|
glyph = font.createChar(32)
|
|
glyph.width = 200
|
|
|
|
font.sfntRevision = None # Auto-set (refreshed) by fontforge
|
|
font.version = version
|
|
font.copyright = "GitHub Inc."
|
|
font.appendSFNTName("English (US)", "Version", archive + "; " + version)
|
|
font.appendSFNTName(
|
|
"English (US)", "Vendor URL", "https://github.com/ryanoasis/nerd-fonts"
|
|
)
|
|
font.appendSFNTName("English (US)", "Copyright", "GitHub Inc.")
|
|
|
|
for codepoint, _, name in new_mapping:
|
|
codepoint = int(codepoint, 16)
|
|
filename = name + subset + ".svg"
|
|
if filename not in svgs:
|
|
filename = name + subset_other + ".svg"
|
|
addIcon(codepoint, name, filename)
|
|
|
|
num_icons = len(new_mapping)
|
|
|
|
print("Generating {} with {} glyphs".format(fontfile, num_icons))
|
|
font.generate(os.path.join(fontdir, fontfile), flags=("no-FFTM-table",))
|
|
|
|
codepoints = [int(p, 16) for _, p, _ in new_mapping]
|
|
intro = "# Octicons ({} icons)\n".format(num_icons)
|
|
intro += "# Codepoints: {:X}-{:X} with gaps\n".format(min(codepoints), max(codepoints))
|
|
intro += "# Nerd Fonts Version: {}\n".format(version)
|
|
|
|
print("Generating GlyphInfo {}".format(glyphsetfile))
|
|
createGlyphInfo(new_mapping, os.path.join(glyphsetsdir, glyphsetfile), intro)
|
|
print("Finished")
|