Commit 2bc11fa1 authored by bzt's avatar bzt

Added glyph variants

parent acd8ccb0
......@@ -2,7 +2,7 @@ Scalable Screen Font
====================
This is a single ANSI C/C++ header file, scalable bitmap and vector font renderer. It has only memory related
libc dependency and it does not use floating point numbers. It's extremely small (approx. 21k) and it is very
libc dependency and it does not use floating point numbers. It's extremely small (approx. 22k) and it is very
easy on memory, perfect for embedded systems and hobby OS kernels. It was a hobby project for me, so donations
and contributions would be much appreciated if it turns out to be useful to you.
......@@ -104,14 +104,21 @@ bitmap fonts. It does not allocate memory or need libc. It can't scale, but it c
(like 8x16 for Latin letters, and 16x16 for CJK ideograms), so you can implement a true UNICODE console with
this renderer.
Font Editor
-----------
Besides of the converters that can import various font formats, there's also an SSFN font editor available:
<img alt="Scalable Screen Font Editor" src="https://gitlab.com/bztsrc/scalable-font/raw/master/sfnedit4.png">
License
-------
Both the renderers and the converter utilities are licensed under the terms of MIT license in the hope that
they will be useful.
Both the renderers, the converter utilities and the editor are licensed under the terms of MIT license in the
hope that they will be useful.
IMPORTANT NOTE: although the renderers and the file format is licensed under MIT, it is possible that the font
stored in a SSFN file is NOT! Always consult the license field in the font's header! See `sfn2asc -h`.
IMPORTANT NOTE: although the file format is licensed under MIT, it is possible that the font stored in a SSFN
file is NOT! Always consult the license field in the font's header! See `sfn2asc -h`.
Dependencies
------------
......@@ -128,6 +135,8 @@ The scalable font converter is built on the freetype2 library to read vector fon
converter has no dependencies other than libc, but can be built optionally with zlib to read and write LZW
compressed (gzip deflate) files on-the-fly.
The editor uses SDL2 with an X11 fallback, and optionally needs zlib.
The test applications use SDL2 to create a window and display the rendered texts.
Known Bugs
......@@ -151,6 +160,9 @@ at the moment, it only use one axis partitions instead of boxes.
A [portable SSFN editor with GUI](https://gitlab.com/bztsrc/scalable-font/tree/master/sfnedit). I'm already on it.
It' still very early in development, you can't modify yet, but it's good enough to preview any SSFN font.
Converters only write the default glyph variants except for SSFN ASCII files where all variants are supported. The
editor is capable of editing all variant layers.
Authors
-------
......
......@@ -74,6 +74,7 @@ As my favourite principle is K.I.S.S., there's only a few, clearly named functio
- if you want to change the size for example, you can call `ssfn_select()` again, no need to load the fonts again.
- if you need it, you can get the rendered text's dimensions in advance with `ssfn_bbox()`.
- use `ssfn_utf8()` to decode an UTF-8 multibyte character into UNICODE code point.
- optionally select a glyph variant with `ssfn_variant()`.
- call `ssfn_render()` to rasterize a glyph in the given style and size for that UNICODE code point.
- draw the returned bitmap on your screen at cursor position.
- optionally alter the returned advance offsets with `ssfn_kern()`.
......@@ -238,7 +239,53 @@ Parameter defines:
### Return value
Error code. `SSFN_ERR_NOFACE` returned if no font could be found, otherwise `SSFN_ERR_BADx` refers to
the invalid argument.
the invalid argument. This function always resets glyph variant to `SSFN_VARIANT_DEFAULT`.
## Select Glyph Variant
```c
int ssfn_variant(ssfn_t *ctx, int variant);
```
This is optional, and only used in special cases. A UNICODE character may have several variants
depending on context or localization, you can choose which one to render with this function. If
a specific variant is not found, then the renderer fallbacks to `SSFN_VARIANT_DEFAULT`.
The contextual version variants are currently unused and no SSFN fonts are generated with such
variants because UNICODE provides different code points for those glyphs. This feature can be used
by special applications if needed. There's no isolated variant because default should contain those
in the first place.
Also note hese can be used for localization too. Currently there's only one such case, the
`SSFN_VARIANT_INITIAL` (or `SSFN_VARIANT_LOCAL1`) is used to store Serbian/Macedonian
B D G P T letter variations.
### Parameters
| Parameter | Description |
| --------- | ----------- |
| ctx | pointer to the renderer's context |
| variant | one of the variant defines, see below |
Parameter defines:
| variant | Description |
| ----------------------- | ----------- |
| `SSFN_VARIANT_DEFAULT` | selects the default (isolated) glyph variant |
| `SSFN_VARIANT_LOCAL0` | same as default |
| `SSFN_VARIANT_INITIAL` | selects the version when the glyph is at the beginning |
| `SSFN_VARIANT_LOCAL1` | same as initial |
| `SSFN_VARIANT_MEDIAL` | selects the version when the glyph is in the middle |
| `SSFN_VARIANT_LOCAL2` | same as medial |
| `SSFN_VARIANT_FINAL` | selects the version when the glyph is at the end |
| `SSFN_VARIANT_LOCAL3` | same as final |
| `SSFN_VARIANT_LOCAL4` | unused for now |
| `SSFN_VARIANT_LOCAL5` | unused for now |
| `SSFN_VARIANT_LOCAL6` | unused for now |
### Return value
Error code. `SSFN_ERR_INVINP` returned if wrong parameters supplied, otherwise `SSFN_OK`.
## Convert UTF-8 to UNICODE
......@@ -247,8 +294,13 @@ uint32_t ssfn_utf8(char **str);
```
Decodes an UTF-8 multibyte character. Be careful, this function does not check its input, expects that pointer is
valid and points to a string with only valid UTF-8 sequences. The behaviour with invalid input is undefined. All
the other functions check for valid input, this is an exception because it's expected to be called many times.
valid and points to a string with only valid UTF-8 sequences. The behaviour with invalid input is undefined. All the
other functions check for valid input, this is an exception because it's expected to be called repeatedly many times.
This function does not take code point combinations into consideration either. That's the caller responsibility
to generate and use the proper glyphs for UNICODE Variation Selectors and other special cases (see `ssfn_variant()`).
For Variation Selectors I would rather recommend to use separate fonts, one for each VSx, and render the normal UNICODE
code point with those special fonts.
### Parameters
......@@ -268,9 +320,12 @@ ssfn_glyph_t *ssfn_render(ssfn_t *ctx, uint32_t unicode);
Render a glyph. It is possible to load more fonts with different UNICODE code range coverage, the
renderer will pick the ones which have a glyph defined for `unicode`. If more fonts have glyphs for this
character, then the renderer will look for the best style match to figure out which font to use, unless
you've asked for a specific font with `SSFN_FAMILY_BYNAME`. If there's no font for the requested style,
then the renderer will mimic bold or italic.
character, then the renderer will look for the best variant and style match to figure out which font to
use, unless you've asked for a specific font with `SSFN_FAMILY_BYNAME`. If there's no font for the
requested style, then the renderer will mimic bold or italic.
If you have called `ssfn_variant()` before this call, then glyph variant will also participate in
font selection, and takes precedence over style.
### Parameters
......@@ -320,6 +375,8 @@ to get valid results. For this purpose, you can pass `SSFN_MODE_NONE` to the ren
`SSFN_FAMILY_BYNAME` to select exactly one font, and then you don't have to call ssfn_render). If
the character or the kerning pair not found, `x`and `y` will be left unchanged.
Kerning also takes glyph variants into considerations if `ssfn_variant()` used.
### Parameters
| Parameter | Description |
......
......@@ -77,6 +77,17 @@ After this line come glyph height lines with glyph width times hex colors `(aarr
Each line encode one row of the pixmap. The values `00000000` and `FF000000` mean transparency, which can be written
as eight dots `........` for increased visibility. To encode black foreground color, use `FE000000` or `FF000100`.
#### Glyph Variants
Alternative glyph versions must be stored right after the default one. They are encoded exactly the same way, except
their header line has a variant number at the 4th character, `+!-(variant)-(unicode)-(adv x)-(adv y)---`,
`++-(variant)-(unicode)-`, `+=-(variant)-(unicode)-(adv x)-(adv y)---`, `+%-(variant)-(unicode)-(adv x)-(adv y)---`.
Variant numbers are from '0' to '6', and the first 4 variants are also recognized by the characters '-' (0, the default,
also the isolated variant), 'i' (1, initial), 'm' (2, medial), 'f' (3, final).
Variations are usually not needed as contextual letters have their own UNICODE code points, and they are stored as
the default variant.
### Kerning Table (optional)
The line starting with `+@---K` encode kerning information. The line is not parsed any further, instead the following
......
......@@ -34,8 +34,7 @@ results.
SSFN can store everything that a TrueType or OpenType font can, except for:
- charset encodings,
- expressed ligatures and
- glyph alternatives.
- expressed ligatures.
As for character encoding, SSFN exclusively uses UNICODE (ISO-10464) by design. For
ligatures, I suggest to use the proper UNICODE code point for the ligature (which can
......@@ -67,7 +66,7 @@ The basic structure of the format is as follows:
| Header | A fixed sized struct starting with 'SSFN' magic and basic font information |
| Strings | Human readable UTF-8 strings, like font's name and license |
| Fragments | Sub-glyph information table |
| Characters | UNICODE-glyph mapping |
| Characters | UNICODE-glyph mappings |
| Kerning | Kerning table (character combination distances, optional) |
| Colormap | Palette for color commands and pixmap fragments (optional) |
| End magic | Terminating 'NFSS' magic bytes |
......@@ -100,10 +99,12 @@ The uncompressed file's header is the same as `ssfn_font_t` struct, see the
| 24 | 2 | bounding box right |
| 26 | 2 | bounding box bottom |
| 28 | 4 | fragments table offset (relative to magic) |
| 32 | 4 | number of fragments |
| 36 | 4 | character table offset |
| 40 | 4 | number of characters |
| 44 | 4 | kerning table offset |
| 32 | 4 | character table offset (glyph variant 0, default also isolated) (5) |
| 36 | 4 | character table offset (glyph variant 1, initial) |
| 40 | 4 | character table offset (glyph variant 2, medial) |
| 44 | 4 | character table offset (glyph variant 3, final) |
| 48 | 12 | character table offset (glyph variants 4-6, must be zero for now) |
| 60 | 4 | kerning table offset |
Note(1): style bits 2 (underline) and 3 (strike-through) are never stored in the file, only used by the API.
......@@ -123,6 +124,11 @@ Note(3): For future use, this specification is for the original format, revision
Note(4): bounding box defines the biggest box that includes every points for all the glyphs.
Note(5): only the first character table (for the default glyph variants) is mandatory, the others can
be zero. Variant 1 is also used for Serbian/Macedonian Cyrillic letters, but otherwise this feature
is not utilized as contextual glyphs have their own UNICODE code points and they are stored as default
variant.
String Table
------------
......@@ -185,36 +191,45 @@ After that other commands follow, depending on quality:
| Quality | Grid size | Path command | Bytes | Command format |
| ------: | --------: | -------------- | ----: | -------------- |
| 0-3 | 128 | color (1) | 2 | 0000000C 0ccccccc |
| 0-3 | 128 | color gradient | 5 | 0000010C 0ccccccc dddddddd eeeeeeee ffffffff |
| 0-3 | 128 | line to | 2 | 1xxxxxxx 0yyyyyyy |
| 0-3 | 128 | quad curve to | 4 | 0xxxxxxx 1yyyyyyy 0aaaaaaa 0bbbbbbb (where (a,b) is the control point) |
| 0-3 | 128 | cubic curve to | 6 | 1xxxxxxx 1yyyyyyy 0aaaaaaa 0bbbbbbb 0ccccccc 0ddddddd (where c1=(a,b), c2=(c,d)) |
| - | - | - | - | - |
| 4 | 256 | color | 2 | 00000000 cccccccc |
| 4 | 256 | color gradient | 5 | 00000100 cccccccc dddddddd eeeeeeee ffffffff |
| 4 | 256 | line to | 3 | 00000001 xxxxxxxx yyyyyyyy |
| 4 | 256 | quad curve to | 4 | 00000010 xxxxxxxx yyyyyyyy aaaaaaaa bbbbbbbb |
| 4 | 256 | cubic curve to | 6 | 00000011 xxxxxxxx yyyyyyyy aaaaaaaa bbbbbbbb cccccccc dddddddd |
| - | - | - | - | - |
| 5 | 512 | color | 2 | 00000000 cccccccc |
| 5 | 512 | color gradient | 5 | 00000100 cccccccc dddddddd eeeeeeee ffffffff |
| 5 | 512 | line to | 3 | 0000YX01 xxxxxxxx yyyyyyyy (where (uppercase<<8 + lowercase) gives the coordinate) |
| 5 | 512 | quad curve to | 5 | 00BAYX10 xxxxxxxx yyyyyyyy aaaaaaaa bbbbbbbb |
| 5 | 512 | cubic curve to | 7 | DCBAYX11 xxxxxxxx yyyyyyyy aaaaaaaa bbbbbbbb cccccccc dddddddd |
| - | - | - | - | - |
| 6 | 1024 | color | 2 | 00000000 cccccccc |
| 6 | 1024 | color gradient | 5 | 00000100 cccccccc dddddddd eeeeeeee ffffffff |
| 6 | 1024 | line to | 3 | 00YYXX01 xxxxxxxx yyyyyyyy |
| 6 | 1024 | quad curve to | 6 | 00YYXX10 xxxxxxxx yyyyyyyy 0000BBAA aaaaaaaa bbbbbbbb |
| 6 | 1024 | cubic curve to | 8 | 00YYXX11 xxxxxxxx yyyyyyyy DDCCBBAA aaaaaaaa bbbbbbbb cccccccc dddddddd |
| - | - | - | - | - |
| 7 (2) | 2048 | color | 2 | 00000000 cccccccc |
| 7 | 2048 | color gradient | 5 | 00000100 cccccccc dddddddd eeeeeeee ffffffff |
| 7 | 2048 | line to | 3 | YYYXXX01 xxxxxxxx yyyyyyyy |
| 7 | 2048 | quad curve to | 6 | YYYXXX10 xxxxxxxx yyyyyyyy 0BBB0AAA aaaaaaaa bbbbbbbb |
| 7 | 2048 | cubic curve to | 9 | YYYXXX11 xxxxxxxx yyyyyyyy 0BBB0AAA aaaaaaaa bbbbbbbb 0DDD0CCC cccccccc dddddddd |
| - | - | - | - | - |
| 8 (2) | 4096 | color | 2 | 00000000 cccccccc |
| 8 | 4096 | color gradient | 5 | 00000100 cccccccc dddddddd eeeeeeee ffffffff |
| 8 | 4096 | line to | 4 | 00XXXX01 xxxxxxxx 0000YYYY yyyyyyyy |
| 8 | 4096 | quad curve to | 7 | 00XXXX10 xxxxxxxx 0000YYYY yyyyyyyy BBBBAAAA aaaaaaaa bbbbbbbb |
| 8 | 4096 | cubic curve to | 10 | 00XXXX11 xxxxxxxx 0000YYYY yyyyyyyy BBBBAAAA aaaaaaaa bbbbbbbb DDDDCCCC cccccccc dddddddd |
Note(1): the color command selects an ARGB from the color map.
Note(1): the color command selects an ARGB from the color map. With gradients, four colors are selected, which
represents the four corners of the bounding box, c=top left, d=top right, e=bottom left and f=bottom right. The
glyph must be rasterized with gradients between those. If a "color" or "gradient" command present, it must come
right after the implicit "move to" command.
Note(2): contour commands for grid size 2048 and above are not implemented yet.
......@@ -279,15 +294,19 @@ runs, one if there's a glyph defined for the code point, and one if not (skip ru
table always describes the entire 0 .. 0x10FFFF UNICODE range. If UNICODE Inc. decides to
increase that range, then file revision must be incremented.
There's a Character Lookup Table for each glyph variants, but only the first table is
mandatory.
| 1st byte | More bytes | Description |
| -------- | ---------- | ----------- |
| 0nnnnnnn | 9+x | There's a glyph for this character, which consist of N fragments |
| 10nnnnnn | 0 | Skip N+1 code points (up to 64) |
| 11NNNNNN | 1 | Skip N+1 code points (N << 8 + additional byte, up to 16384) |
In edge case, when the font does not encode any characters (shouldn't happen, at least
In edge case, when the variant does not encode any characters (shouldn't happen, at least
one character required), then the characters table contains 68 skip runs to cover the
entire UNICODE range, which is 136 bytes in the file.
entire UNICODE range, which is 136 bytes in the file. In this case the table's offset in
header is zero, and no runs are stored.
A TrueType font usually contains 2 or 3 tops composite glyphs. SSFN can store more than a
hundred compositions, and it compresses the glyphs by storing normalized glyph fragments.
......
docs/sfnedit3.png

13.4 KB | W: | H:

docs/sfnedit3.png

12 KB | W: | H:

docs/sfnedit3.png
docs/sfnedit3.png
docs/sfnedit3.png
docs/sfnedit3.png
  • 2-up
  • Swipe
  • Onion skin
No preview for this file type
No preview for this file type
Scalable Screen Font - Example Fonts
====================================
- FreeSans.sfn.gz - vector based font with cubic Bezier curves, converted from [FreeSans.otf](https://www.gnu.org/software/freefont/) of the GNU freefont project
- FreeSans.sfn.gz - vector based font with cubic Bezier curves, converted from [FreeSans.otf](https://www.gnu.org/software/freefont/) of the GNU freefont project with maximum quality
- FreeSans4.sfn.gz - same, but compressed at recommended quality level 4
- FreeSerif.sfn.gz - vector based font, converted from FreeSerif.otf of the GNU freefont project
- FreeSerif4.sfn.gz - same, but compressed at recommended quality level 4
- VeraX.sfn - vector based fonts with quadratic Bezier curves, converted from [VeraX.ttf](https://www.gnome.org/fonts/) of the GNOME Bitstream Vera Fonts (variations on style)
- u_vga16.sfn - bitmap font, converted from [u_vga16.bdf](http://www.inp.nsk.su/~bolkhov/files/fonts/univga/), made by Dmitry Bolkhovityanov
......
No preview for this file type
No preview for this file type
No preview for this file type
No preview for this file type
No preview for this file type
No preview for this file type
No preview for this file type
No preview for this file type
No preview for this file type
This diff is collapsed.
This diff is collapsed.
/*
* ttf2sfn.c
* sfnconv/ttf2sfn.c
*
* Copyright (C) 2019 bzt (bztsrc@gitlab)
*
......@@ -97,6 +97,7 @@ FT_Library library;
FT_Face face;
FT_SfntName name;
FT_GlyphSlot glyph;
int fragments_num = 0, characters_num = 0;
int hinting = 0, zip = 0, adv = 0, gridsize = 1024, unicode, kgrpnum = 0, hints[2048];
int hintunit, frag_len = 0, frag_mx, frag_my, frag_ix, frag_iy, frag_ax, frag_ay;
int min_y, max_y, min_x, max_x, max_s, d_x, d_y, ox, oy, lx, ly, gx, gy;
......@@ -115,11 +116,11 @@ void hint_add()
char_t *c;
int i, j, k, l, m, n, *hintgrid;
if(!hinting || !ssfn_hdr.characters_num) return;
if(!hinting || !characters_num) return;
for(i=n=0;i<1024;i++) { if(hints[i] >= gridsize/SSFN_HINTING_THRESHOLD) n++; }
if(n < 2) return;
c = &chars[ssfn_hdr.characters_num-1];
c = &chars[characters_num-1];
hintgrid = (int*)malloc(n * sizeof(int));
if(!hintgrid) { fprintf(stderr,"memory allocation error\n"); exit(2); }
......@@ -142,7 +143,7 @@ void hint_add()
ssfn_hdr.features |= SSFN_FEAT_HASHINT;
if(m > 255) ssfn_hdr.features |= SSFN_FEAT_HBIGCRD;
for(j=-1,i=0;i<(int)ssfn_hdr.fragments_num;i++) {
for(j=-1,i=0;i<fragments_num;i++) {
if(frags[i].type == SSFN_FRAG_HINTING && frags[i].len == l &&
!memcmp(frags[i].data, hintgrid, l*sizeof(int))) {
j = i;
......@@ -150,8 +151,8 @@ void hint_add()
}
}
if(j==-1) {
j = ssfn_hdr.fragments_num++;
frags = (frag_t*)realloc(frags, ssfn_hdr.fragments_num*sizeof(frag_t));
j = fragments_num++;
frags = (frag_t*)realloc(frags, fragments_num*sizeof(frag_t));
if(!frags) { fprintf(stderr,"memory allocation error\n"); exit(2); }
frags[j].type = SSFN_FRAG_HINTING;
frags[j].len = l;
......@@ -180,11 +181,11 @@ void char_add(int x, int y, int w, int h, int f)
char_t *c;
int i;
if(!ssfn_hdr.characters_num || chars[ssfn_hdr.characters_num-1].unicode != unicode) {
ssfn_hdr.characters_num++;
chars = (char_t*)realloc(chars, ssfn_hdr.characters_num*sizeof(char_t));
if(!characters_num || chars[characters_num-1].unicode != unicode) {
characters_num++;
chars = (char_t*)realloc(chars, characters_num*sizeof(char_t));
if(!chars) { fprintf(stderr,"memory allocation error\n"); exit(2); }
c = &chars[ssfn_hdr.characters_num-1];
c = &chars[characters_num-1];
c->kgrp = -1;
c->frags = c->kern = NULL;
c->len = c->klen = 0;
......@@ -194,7 +195,7 @@ void char_add(int x, int y, int w, int h, int f)
c->adv_x = (((glyph->advance.x*(gridsize-1+(gridsize>>7))+(max_s/2))/max_s)+63)/64 + adv; /* make sure we round it up */
c->adv_y = (((glyph->advance.y*(gridsize-1+(gridsize>>7))+(max_s/2))/max_s)+63)/64;
} else
c = &chars[ssfn_hdr.characters_num-1];
c = &chars[characters_num-1];
if(w > c->w) c->w = w;
if(h > c->h) c->h = h;
if(x < c->bear_l) c->bear_l = x;
......@@ -356,7 +357,7 @@ void frag_add()
}
}
}
for(i=0;i<(int)ssfn_hdr.fragments_num;i++) {
for(i=0;i<fragments_num;i++) {
if(frags[i].type == SSFN_FRAG_CONTOUR && frags[i].len == frag_len &&
!memcmp(frags[i].data, contour, frag_len*sizeof(cont_t))) {
j = i;
......@@ -364,8 +365,8 @@ void frag_add()
}
}
if(j==-1) {
j = ssfn_hdr.fragments_num++;
frags = (frag_t*)realloc(frags, ssfn_hdr.fragments_num*sizeof(frag_t));
j = fragments_num++;
frags = (frag_t*)realloc(frags, fragments_num*sizeof(frag_t));
if(!frags) { fprintf(stderr,"memory allocation error\n"); exit(2); }
frags[j].type = SSFN_FRAG_CONTOUR;
frags[j].len = frag_len;
......@@ -570,43 +571,63 @@ int main(int argc, char **argv)
if(arg_name) { /* unique font name */
s = strlen(arg_name)+1;
memcpy(ssfn_strings, arg_name, s);
while(ssfn_strings[s-1]==' ' || ssfn_strings[s-1]=='\t') s--;
ssfn_strings[s] = 0;
} else if(!FT_Get_Sfnt_Name(face, 3, &name)) {
memcpy(ssfn_strings, name.string, name.string_len);
s += name.string_len+1;
while(ssfn_strings[s-1]==' ' || ssfn_strings[s-1]=='\t') s--;
ssfn_strings[s] = 0;
} else if(!FT_Get_Sfnt_Name(face, 20, &name)) {
memcpy(ssfn_strings + s, name.string, name.string_len);
s += name.string_len+1;
while(ssfn_strings[s-1]==' ' || ssfn_strings[s-1]=='\t') s--;
ssfn_strings[s] = 0;
} else s++;
if(!FT_Get_Sfnt_Name(face, 1, &name)) { /* family name */
memcpy(ssfn_strings + s, name.string, name.string_len);
s += name.string_len+1;
while(ssfn_strings[s-1]==' ' || ssfn_strings[s-1]=='\t') s--;
ssfn_strings[s] = 0;
} else s++;
if(!FT_Get_Sfnt_Name(face, 2, &name)) { /* subfamily name */
memcpy(ssfn_strings + s, name.string, name.string_len);
s += name.string_len+1;
while(ssfn_strings[s-1]==' ' || ssfn_strings[s-1]=='\t') s--;
ssfn_strings[s] = 0;
} else s++;
if(!FT_Get_Sfnt_Name(face, 5, &name)) { /* version */
memcpy(ssfn_strings + s, name.string, name.string_len);
s += name.string_len+1;
while(ssfn_strings[s-1]==' ' || ssfn_strings[s-1]=='\t') s--;
ssfn_strings[s] = 0;
} else s++;
if(!FT_Get_Sfnt_Name(face, 8, &name)) { /* manufacturer */
memcpy(ssfn_strings + s, name.string, name.string_len);
s += name.string_len+1;
while(ssfn_strings[s-1]==' ' || ssfn_strings[s-1]=='\t') s--;
ssfn_strings[s] = 0;
} else if(!FT_Get_Sfnt_Name(face, 9, &name)) {
memcpy(ssfn_strings + s, name.string, name.string_len);
s += name.string_len+1;
while(ssfn_strings[s-1]==' ' || ssfn_strings[s-1]=='\t') s--;
ssfn_strings[s] = 0;
} else s++;
if(!FT_Get_Sfnt_Name(face, 0, &name)) { /* copyright */
memcpy(ssfn_strings + s, name.string, name.string_len);
s += name.string_len+1;
while(ssfn_strings[s-1]==' ' || ssfn_strings[s-1]=='\t') s--;
ssfn_strings[s] = 0;
} else if(!FT_Get_Sfnt_Name(face, 7, &name)) {
memcpy(ssfn_strings + s, name.string, name.string_len);
s += name.string_len+1;
while(ssfn_strings[s-1]==' ' || ssfn_strings[s-1]=='\t') s--;
ssfn_strings[s] = 0;
} else s++;
ssfn_hdr.size = ssfn_hdr.fragments_offs = sizeof(ssfn_font_t) + s;
......@@ -640,7 +661,7 @@ int main(int argc, char **argv)
ssfn_hdr.bbox_right = max_x;
/* serialize fragments */
for(fs=0,i=0;i<(int)ssfn_hdr.fragments_num;i++) {
for(fs=0,i=0;i<fragments_num;i++) {
frg = (unsigned char*)realloc(frg, fs+16);
if(!frg) { fprintf(stderr,"memory allocation error\n"); return 2; }
frags[i].pos = ssfn_hdr.fragments_offs + fs;
......@@ -834,15 +855,15 @@ int main(int argc, char **argv)
}
if(frags[i].data) free(frags[i].data);
}
ssfn_hdr.characters_offs = ssfn_hdr.fragments_offs + fs;
ssfn_hdr.characters_offs[0] = ssfn_hdr.fragments_offs + fs;
ssfn_hdr.size += fs;
/* serialize character map */
if(ssfn_hdr.quality < 5 && ssfn_hdr.characters_offs < 65536) l = 0; else
if(ssfn_hdr.characters_offs < 1048576) l = 1; else l = 2;
if(ssfn_hdr.quality < 5 && ssfn_hdr.characters_offs[0] < 65536) l = 0; else
if(ssfn_hdr.characters_offs[0] < 1048576) l = 1; else l = 2;
min_x = max_x = lastkern = haskern = 0;
unicode = -1;
for(i=ks=0;i<(int)ssfn_hdr.characters_num;i++) {
for(i=ks=0;i<characters_num;i++) {
j = chars[i].unicode - unicode - 1;
chr = (unsigned char*)realloc(chr, cs+256+chars[i].len*10);
if(!chr) { fprintf(stderr,"memory allocation error\n"); return 2; }
......@@ -908,13 +929,13 @@ int main(int argc, char **argv)
}
if(chars[i].frags) free(chars[i].frags);
/* add to kerning */
j = i*100/(int)ssfn_hdr.characters_num;
j = i*100/characters_num;
if(j/5!=ks/5) {
if(!ks) printf("\r ");
printf("\r 1 2 (3) Kerning... %3d%%", j); fflush(stdout); ks=j;
}
if(chars[i].unicode>32)
for(j=0;j<(int)ssfn_hdr.characters_num;j++) {
for(j=0;j<characters_num;j++) {
if(chars[j].unicode<33) continue;
FT_Get_Kerning(face, chars[i].unicode, chars[j].unicode, 0, &v);
v.x = (((v.x*(gridsize-1)+(max_s/2))/max_s)+63)/64;
......@@ -968,13 +989,13 @@ int main(int argc, char **argv)
ssfn_hdr.size += cs;
printf("\rFragments: %d, Characters: %d, bbox: (%d,%d) (%d,%d), baseline: %d, underline: %d\n",
ssfn_hdr.fragments_num, ssfn_hdr.characters_num, ssfn_hdr.bbox_left,ssfn_hdr.bbox_top,
fragments_num, characters_num, ssfn_hdr.bbox_left,ssfn_hdr.bbox_top,
ssfn_hdr.bbox_right,ssfn_hdr.bbox_bottom, ssfn_hdr.baseline, ssfn_hdr.underline);
/* serialize kerning information */
if(haskern) {
ssfn_hdr.kerning_offs = ssfn_hdr.characters_offs + cs;
for(kgrpnum=i=0;i<(int)ssfn_hdr.characters_num;i++) {
ssfn_hdr.kerning_offs = ssfn_hdr.characters_offs[0] + cs;
for(kgrpnum=i=0;i<characters_num;i++) {
if(chars[i].klen) chars[i].kgrp = kgrp_add(chars[i].klen, chars[i].kern);
}
printf("Kerning: %d pairs, %d groups\n", haskern, kgrpnum);
......@@ -1022,7 +1043,7 @@ int main(int argc, char **argv)
}
}
unicode = -1;
for(ks=k=i=0;i<(int)ssfn_hdr.characters_num;i++) {
for(ks=k=i=0;i<characters_num;i++) {
if(chars[i].kgrp == -1) continue;
j = chars[i].unicode - unicode - 1;
while(j > 0) {
......@@ -1032,7 +1053,7 @@ int main(int argc, char **argv)
if(j > 64) { ks += 2; break; }
}
}
for(j=0;j<(int)ssfn_hdr.characters_num-i && j<128 &&
for(j=0;j<characters_num-i && j<128 &&
chars[i].unicode+j == chars[i+j].unicode && chars[i].kgrp == chars[i+j].kgrp;j++);
j--;
unicode = chars[i].unicode + j;
......@@ -1053,7 +1074,7 @@ int main(int argc, char **argv)
} else { k *= 3; k += ks; }
unicode = -1;
for(ks=i=0;i<(int)ssfn_hdr.characters_num;i++) {
for(ks=i=0;i<characters_num;i++) {
if(chars[i].kgrp == -1) continue;
j = chars[i].unicode - unicode - 1;
kern = (unsigned char*)realloc(kern, ks+256+2);
......@@ -1075,7 +1096,7 @@ int main(int argc, char **argv)
}
}
}
for(j=0;j<(int)ssfn_hdr.characters_num-i && j<128 &&
for(j=0;j<characters_num-i && j<128 &&
chars[i].unicode+j == chars[i+j].unicode && chars[i].kgrp == chars[i+j].kgrp;j++);
j--;
kern[ks++] = j & 0x7F;
......
......@@ -6,13 +6,14 @@ A little application to read and modify [SSFN](https://gitlab.com/bztsrc/scalabl
Read the user manual on this tool in the [documentation](https://gitlab.com/bztsrc/scalable-font/blob/master/docs/editor.md).
Dependencies
------------
Compilation Dependencies
------------------------
Uses SDL by default, so it should be easy to port. If SDL not found, it fallbacks to X11.
It autodetects zlib too. If found, then it can read and write gzip compressed files. If not,
then decompression is solved by a built-in tiny inflate library, but no compression support
on writes. Other than those, requires libc only.