Downsizing an .OTF font by removing glyphs
Asked Answered
E

8

39

I can't quite believe this question hasn't been asked specifically for OpenType fonts, but does anyone know of a way to remove glyphs from these fonts?

I have an .OTF with a very large file-size (almost 10MB) and I need to make it smaller. The reasons are two fold.

1) I'm trying to prepare it for web embedding, so the smaller the files, the easier for the client.

2) Font Squirrel (used for easy preparation of font files) has a 2MB upload limit - I know there are alternatives, but none so far have been successful. To save wasting peoples time, the ones I've tried that have failed are http://fontface.codeandmore.com/ and http://www.font2web.com/. CodeAndMore.com appears to work, but the fonts it spits back out are completely different to the one I gave it.

Please be aware I'm not a font expert, so go easy on the answer.

Edda answered 28/1, 2013 at 8:13 Comment(0)
H
27

I've written a Python2 script with fontforge library does the following:

  • Accepts a source font
  • Accepts a file containing all characters to be used. It can be a translation file, string asset file, HTML file, etc.
  • Output a font with characters that aren't shown in the file removed

Here is the code:

#!/usr/bin/python2
import sys
import fontforge

if len(sys.argv) == 4:
    font = fontforge.open(sys.argv[1])

    with open(sys.argv[2], "r") as f:
        for i in f.read().decode("UTF-8"):
            font.selection[ord(i)] = True

    font.selection.invert()

    for i in font.selection.byGlyphs:
        font.removeGlyph(i)

    font.generate(sys.argv[3])
else:
    print "WARNING: Check the license of the source font\nbefore distributing the output font generated by this script.\nI'm not responsible for any legal issue caused by\ninappropriate use of this script!\n"
    print "Usage: {} [source font] [file with glyphs NOT to be removed] [output]".format(sys.argv[0])
    print "Example: {} /path/to/ukai.ttc chineseTranslation.txt ukaiStripped.ttf".format(sys.argv[0])

Please notice that it may not be legal to use this script on certain fonts. Ensure to checkout the license of the source font. I'm not responsible for any legal issue caused by using any font generated by this script.

Haimes answered 7/12, 2015 at 11:50 Comment(0)
P
25

FontTools has a program called pyftsubset that does it

https://github.com/behdad/fonttools

Usage something like this:

pyftsubset MyFont.otf --output-file=MyFontStripped.otf --unicodes-file=file-of-codes.txt

Where file-of-codes.txt contains a list of codes like:

U+09c78
U+09ce5
U+09ce7
U+09ce9
U+09ceb
U+09cec
U+09cf0
U+09cf3
U+09cf4
U+09cf6
...
Portsalut answered 11/5, 2016 at 9:58 Comment(1)
You can change convert unicode letters to ascii using uni2ascii (鱸 -> U+09c78): uni2ascii -a P letters-in-unicode.txt > letters-in-ascii.txtAlyose
O
5

As pointed out by others, you can use fonttools to remove unwanted glyphs from a font.

Installation is straightforward with pip:

pip install fonttools

You can then use pyftsubset function to restrict a font to a subset of characters. For instance if you want to keep only basic latin characters (punctuation, digits, letters):

pyftsubset myFont.otf --output-file=mySmallerFont.otf --unicodes=U+0020-007E

You can also convert the font at the same time:

pyftsubset myFont.otf --output-file=mySmallerFont.woff --flavor=woff --unicodes=U+0020-007E

Note that if you need to convert to woff2, you will have to install brotli first, using pip install brotli.

Full documentation is available here.

Oira answered 17/2, 2021 at 8:54 Comment(0)
S
4

Following latest comment: You can select several sets at a time in the left menu (Languages) using shift and command key all in one shot. Note the image keeps the Basic Latin (A-Z a-z). Once you have the sets selected click in the glyphs window and Select All (cmd-A). It will take some time if there are a lot as this case. Then cmd-Delete and you remove all. You only need to repeat the operation with 'Categories' and you're done. Then export .otf

Categories selection Languages selection

Smew answered 8/2, 2013 at 18:56 Comment(1)
I'm voting the answer up because of the effort put in, but I'm afraid that it hasn't really solved the issue. Firstly, there are still many many characters left in the standard All Letters section - in the thousands I believe - that are not all ASCII characters. As I said, I wanted to know how I can get rid of uneeded characters, and we've established that uneeded characters essentially translates as non-ASCII characters (for the english speaking web). The other issue is that when I tried to export the OTF it just hung or crashed. Unfortunately, my trial for Glyphs has now expired. :sEdda
B
4

