Create new octicons.ttf (1/2)

[why]
The octicons got a lot updates.
But they do not have a font anymore.

[how]
Keep our old codepoints constant, but add the new icons thereafter.

This commit just moves all the mechanics in and moves the (old) font.
No actual update here.

The mapping file has been created with the analyze_octicons script.

Fixes: #490

Signed-off-by: Fini Jastrow <ulf.fini.jastrow@desy.de>
This commit is contained in:
Fini Jastrow 2023-03-25 13:20:22 +01:00 committed by Fini
parent c68d1d57be
commit 91953e2de7
6 changed files with 411 additions and 5 deletions

View file

@ -6,7 +6,7 @@
from __future__ import absolute_import, print_function, unicode_literals
# Change the script version when you edit this script:
script_version = "4.0.0"
script_version = "4.1.0"
version = "2.3.3"
projectName = "Nerd Fonts"
@ -1031,10 +1031,10 @@ class font_patcher:
{'Enabled': self.args.material, 'Name': "Material", 'Filename': "materialdesign/MaterialDesignIconsDesktop.ttf", 'Exact': True, 'SymStart': 0xF0001,'SymEnd': 0xF1AF0,'SrcStart': None, 'ScaleRules': MDI_SCALE_LIST, 'Attributes': SYM_ATTR_DEFAULT},
{'Enabled': self.args.weather, 'Name': "Weather Icons", 'Filename': "weather-icons/weathericons-regular-webfont.ttf", 'Exact': False, 'SymStart': 0xF000, 'SymEnd': 0xF0EB, 'SrcStart': 0xE300, 'ScaleRules': WEATH_SCALE_LIST, 'Attributes': SYM_ATTR_DEFAULT},
{'Enabled': self.args.fontlogos, 'Name': "Font Logos", 'Filename': "font-logos.ttf", 'Exact': True, 'SymStart': 0xF300, 'SymEnd': 0xF32F, 'SrcStart': None, 'ScaleRules': None, 'Attributes': SYM_ATTR_DEFAULT},
{'Enabled': self.args.octicons, 'Name': "Octicons", 'Filename': "octicons.ttf", 'Exact': False, 'SymStart': 0xF000, 'SymEnd': 0xF105, 'SrcStart': 0xF400, 'ScaleRules': OCTI_SCALE_LIST, 'Attributes': SYM_ATTR_DEFAULT}, # Magnifying glass
{'Enabled': self.args.octicons, 'Name': "Octicons", 'Filename': "octicons.ttf", 'Exact': True, 'SymStart': 0x2665, 'SymEnd': 0x2665, 'SrcStart': None, 'ScaleRules': OCTI_SCALE_LIST, 'Attributes': SYM_ATTR_DEFAULT}, # Heart
{'Enabled': self.args.octicons, 'Name': "Octicons", 'Filename': "octicons.ttf", 'Exact': True, 'SymStart': 0X26A1, 'SymEnd': 0X26A1, 'SrcStart': None, 'ScaleRules': OCTI_SCALE_LIST, 'Attributes': SYM_ATTR_DEFAULT}, # Zap
{'Enabled': self.args.octicons, 'Name': "Octicons", 'Filename': "octicons.ttf", 'Exact': False, 'SymStart': 0xF27C, 'SymEnd': 0xF27C, 'SrcStart': 0xF4A9, 'ScaleRules': OCTI_SCALE_LIST, 'Attributes': SYM_ATTR_DEFAULT}, # Desktop
{'Enabled': self.args.octicons, 'Name': "Octicons", 'Filename': "octicons/octicons.ttf", 'Exact': False, 'SymStart': 0xF000, 'SymEnd': 0xF105, 'SrcStart': 0xF400, 'ScaleRules': OCTI_SCALE_LIST, 'Attributes': SYM_ATTR_DEFAULT}, # Magnifying glass
{'Enabled': self.args.octicons, 'Name': "Octicons", 'Filename': "octicons/octicons.ttf", 'Exact': True, 'SymStart': 0x2665, 'SymEnd': 0x2665, 'SrcStart': None, 'ScaleRules': OCTI_SCALE_LIST, 'Attributes': SYM_ATTR_DEFAULT}, # Heart
{'Enabled': self.args.octicons, 'Name': "Octicons", 'Filename': "octicons/octicons.ttf", 'Exact': True, 'SymStart': 0X26A1, 'SymEnd': 0X26A1, 'SrcStart': None, 'ScaleRules': OCTI_SCALE_LIST, 'Attributes': SYM_ATTR_DEFAULT}, # Zap
{'Enabled': self.args.octicons, 'Name': "Octicons", 'Filename': "octicons/octicons.ttf", 'Exact': False, 'SymStart': 0xF27C, 'SymEnd': 0xF27C, 'SrcStart': 0xF4A9, 'ScaleRules': OCTI_SCALE_LIST, 'Attributes': SYM_ATTR_DEFAULT}, # Desktop
{'Enabled': self.args.codicons, 'Name': "Codicons", 'Filename': "codicons/codicon.ttf", 'Exact': True, 'SymStart': 0xEA60, 'SymEnd': 0xEBEB, 'SrcStart': None, 'ScaleRules': CODI_SCALE_LIST, 'Attributes': SYM_ATTR_DEFAULT},
{'Enabled': self.args.custom, 'Name': "Custom", 'Filename': self.args.custom, 'Exact': True, 'SymStart': 0x0000, 'SymEnd': 0x0000, 'SrcStart': None, 'ScaleRules': None, 'Attributes': CUSTOM_ATTR}
]

