Skip to content

Instantly share code, notes, and snippets.

@reiver-dev
Created February 5, 2018 03:07
Show Gist options
  • Star 7 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save reiver-dev/82da77ba3f0008c56624661a7375e0e8 to your computer and use it in GitHub Desktop.
Save reiver-dev/82da77ba3f0008c56624661a7375e0e8 to your computer and use it in GitHub Desktop.
Move ligatures for Fira Code font to private unicode area at U+e100
import os
import sys
import argparse
from glob import glob
from itertools import chain
import fontforge
ADDITIONAL_LIGATURES = [
'x.multiply',
'colon.uc',
'plus.lc',
'plus.tosf2',
]
def run(fontpath, outpath, starting_point=0xe100):
try:
font = fontforge.open(fontpath)
ligatures = list(filter(lambda x: x.glyphname.endswith('.liga'),
font.glyphs()))
for i, glyph in enumerate(ligatures):
point = starting_point + i
name = glyph.glyphname
newchar = font.createChar(point, name + '.private')
newchar.addReference(name)
# Other option is to use alternative unicode encoding
# fontforge recommends to use references
# glygh.altuni = (point, -1, 0)
font.generate(outpath)
finally:
font.close()
def main(argv):
parser = argparse.ArgumentParser()
parser.add_argument('--output-dir', '-o', type=str)
parser.add_argument('fonts', nargs='+',
help='font files to process')
argvals = parser.parse_args(argv)
output = argvals.output_dir
fonts = argvals.fonts
os.makedirs(output, exist_ok=True)
for f in chain.from_iterable(map(glob, fonts)):
run(f, os.path.join(output, os.path.basename(f)))
if __name__ == '__main__':
main(sys.argv[1:])
(defconst ligatures-fira-code-start #Xe100)
(defconst ligatures-fira-code-list
'("www" "**" "***" "*>" "*/" "\\\\" "\\\\\\" "]#" "::" ":::"
":=" "!!" "!=" "!==" "--" "---" "-->" "->" "->>" "-<"
"-<<" "-~" "#{" "#[" "#!" "##" "###" "####" "#(" "#?"
"#_" "#_(" ".-" ".=" ".." "..<" "..." ".?" "?:" "?="
"?." "??" ";;" "/*" "/=" "/==" "/>" "//" "///" "__"
"&&" "||" "|||>" "||=" "||>" "|=" "|>" "^=" "$>" "++"
"+++" "+>" "=:=" "==" "===" "==>" "=>" "=>>" "<=" "=<<"
"=/=" ">-" ">->" ">=" ">=>" ">>" ">>-" ">>=" ">>>" "<*"
"<*>" "<|" "<||" "<|||" "<|>" "<$" "<$>" "<!--" "<-" "<--"
"<->" "<-<" "<+" "<+>" "<=" "<==" "<=>" "<=<" "<>" "<<"
"<<-" "<<=" "<<<" "<~" "<~>" "<~~" "</" "</>" "~@" "~-"
"~=" "~>" "~~" "~~>" "%%")
"Ordered ligatures for Fira Code font")
(defun ligatures-correct-symbol-bounds (len char)
"Prepend up to LEN non-breaking spaces with reference points to CHAR.
This way `compose-region' called by function `prettify-symbols-mode'
will use the correct width of the symbols instead of the width
measured by `char-width'."
(let ((acc (list char)))
(while (> len 1)
(setq acc (cons #X00a0 (cons '(Br . Bl) acc)))
(setq len (1- len)))
acc))
(defun ligatures-make-alist (ligatures starting-code)
"Construct text to ligature character.
For each string in LIGATURES list add replacement from STARTING-CODE
sequentially."
(mapcar (lambda (l)
(let ((n starting-code))
(setq starting-code (1+ starting-code))
(when l
(cons l (ligatures-correct-symbol-bounds
(length l) n)))))
ligatures))
(defun ligatures-fira-code-setup ()
"Add Fira Code ligatures to `prettify-symbols-alist'."
(setq prettify-symbols-alist (append (ligatures-make-alist
ligatures-fira-code-list
ligatures-fira-code-start)
prettify-symbols-alist)))
@zbeekman
Copy link

zbeekman commented Mar 2, 2018

what needs to be used as the outpath?

@reiver-dev
Copy link
Author

reiver-dev commented Mar 8, 2018

@zbeekman

def run(fontpath, outpath, starting_point=0xe100)

Here outpath is a path to file that will be used for new font. /path/output_dir/Fira Code Medium.otf

font.generate(outpath)

Output dir in arguments is a directory for new font files

Processed font files are here https://github.com/tonsky/FiraCode/files/1734878/FiraCode-private-area.zip

@zbeekman
Copy link

@reiver-dev also, where is the fontforge module from? On my system (macOS with python 2 and 3 from Homebrew) I can't import fontforge:

>>> import fontforge
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ModuleNotFoundError: No module named 'fontforge'

@zbeekman
Copy link

@reiver-dev: Ah, I need the fontforge complete software package. Please ignore my previous comment.

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