[[TOC]]
SSFN is not just a renderer. It is also a file format, which comes with a command line tool to manage SSFN font files.
The sfnconv
tool has a native importer for FontForge's SplineFontDB format. Otherwise it uses the
freetype2 library so it can convert anything that freetype2 can read. This
includes all the common formats, TrueType (.ttf), OpenType (.otf), Web Fonts (.woff, .woff2) etc.
SSFN uses a data-loss compression. See the comparison table on how this influences the resulting file sizes. Storing a font in SSFN usually requires half the file size of an OTF without noticable loss of typeface details.
Reading bitmap font has no library dependencies, and the tool can read all the most common bitmap formats: PC Screen Font (.psfu, Linux Console's format), X11 Bitmap Distribution Format (.bdf) and X11 Portable Compiled Font (.pcf), GNU unifont (.hex), Windows FNT/FON (.fnt), GRUB's PFF2 (.pf2), Bits'N'Picas fonts (.kbits, .kbitx), and Yet Another Font Format (.yaff), as well as raw ROM images (.f08, .f12, .f16). Other bitmap formats, like bitmap sfnt are supported through freetype2.
Bitmap fonts are also compressed, but with a loss-less deduplication algorithm.
Pixmaps differ to bitmaps in that they contain not bits, but pixels, so they can include colored glyphs. This is primarily used for fancy colorful emoji icons. The only font format that can be used to import these is the Bytemap Font, however the converter can also read Truevision TARGA (.tga) and Portable Network Graphics (.png) image files to generate such pixmap SSFN fonts. TGA is a very simple and common format (the Gimp can save it, and Imagemagick can convert to it too), and PNG is the most widespread for lossless images, literally all image manipulator programs can handle it (JPEG is not good for fonts as it uses data-loss compression on pixels).
In images, ugly pink #FF00FF
and full black #000000
reads as transparency. For compatibility, the full
black FF000000 with alpha channel is transparency too, but you can use FF000001 or FF000200. Green of 1 is also
considered full black, because many programs (like the Gimp for example) saves FF000100.
If glyph dimensions are explicitly given with -X
and -Y
, then that big glyphs will be read from the image,
starting at UNICODE range, going from left to right and then top to bottom as many times that glyph dimension
fits on the image.
Without dimensions, the glyphs can be either stored vertically or horizontally in the image file, and UNICODE
range (flag -r
) must be specified. If image is taller than wide, then image width will became the glyph's
width, and image height will be divided by the size of the range, thus giving the height of each glyph. If image
is wider than tall, then image height will became glyph height and image width divided by range size will be glyph
width. For example 256 characters each with a 8 x 16 glyph can be stored in a 8 x 4096 or in an 2048 x 16 image.
Pixmaps fonts are also deduplicated the same way as bitmap fonts, and in addition they will be RLE compressed too. The combination of these two algorithms usually yields better compression ratios than PNG files for example (that's because SSFN does not store empty lines and it stores repeating image blocks only once, while PNG compresses all occurances separately, including empty parts).
SSFN has a plain text, human readable "source file" format, called ASC. The converter can read and write these as well. SSFN ASCII format is a text editor friendly format (with optional UTF-8 sequences in data). It was designed in a way that you can edit and copy'n'paste glyphs from one file to another.
Because not all bitmap font formats has font type, name and licensing information, you can pass those as arguments on the command line. In addition to bold and italic, there's two user-defined style, "u" and "U". You can use those to select a font from a font collection (see below). Family type can be specified using a number from 0 to 4, or alternatively with a letter.
-t: set type b=bold,i=italic,u=usrdef1,U=usrdef2,0=Serif,1/s=Sans,2/d=Decorative,3/m=Monospace,4/h=Handwriting
Example:
$ ./sfnconv -t b1 FreeSansBold.otf FreeSansBold.sfn
Strings can be specified as:
-n: set font unique name
-f: set font family (like FreeSerif, Vera, Helvetica)
-s: set subfamily (like Regular, Medium, Bold, Oblique, Thin, etc.)
-v: set font version / revision (like creation date for example)
-m: set manufacturer (creator, designer, foundry)
-l: set license (like MIT, GPL or URL to the license)
Example:
$ ./sfnconv -n "My Console Font" -f "MyOS" -s "Regular" -v "2020-05" -m "me https://github.com/me/myfont" -l "MIT" my.psf my.sfn
You can add one or more input files on the command line. They can be mixed, bitmaps and vector fonts
for example. All input file can be prefixed with a -r
(range) specifier, which will load only the
specified range from that input file.
$ ./sfnconv -r 0x900 0x97F unifont.hex console.sfn
Or
$ ./sfnconv -r U+0900 U+097F unifont.hex console.sfn
UTF-8 sequences are also accepted, if they are enclosed in single quotes (shell requires slashes):
$ ./sfnconv -r U+0900 U+097F unifont.hex -r \'A\' \'Z\' u_vga16.bdf console.sfn
Or you can use UNICODE block names, for example:
$ ./sfnconv -r "Cyrillic Extended-C" unifont.hex -r cjkunifiedideographs cjk.bdf console.sfn
If you need more control over what glyphs to include, you can use a sample file.
$ ./sfnconv -F sample.txt unifont.hex console.sfn
This will include all the glyphs that occur as UTF-8 encoded characters in the given sample file.
For compatibility it assumes that all ASCII characters are included (U+000000 to U+00007F). Specify
-r U+0080 U+10FFFF
to exclude them all at once, or use -S
(skip) to fine tune per character.
By default, the glyph in the first input file is used. You can replace glyphs from subsequent
files when -R
(replace) specified:
$ ./sfnconv unifont.hex -R fixedglyphs.bdf console.sfn
Sometimes fonts contain bad glyphs or invalid metrics. The converter warns you about these.
$ ./sfnconv UbuntuBold.ttf UbuntuBold.sfn
Loaded 'UbuntuBold.ttf' (FreeType2, 0 - 10FFFF)
Name 'Ubuntu' num_glyphs: 1264, units_per_EM: 1000, ascender: 932, underline: -183, vector
libsfn: inconsistent font: U+00048A, xMax 786 != xMin+w 788
libsfn: inconsistent font: U+00048B, xMax 619 != xMin+w 620
libsfn: inconsistent font: U+0004C5, xMax 809 != xMin+w 811
libsfn: inconsistent font: U+0004C6, xMax 654 != xMin+w 655
libsfn: inconsistent font: U+0004C9, xMax 765 != xMin+w 767
libsfn: inconsistent font: U+0004CA, xMax 606 != xMin+w 607
libsfn: inconsistent font: U+0004CD, xMax 941 != xMin+w 943
libsfn: inconsistent font: U+0004CE, xMax 783 != xMin+w 784
libsfn: inconsistent font: ascender 932 != max(yMax) 962
Numchars: 1194, Bounding box: (-170, -221), (3475, 962) dx 3645 dy 1183, w: 3455, h: 1142, baseline: 962
Scaling to 3455 x 3455, average: 570 x 741
libsfn: irregular dimensions in font U+001FAA: width: 1444
libsfn: irregular dimensions in font U+00EFFD: width: 1668
libsfn: irregular dimensions in font U+00F200: width: 3455 (6 times the average, can't be right!!!)
Kerning 1086 pairs
Saving 'UbuntuBold.sfn' (bin, compress)
Numchars: 1189, Numlayers: 2641
Done. Compressed to 14.69%
You can exclude certain glyphs with -S
(skip), and you can specify this flag multiple times:
$ ./sfnconv -S U+EFFD -S U+F200 UbuntuBold.ttf UbuntuBold.sfn
...
Numchars: 1192, Bounding box: (-170, -221), (1452, 962) dx 1622 dy 1183, w: 1444, h: 1142, baseline: 962
Scaling to 1444 x 1444, average: 565 x 740
...
You can also negate its effect by prefixing the code point with a !
, like
$ ./sfnconv -S !U+0020 UbuntuBold.ttf UbuntuBold.sfn
Means do not skip the space character, make it always be included in the output.
There's a special case for skip, -S undef
which will skip all characters that are not defined by
UNICODE.
$ ./sfnconv -S undef unifont.bdf.gz unifont.sfn.gz
Some formats don't store these, and many fonts has invalid values, so you might need to specify them manually. It helps a lot if you open OTF/TTF fonts in FontForge and save them to a new file, as FontForge does a very good job in validation and correction on metrics.
To convert into monospace, there are two flags. -T
can be used for bitmap fonts, which recalculates
the advance and rounds it up to the smallest multiple of 8 that's larger than width. -M
requires a
numeric argument, and rounds advances up to multiple of that, so this can be used for vector fonts too.
The convert to proportional flag -p
does the opposite, it checks which pixels are foreground and
sets the glyph's width and advance accordingly, hence all glyphs will have different sizes.
-b: horizontal baseline in pixels
-u: underline position in pixels (relative to baseline)
-a: add a constant to advance (some fonts need it, others don't)
-o: use original width and height instead of calculated one
-E: don't care about rounding errors
-T: recalculate bitmap advances
-M: monospacing, round advances up to multiple of n
-p: convert to proportional font
Example:
$ ./sfnconv -b 30 -u 2 -a 6 -o -E Vera.ttf Vera.sfn
You can also tweak how the output should be saved, and how much output should be printed.
-U: save uncompressed, non-gzipped output
-A: output SSFN ASCII
-B: rasterize vector fonts to bitmaps
-V: vectorize bitmap fonts to scalable fonts
-L: convert curves to lines in contours
-g: save grid information for hinting
-q: quiet, don't report font errors
You can put more fonts into a single file with the -c
(collection) flag. This has the advatange
that you can load them at once using ssfn_load
. It is typical to create Italic and Bold variations
for a typeface, and then pack them with the regular font into one file. For bitmap fonts, you can
place the same font at different sizes into a collection.
$ ./sfnconv -c VeraR.sfn VeraB.sfn VeraI.sfn VeraBI.sfn Vera.sfn
If you call extract without specifying file names, the converter will list the fonts in the
collection. The first coloumn is the same as with the -t
(type) flag, and separated by a tab
the size if used by -B
(rasterize to bitmap), and after another tab the rest of the line is
the font's unique name (specified by -n
):
$ ./sfnconv -e Vera.sfn
-t -B -n
1 241 Bitstream Vera Sans
b1 214 Bitstream Vera Sans Bold
i1 254 Bitstream Vera Sans Oblique
bi1 220 Bitstream Vera Sans Bold Oblique
Then you must specify exactly as many filenames as the number of fonts in the collection to actually extract the fonts:
$ ./sfnconv -e Vera.sfn VeraR.sfn VeraBd.sfn VeraIt.sfn VeraBI.sfn
You can use the -C
(coverage) flag to quickly check the code point range coverage in an
SSFN font. This will generate reports like this:
$ ./sfnconv -C u_vga16.sfn
Loaded 'u_vga16.sfn' (SSFN BIN, 0 - 10FFFF)
| Coverage | NumChar | Start | End | Description |
| -------: | ------: | ------ | ------ | ------------------------------------------ |
| 75.7% | 97 | 000000 | 00007F | Basic Latin |
| 75.0% | 96 | 000080 | 0000FF | Latin-1 Supplement |
| 100.0% | 128 | 000100 | 00017F | Latin Extended-A |
| 85.5% | 178 | 000180 | 00024F | Latin Extended-B |
...
| 100.0% | 26 | 00FE50 | 00FE6F | Small Form Variants |
| 99.2% | 140 | 00FE70 | 00FEFF | Arabic Presentation Forms-B |
| 20.0% | 1 | 00FFF0 | 00FFFF | Specials |
| -------- | ------- | ------------------------------------------------------------ |
| 62.0% | 2899 | = = = = = = = = Overall Coverage = = = = = = = = |
You can use the converter to dump and debug SSFN fonts.
Flag | Description |
---|---|
-d |
do basic checks and dump the font's header |
-dd |
dumps the string tables (like font name, manufacturer, license etc.) |
-ddd |
dumps the fragments table |
-dddd |
dumps the characters and ligatures tables |
-ddddd |
dumps the kerning offset table |
-D |
dumps everything, even inconsistent files |
Dumping a table will do a through check on that table. If everything went well, you should see a
Font parsed OK.
message. Examples:
$ ./sfnconv -dd FreeSerifI.sfn
Dumping 'FreeSerifI.sfn'
font/x-ssfont Scalable Screen Font
---Header---
magic: 'SFN2'
size: 251797
type: 20 SSFN_FAMILY_SERIF, SSFN_STYLE_ITALIC
features: 00 rev 0
width, height: 255 207
baseline: 154
underline: 180
fragments_offs: 0x000000d9
characters_offs: 0x00030c98
ligature_offs: 0x00000000
kerning_offs: 0x0003beee
cmap_offs: 0x00000000
---String Table---
0. name "GNU: FreeSerif Italic: 2012"
1. family "FreeSerif"
2. subfamily "Italic"
3. revision "Version 0412.2268"
4. manufacturer "https://savannah.gnu.org/projects/freefont/"
5. license "Copyright 2002, 2003, 2005, 2008, 2009, 2010, 2012 GNU Freefont contributors."
Font parsed OK.
Fragments table:
$ ./sfnconv -ddd ../fonts/FreeSerif.sfn
Dumping '../fonts/FreeSerif.sfn'
font/x-ssfont Scalable Screen Font
---Header---
magic: 'SFN2'
size: 840260
type: 00 SSFN_FAMILY_SERIF
features: 00 rev 0
width, height: 255 214
baseline: 142
underline: 165
fragments_offs: 0x000000da
characters_offs: 0x000a8df9
ligature_offs: 0x00000000
kerning_offs: 0x000cb496
cmap_offs: 0x00000000
name: "GNU: FreeSerif Normal: 2012"
---Fragments Table---
0000da: 04 SSFN_FRAG_CONTOUR n=5
54:0=0 SSFN_CONTOUR_MOVE p= 17, 8
54:2=1 SSFN_CONTOUR_LINE p= 8, 0
54:4=1 SSFN_CONTOUR_LINE p= 0, 7
54:6=1 SSFN_CONTOUR_LINE p= 9, 16
01:0=1 SSFN_CONTOUR_LINE p= 17, 8
...
0001ac: c0 01 00 SSFN_FRAG_KERNING n=2 c=0
4c 00 00 4c 00 00 9a 1d U+00004C..U+00004C o=0cd230
f6 00 00 f6 00 00 9a 1d U+0000F6..U+0000F6 o=0cd230
0001bf: 04 SSFN_FRAG_CONTOUR n=5
fc:0=0 SSFN_CONTOUR_MOVE p= 17, 8
fc:2=3 SSFN_CONTOUR_CUBIC p= 8, 0 c1= 17, 4 c2= 13, 0
fc:4=3 SSFN_CONTOUR_CUBIC p= 0, 8 c1= 3, 0 c2= 0, 4
fc:6=3 SSFN_CONTOUR_CUBIC p= 8, 17 c1= 0, 13 c2= 3, 17
03:0=3 SSFN_CONTOUR_CUBIC p= 17, 8 c1= 13, 17 c2= 17, 13
...
Font parsed OK.
Characters table:
$ ./sfnconv -dddd ../fonts/FreeSerif.sfn
Dumping '../fonts/FreeSerif.sfn'
font/x-ssfont Scalable Screen Font
---Header---
magic: 'SFN2'
size: 840260
type: 00 SSFN_FAMILY_SERIF
features: 00 rev 0
width, height: 255 214
baseline: 142
underline: 165
fragments_offs: 0x000000da
characters_offs: 0x000a8df9
ligature_offs: 0x00000000
kerning_offs: 0x000cb496
cmap_offs: 0x00000000
name: "GNU: FreeSerif Normal: 2012"
---Characters Table---
0a8df9: 9f --- skip 32 code points ---
0a8dfa: 00 00 00 00 29 00 --- U+000020 n=0 f=0 o=0 w=0 h=0 ax=41 ay=0 ---
0a8e00: 00 02 11 91 37 00 --- U+000021 n=2 f=0 o=0 w=17 h=145 ax=55 ay=0 ---
00 26 ea 49 00 x= 0 y= 38 frag=0049ea SSFN_FRAG_CONTOUR
00 7f e8 43 00 x= 0 y=127 frag=0043e8 SSFN_FRAG_CONTOUR
0a8e10: 00 03 28 4c 42 00 --- U+000022 n=3 f=0 o=0 w=40 h=76 ax=66 ay=0 ---
01 00 9f 2d 01 x= 1 y= 0 frag=012d9f SSFN_FRAG_KERNING
00 26 02 a4 00 x= 0 y= 38 frag=00a402 SSFN_FRAG_CONTOUR
1a 26 a9 13 02 x= 26 y= 38 frag=0213a9 SSFN_FRAG_CONTOUR
0a8e25: 00 03 4c 8f 52 00 --- U+000023 n=3 f=0 o=0 w=76 h=143 ax=82 ay=0 ---
01 00 44 25 00 x= 1 y= 0 frag=002544 SSFN_FRAG_KERNING
00 28 b8 1e 08 x= 0 y= 40 frag=081eb8 SSFN_FRAG_CONTOUR
1a 4f 39 51 01 x= 26 y= 79 frag=015139 SSFN_FRAG_CONTOUR
0a8e3a: 00 04 40 9d 52 00 --- U+000024 n=4 f=0 o=0 w=64 h=157 ax=82 ay=0 ---
01 00 ac 01 00 x= 1 y= 0 frag=0001ac SSFN_FRAG_KERNING
00 1e b8 04 06 x= 0 y= 30 frag=0604b8 SSFN_FRAG_CONTOUR
0c 2c 9b 2e 01 x= 12 y= 44 frag=012e9b SSFN_FRAG_CONTOUR
22 61 ad 2e 01 x= 34 y= 97 frag=012ead SSFN_FRAG_CONTOUR
...
Font parsed OK.