View file

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2023 GitHub Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View file

@ -0,0 +1,32 @@
#!/usr/bin/env python3
# coding=utf8
# This extracts the names and source and destination codepoints
# of the old octicons font file to keep their codepoints stable
#
# You do not need to redo it, the result is in the repo
#
# Usage:
# fontforge analyze_octicons > mapping
import fontforge
octi_orig = "octicons.ttf"
current_cp = 0xF400
print('# Examining {}'.format(octi_orig))
font = fontforge.open(octi_orig)
for glyph in font.glyphs('encoding'):
point = glyph.unicode
if point < 0:
continue
desti = glyph.unicode
if point < 0xF000:
desti = point
else:
desti = current_cp
current_cp += 1
print("{:X} {:X} {}".format(point, desti, glyph.glyphname))
font.close()

180
src/glyphs/octicons/generate Executable file
View file

@ -0,0 +1,180 @@
#!/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.2.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(u'#!/usr/bin/env bash\n')
f.write(intro)
f.write(u'# Script Version: (autogenerated)\n')
f.write(u'test -n "$__i_oct_loaded" && return || __i_oct_loaded=1\n')
for _, codepoint, name in icon_datasets:
codepoint = int(codepoint, 16)
f.write(u"i='{}' i_oct_{}=$i\n".format(chr(codepoint), name))
f.write(u'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 = u'# Octicons ({} icons)\n'.format(num_icons)
intro += u'# Codepoints: {:X}-{:X} with gaps\n'.format(min(codepoints), max(codepoints))
intro += u'# Nerd Fonts Version: {}\n'.format(version)
print('Generating GlyphInfo {}'.format(glyphsetfile))
createGlyphInfo(new_mapping, os.path.join(glyphsetsdir, glyphsetfile), intro)
print('Finished')

173
src/glyphs/octicons/mapping Normal file
View file

@ -0,0 +1,173 @@
# Examining octicons_old.ttf
2665 2665 heart
26A1 26A1 zap
F000 F400 light-bulb
F001 F401 repo
F002 F402 repo-forked
F005 F403 repo-push
F006 F404 repo-pull
F007 F405 book
F008 F406 octoface
F009 F407 git-pull-request
F00A F408 mark-github
F00B F409 cloud-download
F00C F40A cloud-upload
F00D F40B keyboard
F00E F40C gist
F010 F40D file-code
F011 F40E file-text
F012 F40F file-media
F013 F410 file-zip
F014 F411 file-pdf
F015 F412 tag
F016 F413 file-directory
F017 F414 file-submodule
F018 F415 person
F019 F416 jersey
F01F F417 git-commit
F020 F418 git-branch
F023 F419 git-merge
F024 F41A mirror
F026 F41B issue-opened
F027 F41C issue-reopened
F028 F41D issue-closed
F02A F41E star
F02B F41F comment
F02C F420 question
F02D F421 alert
F02E F422 search
F02F F423 gear
F030 F424 radio-tower
F031 F425 tools
F032 F426 sign-out
F033 F427 rocket
F034 F428 rss
F035 F429 clippy
F036 F42A sign-in
F037 F42B organization
F038 F42C device-mobile
F039 F42D unfold
F03A F42E check
F03B F42F mail
F03C F430 mail-read
F03D F431 arrow-up
F03E F432 arrow-right
F03F F433 arrow-down
F040 F434 arrow-left
F041 F435 pin
F042 F436 gift
F043 F437 graph
F044 F438 triangle-left
F045 F439 credit-card
F046 F43A clock
F047 F43B ruby
F048 F43C broadcast
F049 F43D key
F04A F43E repo-force-push
F04C F43F repo-clone
F04D F440 diff
F04E F441 eye
F04F F442 comment-discussion
F051 F443 mail-reply
F052 F444 primitive-dot
F053 F445 primitive-square
F056 F446 device-camera
F057 F447 device-camera-video
F058 F448 pencil
F059 F449 info
F05A F44A triangle-right
F05B F44B triangle-down
F05C F44C link
F05D F44D plus
F05E F44E three-bars
F05F F44F code
F060 F450 location
F061 F451 list-unordered
F062 F452 list-ordered
F063 F453 quote
F064 F454 versions
F068 F455 calendar
F06A F456 lock
F06B F457 diff-added
F06C F458 diff-removed
F06D F459 diff-modified
F06E F45A diff-renamed
F070 F45B horizontal-rule
F071 F45C arrow-small-right
F075 F45D milestone
F076 F45E checklist
F077 F45F megaphone
F078 F460 chevron-right
F07B F461 bookmark
F07C F462 settings
F07D F463 dashboard
F07E F464 history
F07F F465 link-external
F080 F466 mute
F081 F467 x
F084 F468 circle-slash
F085 F469 pulse
F087 F46A sync
F088 F46B telescope
F08C F46C gist-secret
F08D F46D home
F08F F46E stop
F091 F46F bug
F092 F470 logo-github
F094 F471 file-binary
F096 F472 database
F097 F473 server
F099 F474 diff-ignored
F09A F475 ellipsis
F09C F476 no-newline
F09D F477 hubot
F09F F478 arrow-small-up
F0A0 F479 arrow-small-down
F0A1 F47A arrow-small-left
F0A2 F47B chevron-up
F0A3 F47C chevron-down
F0A4 F47D chevron-left
F0AA F47E triangle-up
F0AC F47F git-compare
F0AD F480 logo-gist
F0B0 F481 file-symlink-file
F0B1 F482 file-symlink-directory
F0B2 F483 squirrel
F0B6 F484 globe
F0BA F485 unmute
F0BE F486 mention
F0C4 F487 package
F0C5 F488 browser
F0C8 F489 terminal
F0C9 F48A markdown
F0CA F48B dash
F0CC F48C fold
F0CF F48D inbox
F0D0 F48E trashcan
F0D1 F48F paintcan
F0D2 F490 flame
F0D3 F491 briefcase
F0D4 F492 plug
F0D6 F493 circuit-board
F0D7 F494 mortar-board
F0D8 F495 law
F0DA F496 thumbsup
F0DB F497 thumbsdown
F0DC F498 desktop-download
F0DD F499 beaker
F0DE F49A bell
F0E0 F49B watch
F0E1 F49C shield
F0E2 F49D bold
F0E3 F49E text-size
F0E4 F49F italic
F0E5 F4A0 tasklist
F0E6 F4A1 verified
F0E7 F4A2 smiley
F0E8 F4A3 unverified
F101 F4A4 ellipses
F102 F4A5 file
F103 F4A6 grabber
F104 F4A7 plus-small
F105 F4A8 reply
F27C F4A9 device-desktop