diff options
Diffstat (limited to 'symbols/symbols-tool.c')
| -rw-r--r-- | symbols/symbols-tool.c | 104 |
1 files changed, 70 insertions, 34 deletions
diff --git a/symbols/symbols-tool.c b/symbols/symbols-tool.c index b003978..b705d84 100644 --- a/symbols/symbols-tool.c +++ b/symbols/symbols-tool.c @@ -1,7 +1,7 @@ #define _POSIX_C_SOURCE 200809L /* vega-symbols: build symbol files from fonts and SVG icons, and measure them * against FreeType (docs/design/002-symbol-format.md). - * vega-symbols build [--font FONT.ttf --codes 0x20-0x7e,...] [--svg CODE=ICON.svg ...] [--units N] -o OUT.vsym + * vega-symbols build [--font FONT.ttf [--face N] --codes 0x20-0x7e,...]... [--svg CODE=ICON.svg ...] [--units N] -o OUT.vsym * vega-symbols compare FONT.ttf OUT.vsym --sizes 12,16,32,64 * vega-symbols info OUT.vsym * vega-symbols render OUT.vsym CODE PIXELS OUT.pgm @@ -407,7 +407,9 @@ static int by_code(const void *a, const void *b) { return (x > y) - (x < y); } -static void write_symbols(const char *out, uint16_t units, Built *built, size_t count, const Path *path) { +/* line: a font's ascent and descent in the build's units (0 and 0 without a font). */ +static void write_symbols(const char *out, uint16_t units, const long line[2], Built *built, size_t count, const Path *path) { + long ascent = line[0], descent = line[1]; qsort(built, count, sizeof(*built), by_code); for (size_t i = 1; i < count; ++i) if (built[i].code == built[i - 1].code) fail("a code is given twice"); Points points = {0}; @@ -422,16 +424,21 @@ static void write_symbols(const char *out, uint16_t units, Built *built, size_t bool even = units % 2 == 0 && units > 16; for (size_t i = 0; even && i < points.count; ++i) even = points.items[i].x % 2 == 0 && points.items[i].y % 2 == 0; for (size_t i = 0; even && i < count; ++i) even = built[i].advance % 2 == 0; + even = even && ascent % 2 == 0 && descent % 2 == 0; if (!even) break; for (size_t i = 0; i < points.count; ++i) { points.items[i].x /= 2; points.items[i].y /= 2; } for (size_t i = 0; i < count; ++i) built[i].advance = (int16_t)(built[i].advance / 2); + ascent /= 2, descent /= 2; units /= 2; } + if (ascent > INT16_MAX || descent < INT16_MIN) fail("the font's line does not fit 16 bits"); size_t size = VEGA_SYMBOLS_HEADER + count * VEGA_SYMBOL_BYTES + points.count * VEGA_POINT_BYTES; unsigned char *bytes = calloc(1, size); if (!bytes) fail("out of memory"); - memcpy(bytes, "VEGASYM2", 8); + memcpy(bytes, "VEGASYM3", 8); put16(bytes + 8, units); + put16(bytes + 24, (int)ascent); + put16(bytes + 26, (int)descent); put32(bytes + 12, (uint32_t)count); put32(bytes + 16, (uint32_t)points.count); put32(bytes + 20, (uint32_t)size); @@ -506,48 +513,68 @@ static double seconds(void) { return (double)t.tv_sec + (double)t.tv_nsec / 1e9; } -#define BUILD_USAGE "usage: vega-symbols build [--font FONT.ttf --codes 0x20-0x7e,...] [--svg CODE=ICON.svg ...] [--units N] -o OUT.vsym" +#define BUILD_USAGE \ + "usage: vega-symbols build [--font FONT.ttf [--face N] --codes 0x20-0x7e,...]... [--svg CODE=ICON.svg ...] [--units N] -o OUT.vsym" + +/* Fonts in order, each with the codes to take from it: a code an earlier + * font gave is not taken again, so later fonts fill what earlier ones lack + * (a monospace Latin font, then a CJK one for Hangul). */ +typedef struct { const char *path, *codes; long face; } Font; static int build(int argc, char **argv) { - const char *font = NULL, *out = NULL, *codes = NULL, **icons = calloc((size_t)argc, sizeof(*icons)); - size_t icon_count = 0; + const char *out = NULL, **icons = calloc((size_t)argc, sizeof(*icons)); + Font *fonts = calloc((size_t)argc, sizeof(*fonts)); + size_t icon_count = 0, font_count = 0; long units = 0; for (int i = 2; i < argc; i += 2) { if (i + 1 == argc) fail(BUILD_USAGE); if (!strcmp(argv[i], "-o")) out = argv[i + 1]; - else if (!strcmp(argv[i], "--font")) font = argv[i + 1]; - else if (!strcmp(argv[i], "--codes")) codes = argv[i + 1]; + else if (!strcmp(argv[i], "--font")) fonts[font_count++] = (Font){argv[i + 1], NULL, 0}; + else if (!strcmp(argv[i], "--face") && font_count) fonts[font_count - 1].face = strtol(argv[i + 1], NULL, 10); /* in a .ttc */ + else if (!strcmp(argv[i], "--codes") && font_count) fonts[font_count - 1].codes = argv[i + 1]; else if (!strcmp(argv[i], "--units")) units = strtol(argv[i + 1], NULL, 10); else if (!strcmp(argv[i], "--svg")) icons[icon_count++] = argv[i + 1]; else fail(BUILD_USAGE); } - if (!out || !font != !codes) fail(BUILD_USAGE); + if (!out) fail(BUILD_USAGE); + for (size_t f = 0; f < font_count; ++f) if (!fonts[f].codes) fail(BUILD_USAGE); FT_Library library = NULL; - FT_Face face = NULL; - if (font && (FT_Init_FreeType(&library) || FT_New_Face(library, font, 0, &face))) fail("cannot open the font"); - /* Twice a font's units by default: TrueType's implied points, halfway - * between two control points, then land on whole units. (FreeType finds - * them by integer halving, so outlines are doubled before it walks them.) */ - if (!units) units = face ? 2 * face->units_per_EM : 4096; + FT_Face *faces = calloc(font_count + 1, sizeof(*faces)); + if (font_count && FT_Init_FreeType(&library)) fail("cannot start FreeType"); + for (size_t f = 0; f < font_count; ++f) + if (FT_New_Face(library, fonts[f].path, fonts[f].face, &faces[f])) fail("cannot open a font"); + /* Twice the first font's units by default: TrueType's implied points, + * halfway between two control points, then land on whole units. (FreeType + * finds them by integer halving, so outlines are doubled before it walks + * them.) Other fonts are scaled into the same units. */ + if (!units) units = font_count ? 2 * faces[0]->units_per_EM : 4096; if (units < 16 || units > 16384) fail("--units: 16 to 16384"); - uint32_t *list = NULL; - size_t count = codes ? codes_of(codes, &list) : 0; - Built *built = calloc(count + icon_count + 1, sizeof(*built)); - Path path = {.font_scale = face ? (double)units / (2.0 * face->units_per_EM) : 1}; + size_t capacity = icon_count + 1; + uint32_t **lists = calloc(font_count + 1, sizeof(*lists)); + size_t *counts = calloc(font_count + 1, sizeof(*counts)); + for (size_t f = 0; f < font_count; ++f) capacity += counts[f] = codes_of(fonts[f].codes, &lists[f]); + Built *built = calloc(capacity, sizeof(*built)); + Path path = {.font_scale = 1}; size_t made = 0; FT_Outline_Funcs walk = {ft_move, ft_line, ft_conic, ft_cubic, 0, 0}; - for (size_t i = 0; i < count; ++i) { - FT_UInt glyph = FT_Get_Char_Index(face, list[i]); - if (!glyph || FT_Load_Glyph(face, glyph, FT_LOAD_NO_SCALE)) continue; /* the font lacks it */ - size_t first = path.count; - path.open = false; - if (face->glyph->format == FT_GLYPH_FORMAT_OUTLINE) { - FT_Outline_Transform(&face->glyph->outline, &(FT_Matrix){0x20000, 0, 0, 0x20000}); - if (FT_Outline_Decompose(&face->glyph->outline, &walk, &path)) fail("cannot walk an outline"); + for (size_t f = 0; f < font_count; ++f) { + FT_Face face = faces[f]; + path.font_scale = (double)units / (2.0 * face->units_per_EM); + for (size_t i = 0; i < counts[f]; ++i) { + bool given = false; + for (size_t k = 0; k < made && !given; ++k) given = built[k].code == lists[f][i]; + FT_UInt glyph = given ? 0 : FT_Get_Char_Index(face, lists[f][i]); + if (!glyph || FT_Load_Glyph(face, glyph, FT_LOAD_NO_SCALE)) continue; /* given, or the font lacks it */ + size_t first = path.count; + path.open = false; + if (face->glyph->format == FT_GLYPH_FORMAT_OUTLINE) { + FT_Outline_Transform(&face->glyph->outline, &(FT_Matrix){0x20000, 0, 0, 0x20000}); + if (FT_Outline_Decompose(&face->glyph->outline, &walk, &path)) fail("cannot walk an outline"); + } + close_path(&path); + long advance = lround((double)face->glyph->metrics.horiAdvance * units / face->units_per_EM); + built[made++] = (Built){lists[f][i], first, path.count - first, (int16_t)(advance > INT16_MAX ? INT16_MAX : advance)}; } - close_path(&path); - long advance = lround((double)face->glyph->metrics.horiAdvance * units / face->units_per_EM); - built[made++] = (Built){list[i], first, path.count - first, (int16_t)(advance > INT16_MAX ? INT16_MAX : advance)}; } for (size_t i = 0; i < icon_count; ++i) { char *equals = strchr(icons[i], '='); @@ -558,11 +585,20 @@ static int build(int argc, char **argv) { svg_icon(&path, equals + 1, (uint16_t)units, &advance); built[made++] = (Built){(uint32_t)strtoul(icons[i], NULL, 0), first, path.count - first, advance}; } - write_symbols(out, (uint16_t)units, built, made, &path); - if (face) FT_Done_Face(face); + /* The first font's line as FreeType reports it (hhea or typo metrics). */ + long line[2] = {font_count ? lround((double)faces[0]->ascender * units / faces[0]->units_per_EM) : 0, + font_count ? lround((double)faces[0]->descender * units / faces[0]->units_per_EM) : 0}; + write_symbols(out, (uint16_t)units, line, built, made, &path); + for (size_t f = 0; f < font_count; ++f) { + FT_Done_Face(faces[f]); + free(lists[f]); + } if (library) FT_Done_FreeType(library); + free(faces); + free(lists); + free(counts); + free(fonts); free(icons); - free(list); free(built); free(path.items); return 0; @@ -698,7 +734,7 @@ static int info(int argc, char **argv) { if (argc < 3) fail("usage: vega-symbols info SYMBOLS.vsym"); char *bytes; VegaSymbols s = open_symbols(argv[2], &bytes); - printf("{\"units\":%u,\"symbols\":[", s.units); + printf("{\"units\":%u,\"ascent\":%d,\"descent\":%d,\"symbols\":[", s.units, s.ascent, s.descent); for (uint32_t i = 0; i < s.symbols; ++i) { VegaSymbol symbol = vega_symbol_at(&s, i); double area = 0; |