Ran into this today because I was trying to save a font after CTRL+A and setting width (don't do that, btw). I went with a little more straight-forward Python scripting approach since I already had the font open in FontForge.

This assumes that you have the intended font open and have it active. It also supposes that you only want to keep the first 128 characters (ASCII range). It selects everything then uses the "less" flag to deselect the range from 0-127. Have a look at the FontForge Python scripting guide for some more info on the different ways you can manipulate the selection.

File > Execute Script

font = fontforge.activeFont()
font.selection.all()
font.selection.select(("ranges","less"),0,127)
font.clear()
Burletta answered 27/4, 2019 at 6:13 Comment(0)
S
3

A 10 MB Opentype font is very rare because it's very heavy weight, so I assume it's an in-house specific font and not commercial. For removing glyphs you can edit the font in FontForge (free) or any other commercial font editor and after that regenerate the .otf (also .ttf)

For building webfonts (I guess you'll need .ttf, .svg, .eot and .woff) you can use locally several free tools: sfnt2woff, ttf2eot, Batik SVG and with ttfautohint you can improve the way the font will look in the screen before making the conversion.

Smew answered 28/1, 2013 at 16:38 Comment(7)
Is there any other free programme that would be able to move font glyphs that you can think of? FontForge requires X11, and compiling, and I hear X Windows implementations of programmes are a complete pain to get installed and use. I'm not that experienced with compiling programmes from source at the moment, either.Edda
Depends on which platform you're working you can try to find a 100% functional demo. If you can use Mac OSX, try RoboFont or Glyphs. I don't know any 100% functional demo for Windows or Linux.Smew
Hmm, yeah I tried Glyphs before - the trial version. The OpenType font I have basically has a bucket load of characters in it. Not only Latin, but some Chinese and Japanese. Obviously deleting those is easy. But under the Latin filter there are still way too many glyphs. I was wondering if there is a way (on these font programmes) to strip it down easily, perhaps based on some simple standard for english characters (cross-referencing a character set?) without going through the headache of deleting individual glyphs? Also, Glyphs doesn't seem to export the .OTF to the location I've asked it.Edda
It seems you have enough with the ASCII character set. There are several ways of doing this: Select the ASCII character set, invert selection, delete (manually) and generate the font. Otherwise you can use Robofab, a python library if you're confortable with this for deleting glyphs which are not in a given character set. Glyphs uses the .ufo format. You can export .otf from Glyphs.Smew
Glyphs doesn't seem to be able to simply distinguish ASCII character sets. It just has a list of primitive filters like 'Number', 'Letters' and languages like 'Latin', 'Arabic', 'Japanese'. There also doesn't appear to be an 'invert selection' option anywhere. No experience with Python unfortunately. Can't seem to find where Glyph's is exporting the .OTF's either. Complete and utter fails going on here.Edda
Go to every set you don't need in the LANGUAGES section. Select All. Remove Glyphs. Go to Latin and there you can keep removing what you don't need. Then go to Export, first option is .otf (note that you will probably loose all kerning and other data). It's not an easy thing to do well if you're not familiar.Smew
I do want to confirm your answer but I'm just not quite there with it yet. Glyphs does not seem to provide me with a useful way of cross-referencing the characters that I want without me having to go through the whole thing and pick them individually. This seems completely counter-productive. Is there no way for me to select the individual glyphs needed based on a relevant character set? And, for the english speaking web world, what would that character set be? Am I looking for ASCII characters only? I obviously need all the necessary punctuation.Edda
L
1

fonttools has now been adopted by Google: https://github.com/googlei18n/fonttools. I've been using it to reduce the number of glyphs in Google's Noto fonts for use in an embedded system. Works a treat!

Lampert answered 4/7, 2018 at 13:50 Comment(0)
F
1

@remeus's answer inspired me, but I don't want to use the pyftsubset.exefontTools.subset directly; I want to use the script since I can easily add something to it.

CODE

import fontTools.subset
from pathlib import Path
import os

SOURCE_FILE = Path('YOUR.ttf') # ttf, otf, ...
OUT_FORMAT = 'woff' # otf, woff, woff2, ...
TEXT_FILE = Path('YOUR.txt')

def main():
    output_file = Path('temp') / Path(SOURCE_FILE.stem + '.' + OUT_FORMAT)
    output_file.parent.mkdir(parents=True, exist_ok=True)
    args = [SOURCE_FILE,
            f"--output-file={output_file}",
            # f"--text=中英文 abcdefg",
            f"--text-file={TEXT_FILE}",  # 中英文 abcdefg
            # "--unicodes=U+0020-007E",
            f"--flavor={OUT_FORMAT}",
            # 👇 https://github.com/fonttools/fonttools/blob/1df4f1e/Lib/fontTools/subset/__init__.py#L332-L337
            "--verbose=true", # https://github.com/fonttools/fonttools/blob/1df4f1e/Lib/fontTools/subset/__init__.py#L3015
            # "--xml=true",
            "--timing=true",
            ]
    fontTools.subset.main([str(_) for _ in args])
    os.startfile(output_file.parent)


if __name__ == "__main__":
    main()

Explain

Here are three ways to specify the subset you want

  1. --text: Write directly

  2. --text-file: Write the text in the file (It doesn't matter if you repeat it, the code will automatically remove it for you) (I recommend this way.)

    example:

    my.txt (utf-8)

    0123456
    abcdefg
    ABCDEFG
    中英文
    
  3. --unicodes: Use unicode range

📙 If you need to convert to woff2, you will have to install brotli first, using pip install brotlicffi.

resason:

Other issue

Below is indirect issues. You can directly skip this paragraph if you don't want to see it.

Here is my experience, I want to use the font on the browser, but I get an error as below

OTS parsing error: Web font size more than 30MB

my font-size

  • size: 40MB
  • format: ttf

So, If I change the format to Woff or Woff2 may solve the problem.

Still, you can use fontTools again

TTFont(Path()).save(Path())

It cost too much time when your file size is large.

package

fonttools

Federico answered 21/6, 2021 at 11:0 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.