diff options
34 files changed, 1840 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9e13a6f --- /dev/null +++ b/.gitignore @@ -0,0 +1,15 @@ +*\#* +*~ +*.o +*.a +*.lo +*.su +*.so +*.so.* +*.dll +*.dylib +*.gch +*.gcov +*.gcno +*.gcda +/demo @@ -0,0 +1,15 @@ +ISC License + +© 2021 Mattias Andrée <maandree@kth.se> + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..0c406ea --- /dev/null +++ b/Makefile @@ -0,0 +1,94 @@ +.POSIX: + +CONFIGFILE = config.mk +include $(CONFIGFILE) + +OS = linux +# Linux: linux +# Mac OS: macos +# Windows: windows +include mk/$(OS).mk + + +LIB_MAJOR = 1 +LIB_MINOR = 0 +LIB_VERSION = $(LIB_MAJOR).$(LIB_MINOR) +LIB_NAME = parsepcf + + +OBJ =\ + libparsepcf_destroy_preparsed_font.o\ + libparsepcf_get_accelerators.o\ + libparsepcf_get_bitmap_offsets.o\ + libparsepcf_get_bitmaps.o\ + libparsepcf_get_encoding.o\ + libparsepcf_get_glyph_indices.o\ + libparsepcf_get_glyph_name_subtable.o\ + libparsepcf_get_glyph_names.o\ + libparsepcf_get_metrics.o\ + libparsepcf_get_metrics_count.o\ + libparsepcf_get_properties.o\ + libparsepcf_get_property_subtable.o\ + libparsepcf_get_swidth_count.o\ + libparsepcf_get_swidths.o\ + libparsepcf_get_table_count.o\ + libparsepcf_get_tables.o\ + libparsepcf_parse_int16_from_unsigned__.o\ + libparsepcf_parse_int32_from_unsigned__.o\ + libparsepcf_parse_lsb_uint16__.o\ + libparsepcf_parse_lsb_uint32__.o\ + libparsepcf_parse_msb_uint16__.o\ + libparsepcf_parse_msb_uint32__.o\ + libparsepcf_preparse_font.o + +HDR =\ + libparsepcf.h\ + common.h + +LOBJ = $(OBJ:.o=.lo) + + +all: libparsepcf.a libparsepcf.$(LIBEXT) demo +$(OBJ): $(HDR) +$(LOBJ): $(HDR) + +demo: demo.o libparsepcf.a + $(CC) -o $@ demo.o libparsepcf.a $(LDFLAGS) + +.c.o: + $(CC) -c -o $@ $< $(CFLAGS) $(CPPFLAGS) + +.c.lo: + $(CC) -fPIC -c -o $@ $< $(CFLAGS) $(CPPFLAGS) + +libparsepcf.a: $(OBJ) + @rm -f -- $@ + $(AR) rc $@ $(OBJ) + +libparsepcf.$(LIBEXT): $(LOBJ) + $(CC) $(LIBFLAGS) -o $@ $(LOBJ) $(LDFLAGS) + +install: libparsepcf.a libparsepcf.$(LIBEXT) + mkdir -p -- "$(DESTDIR)$(PREFIX)/lib" + mkdir -p -- "$(DESTDIR)$(PREFIX)/include" + cp -- libparsepcf.a "$(DESTDIR)$(PREFIX)/lib/" + cp -- libparsepcf.$(LIBEXT) "$(DESTDIR)$(PREFIX)/lib/libparsepcf.$(LIBMINOREXT)" + ln -sf -- libparsepcf.$(LIBMINOREXT) "$(DESTDIR)$(PREFIX)/lib/libparsepcf.$(LIBMAJOREXT)" + ln -sf -- libparsepcf.$(LIBMAJOREXT) "$(DESTDIR)$(PREFIX)/lib/libparsepcf.$(LIBEXT)" + cp -- libparsepcf.h "$(DESTDIR)$(PREFIX)/include/" + +uninstall: + -rm -f -- "$(DESTDIR)$(PREFIX)/lib/libparsepcf.a" + -rm -f -- "$(DESTDIR)$(PREFIX)/lib/libparsepcf.$(LIBMAJOREXT)" + -rm -f -- "$(DESTDIR)$(PREFIX)/lib/libparsepcf.$(LIBMINOREXT)" + -rm -f -- "$(DESTDIR)$(PREFIX)/lib/libparsepcf.$(LIBEXT)" + -rm -f -- "$(DESTDIR)$(PREFIX)/include/libparsepcf.h" + +clean: + -rm -f -- *.o *.a *.lo *.su *.so *.so.* *.dll *.dylib + -rm -f -- *.gch *.gcov *.gcno *.gcda *.$(LIBEXT) demo + +.SUFFIXES: +.SUFFIXES: .lo .o .c + +.PHONY: all install uninstall clean diff --git a/common.h b/common.h new file mode 100644 index 0000000..f81a098 --- /dev/null +++ b/common.h @@ -0,0 +1,54 @@ +/* See LICENSE file for copyright and license details. */ +#include "libparsepcf.h" + +#include <errno.h> +#include <stdlib.h> +#include <string.h> + + +#if defined(__GNUC__) +# define PURE __attribute__((__pure__)) +# define CONST __attribute__((__const__)) +#else +# define PURE +# define CONST +#endif + + +/* Table formats: */ +#define LIBPARSEPCF_DEFAULT_FORMAT UINT32_C(0x00000000) +#define LIBPARSEPCF_INKBOUNDS UINT32_C(0x00000200) +#define LIBPARSEPCF_ACCELERATOR_WITH_INK_BOUNDS UINT32_C(0x00000100) +#define LIBPARSEPCF_COMPRESSED_METRICS UINT32_C(0x00000100) + +/* Table format modifiers: */ +#define LIBPARSEPCF_GLYPH_PAD_MASK (UINT32_C(3) << 0) +#define LIBPARSEPCF_BYTE (UINT32_C(1) << 2) +#define LIBPARSEPCF_BIT (UINT32_C(1) << 3) +#define LIBPARSEPCF_SCAN_UNIT_MASK (UINT32_C(3) << 4) + + +PURE uint32_t libparsepcf_parse_lsb_uint32__(const void *); +PURE uint32_t libparsepcf_parse_msb_uint32__(const void *); + +PURE uint16_t libparsepcf_parse_lsb_uint16__(const void *); +PURE uint16_t libparsepcf_parse_msb_uint16__(const void *); + +CONST int16_t libparsepcf_parse_int16_from_unsigned__(uint16_t); +CONST int32_t libparsepcf_parse_int32_from_unsigned__(uint32_t); + +#define PARSE_UINT32(TEXT, MSB)\ + ((MSB) ? libparsepcf_parse_msb_uint32__(TEXT) : libparsepcf_parse_lsb_uint32__(TEXT)) + +#define PARSE_UINT16(TEXT, MSB)\ + ((MSB) ? libparsepcf_parse_msb_uint16__(TEXT) : libparsepcf_parse_lsb_uint16__(TEXT)) + +#define PARSE_INT32(TEXT, MSB)\ + (libparsepcf_parse_int32_from_unsigned__(PARSE_UINT32(TEXT, MSB))) + +#define PARSE_INT16(TEXT, MSB)\ + (libparsepcf_parse_int16_from_unsigned__(PARSE_UINT16(TEXT, MSB))) + + +#undef PURE +#undef CONST diff --git a/config.mk b/config.mk new file mode 100644 index 0000000..9b16e1d --- /dev/null +++ b/config.mk @@ -0,0 +1,8 @@ +PREFIX = /usr +MANPREFIX = $(PREFIX)/share/man + +CC = cc + +CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_XOPEN_SOURCE=700 -D_GNU_SOURCE +CFLAGS = -std=c99 -Wall -g +LDFLAGS = @@ -0,0 +1,580 @@ +#include "libparsepcf.h" +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + + +static char *file = NULL; +static size_t len = 0; + +static size_t table_n; +static size_t table_i; +static struct libparsepcf_table *tables; + +static struct libparsepcf_font font; + + +static void +load_file(void) +{ + size_t size = 0; + ssize_t r; + + if (isatty(STDIN_FILENO)) { + fprintf(stderr, "An uncompressed PCF file is required as standard input\n"); + exit(1); + } + + for (;;) { + if (size == len) { + file = realloc(file, size += 8096); + if (!file) { + perror("realloc"); + exit(1); + } + } + r = read(STDIN_FILENO, &file[len], size - len); + if (r <= 0) { + if (!r) + break; + perror("read"); + exit(1); + } + len += (size_t)r; + } +} + + +static void +print_properties(void) +{ + size_t prop_i; + struct libparsepcf_properties props; + struct libparsepcf_property_subtable *proptab; + + if (libparsepcf_get_properties(file, len, &tables[table_i], &props)) { + perror("libparsepcf_get_properties"); + exit(1); + } + printf(" PROPERTIES:\n"); + printf("\tnprops: %zu\n", props.property_count); + printf("\tstrlen: %zu\n", props.strings_size); + if (!props.property_count) + return; + proptab = calloc(props.property_count, sizeof(*proptab)); + if (!proptab) { + perror("calloc"); + exit(1); + } + if (libparsepcf_get_property_subtable(file, len, &tables[table_i], &props, + proptab, 0, props.property_count)) { + perror("libparsepcf_get_property_subtable"); + exit(1); + } + for (prop_i = 0; prop_i < props.property_count; prop_i++) { + printf("\t #%zu\n", prop_i); + printf("\t\tname: %s\n", proptab[prop_i].name); + if (proptab[prop_i].is_string_property) + printf("\t\tvalue: \"%s\"\n", proptab[prop_i].value.string_value); + else + printf("\t\tvalue: %i\n", proptab[prop_i].value.signed_value); + } + free(proptab); +} + + +static void +print_metrics(const char *header) +{ + struct libparsepcf_metrics *metrics; + size_t mtx_i, mtx_n; + + if (libparsepcf_get_metrics_count(file, len, &tables[table_i], &mtx_n)) { + perror("libparsepcf_get_metrics_count"); + exit(1); + } + metrics = calloc(mtx_n, sizeof(*metrics)); + if (!metrics) { + perror("calloc"); + exit(1); + } + if (libparsepcf_get_metrics(file, len, &tables[table_i], metrics, 0, mtx_n)) { + perror("libparsepcf_get_metrics"); + exit(1); + } + printf(" %s:\n", header); + printf("\tcount: %zu\n", mtx_n); + for (mtx_i = 0; mtx_i < mtx_n; mtx_i++) { + printf("\t#%zu: lsb=%i, rsb=%i, width=%i, ascent=%i, descent=%i, attributes=%x\n", + mtx_i, metrics[mtx_i].left_side_bearing, metrics[mtx_i].right_side_bearing, + metrics[mtx_i].character_width, metrics[mtx_i].character_ascent, + metrics[mtx_i].character_descent, metrics[mtx_i].character_attributes); + } + free(metrics); +} + + +static void +print_glyph_names(void) +{ + size_t name_i; + struct libparsepcf_glyph_names names; + const char **nametab; + + if (libparsepcf_get_glyph_names(file, len, &tables[table_i], &names)) { + perror("libparsepcf_get_glyph_names"); + exit(1); + } + printf(" GLYPH NAMES:\n"); + printf("\tnglyph: %zu\n", names.glyph_count); + printf("\tstrlen: %zu\n", names.strings_size); + if (!names.glyph_count) + return; + nametab = calloc(names.glyph_count, sizeof(*nametab)); + if (!nametab) { + perror("calloc"); + exit(1); + } + if (libparsepcf_get_glyph_name_subtable(file, len, &tables[table_i], &names, + nametab, 0, names.glyph_count)) { + perror("libparsepcf_get_glyph_name_subtable"); + exit(1); + } + for (name_i = 0; name_i < names.glyph_count; name_i++) + printf("\t #%zu: %s\n", name_i, nametab[name_i]); + free(nametab); +} + + +static void +print_bitmaps(void) +{ + size_t bitmap_i; + struct libparsepcf_bitmaps bitmaps; + size_t *bitmaptab; + + if (libparsepcf_get_bitmaps(file, len, &tables[table_i], &bitmaps)) { + perror("libparsepcf_get_bitmaps"); + exit(1); + } + printf(" BITMAPS:\n"); + printf("\tnglyph: %zu\n", bitmaps.glyph_count); + printf("\tsize: %zu\n", bitmaps.bitmap_size); + printf("\tpacking: %zu\n", bitmaps.bit_packing); + printf("\tpadding: %zu\n", bitmaps.row_padding); + printf("\tlsbyte: %i\n", bitmaps.lsbyte); + printf("\tlsbit: %i\n", bitmaps.lsbit); + if (!bitmaps.glyph_count) + return; + bitmaptab = calloc(bitmaps.glyph_count, sizeof(*bitmaptab)); + if (!bitmaptab) { + perror("calloc"); + exit(1); + } + if (libparsepcf_get_bitmap_offsets(file, len, &tables[table_i], &bitmaps, + bitmaptab, 0, bitmaps.glyph_count)) { + perror("libparsepcf_get_bitmap_offsets"); + exit(1); + } + for (bitmap_i = 0; bitmap_i < bitmaps.glyph_count; bitmap_i++) { + printf("\t #%zu: %zu -> %p (maxsize=%zu)\n", + bitmap_i, bitmaptab[bitmap_i], + (const void *)&bitmaps.bitmap_data[bitmaptab[bitmap_i]], + bitmaps.bitmap_size - bitmaptab[bitmap_i]); + } + free(bitmaptab); +} + + +static void +print_bdf_encodings(void) +{ + struct libparsepcf_encoding encoding; + size_t *indices; + size_t index_i; + + if (libparsepcf_get_encoding(file, len, &tables[table_i], &encoding)) { + perror("libparsepcf_get_encoding:"); + exit(1); + } + printf(" BDF ENCODINGS:\n"); + printf("\trange2: [%u, %u]\n", encoding.min_byte2, encoding.max_byte2); + printf("\trange1: [%u, %u]\n", encoding.min_byte1, encoding.max_byte1); + printf("\tdefault: %u\n", encoding.default_char); + printf("\tnglyph: %zu\n", encoding.glyph_count); + if (!encoding.glyph_count) + return; + indices = calloc(encoding.glyph_count, sizeof(*indices)); + if (libparsepcf_get_glyph_indices(file, len, &tables[table_i], &encoding, + indices, 0, encoding.glyph_count)) { + perror("libparsepcf_get_glyph_indices"); + exit(1); + } + for (index_i = 0; index_i < encoding.glyph_count; index_i++) { + if (indices[index_i] == LIBPARSEPCF_NOT_ENCODED) + printf("\t#%#zx: not encoded\n", index_i); + else + printf("\t#%#zx: %zu\n", index_i, indices[index_i]); + } + free(indices); +} + + +static void +print_accelerators(const char *header) +{ + struct libparsepcf_accelerators accel; + + if (libparsepcf_get_accelerators(file, len, &tables[table_i], &accel)) { + perror("libparsepcf_get_accelerators:"); + exit(1); + } + printf(" %s:\n", header); + printf("\tno overlap: %i\n", accel.no_overlap); + printf("\tconstant metrics: %i\n", accel.constant_metrics); + printf("\tterminal font: %i\n", accel.terminal_font); + printf("\tconstant width: %i\n", accel.constant_width); + printf("\tink inside: %i\n", accel.ink_inside); + printf("\tink metrics: %i\n", accel.ink_metrics); + printf("\tdraw rtl: %i\n", accel.draw_rtl); + printf("\thave ink bounds: %i\n", accel.have_ink_bounds); + printf("\tfont ascent: %i\n", accel.font_ascent); + printf("\tfont descent: %i\n", accel.font_descent); + printf("\tmax overlap: %i\n", accel.max_overlap); + printf("\tmin bounds: lsb=%i, rsb=%i, width=%i, ascent=%i, descent=%i, attributes=%x\n", + accel.min_bounds.left_side_bearing, accel.min_bounds.right_side_bearing, + accel.min_bounds.character_width, accel.min_bounds.character_ascent, + accel.min_bounds.character_descent, accel.min_bounds.character_attributes); + printf("\tmax bounds: lsb=%i, rsb=%i, width=%i, ascent=%i, descent=%i, attributes=%x\n", + accel.max_bounds.left_side_bearing, accel.max_bounds.right_side_bearing, + accel.max_bounds.character_width, accel.max_bounds.character_ascent, + accel.max_bounds.character_descent, accel.max_bounds.character_attributes); + if (accel.have_ink_bounds) { + printf("\tmin ink bounds: lsb=%i, rsb=%i, width=%i, ascent=%i, descent=%i, attributes=%x\n", + accel.min_ink_bounds.left_side_bearing, accel.min_ink_bounds.right_side_bearing, + accel.min_ink_bounds.character_width, accel.min_ink_bounds.character_ascent, + accel.min_ink_bounds.character_descent, accel.min_ink_bounds.character_attributes); + printf("\tmax ink bounds: lsb=%i, rsb=%i, width=%i, ascent=%i, descent=%i, attributes=%x\n", + accel.max_ink_bounds.left_side_bearing, accel.max_ink_bounds.right_side_bearing, + accel.max_ink_bounds.character_width, accel.max_ink_bounds.character_ascent, + accel.max_ink_bounds.character_descent, accel.max_ink_bounds.character_attributes); + } +} + + +static void +print_swidths(void) +{ + size_t sw_i, sw_n; + int32_t *swidths; + + if (libparsepcf_get_swidth_count(file, len, &tables[table_i], &sw_n)) { + perror("libparsepcf_get_swidth_count"); + exit(1); + } + printf(" SWIDTHS:\n"); + printf("\tnglyph: %zu\n", sw_n); + if (!sw_n) + return; + swidths = calloc(sw_n, sizeof(*swidths)); + if (libparsepcf_get_swidths(file, len, &tables[table_i], swidths, 0, sw_n)) { + perror("libparsepcf_get_swidths"); + exit(1); + } + for (sw_i = 0; sw_i < sw_n; sw_i++) + printf("\t#%zu: %i milli-ems\n", sw_i, swidths[sw_i]); +} + + +static void +print_font(void) +{ + if (libparsepcf_get_table_count(file, len, &table_n)) { + perror("libparsepcf_get_table_count"); + exit(1); + } + fprintf(stderr, "size: %zu\n", len); + fprintf(stderr, "ntables: %zu\n", table_n); + + tables = calloc(table_n, sizeof(*tables)); + if (!tables) { + perror("calloc"); + exit(1); + } + if (libparsepcf_get_tables(file, len, tables, 0, table_n)) { + perror("libparsepcf_get_tables"); + exit(1); + } + + for (table_i = 0; table_i < table_n; table_i++) { + printf("table #%zu: type=%#x, format=%#x, offset=%u, size=%u, (end=%u)\n", + table_i, tables[table_i].type, tables[table_i].format, + tables[table_i].offset, tables[table_i].size, + tables[table_i].offset + tables[table_i].size); + + switch (tables[table_i].type) { +#if 1 + case LIBPARSEPCF_PROPERTIES: /* X font atoms */ + print_properties(); + break; +#endif +#if 0 + case LIBPARSEPCF_METRICS: /* bitmap size */ + print_metrics("METRICS"); + break; +#endif +#if 0 + case LIBPARSEPCF_INK_METRICS: /* minimium bounding box metrics */ + print_metrics("INK METRICS"); + break; +#endif +#if 0 + case LIBPARSEPCF_GLYPH_NAMES: /* PostScript names */ + print_glyph_names(); + break; +#endif +#if 0 + case LIBPARSEPCF_BITMAPS: + print_bitmaps(); + break; +#endif +#if 0 + case LIBPARSEPCF_BDF_ENCODINGS: + print_bdf_encodings(); + break; +#endif +#if 1 + case LIBPARSEPCF_ACCELERATORS: + print_accelerators("ACCELERATORS"); + break; +#endif +#if 1 + case LIBPARSEPCF_BDF_ACCELERATORS: /* prefered over LIBPARSEPCF_ACCELERATORS */ + print_accelerators("BDF ACCELERATORS"); + break; +#endif +#if 0 + case LIBPARSEPCF_SWIDTHS: + print_swidths(); + break; +#endif + default: + (void) print_properties; + (void) print_metrics; + (void) print_glyph_names; + (void) print_bitmaps; + (void) print_bdf_encodings; + (void) print_accelerators; + (void) print_swidths; + break; + } + } + + free(tables); +} + + +static void +print_glyph_info(size_t glyph, const struct libparsepcf_metrics *mtx) +{ + struct libparsepcf_metrics inkmtx; + const char *name; + int32_t s32; + + if (font.name_table) { + if (libparsepcf_get_glyph_name_subtable(file, len, font.name_table, &font.names, &name, glyph, 1)) { + perror("libparsepcf_get_glyph_name_subtable"); + exit(1); + } + printf("glyph name: %s\n", name); + } + if (font.swidth_table) { + if (libparsepcf_get_swidths(file, len, font.swidth_table, &s32, glyph, 1)) { + perror("libparsepcf_get_swidths"); + exit(1); + } + printf("scalable width: %i milli-ems\n", s32); + } + printf("metrics:\n"); + printf(" left side bearing: %i\n", mtx->left_side_bearing); + printf(" right side bearing: %i\n", mtx->right_side_bearing); + printf(" character width: %i\n", mtx->character_width); + printf(" character ascent: %i\n", mtx->character_ascent); + printf(" character descent: %i\n", mtx->character_descent); + printf(" character attributes: %u\n", mtx->character_attributes); + if (font.inkmtx_table) { + if (libparsepcf_get_metrics(file, len, font.inkmtx_table, &inkmtx, glyph, 1)) { + perror("libparsepcf_get_metrics"); + exit(1); + } + printf("ink metrics:\n"); + printf(" left side bearing: %i\n", inkmtx.left_side_bearing); + printf(" right side bearing: %i\n", inkmtx.right_side_bearing); + printf(" character width: %i\n", inkmtx.character_width); + printf(" character ascent: %i\n", inkmtx.character_ascent); + printf(" character descent: %i\n", inkmtx.character_descent); + printf(" character attributes: %u\n", inkmtx.character_attributes); + } + if (font.accel_table) { + printf("font ascent: %i\n", font.accels.font_ascent); + printf("font descent: %i\n", font.accels.font_descent); + } +} + + +static void +print_glyph(size_t glyph) +{ + struct libparsepcf_metrics mtx; + size_t width, height, bitmap_size, bitmap_offset; + size_t row_size, padding, y, x, bit, byte, packing; + int32_t font_ascent, font_descent; + size_t extra_left, extra_right, print_width; + const uint8_t *bitmap; + const char *pixel; + + if (glyph >= font.glyph_count) { + fprintf(stderr, "specified glyph does not exist\n"); + exit(1); + } + if (libparsepcf_get_metrics(file, len, font.mtx_table, &mtx, glyph, 1)) { + perror("libparsepcf_get_metrics"); + exit(1); + } + padding = font.bitmaps.row_padding - 1; + packing = font.bitmaps.bit_packing - 1; + width = (size_t)((int32_t)mtx.right_side_bearing - (int32_t)mtx.left_side_bearing); + height = (size_t)((int32_t)mtx.character_ascent + (int32_t)mtx.character_descent); + row_size = width / 8 + !!(width & 7); + row_size = row_size + ((font.bitmaps.row_padding - (row_size & padding)) & padding); + if (font.accel_table) { + font_ascent = font.accels.font_ascent; + font_descent = font.accels.font_descent; + } else { + font_ascent = mtx.character_ascent; + font_descent = mtx.character_descent; + } + + print_glyph_info(glyph, &mtx); + + if (libparsepcf_get_bitmap_offsets(file, len, font.bitmap_table, &font.bitmaps, &bitmap_offset, glyph, 1)) { + perror("libparsepcf_get_bitmap_offsets"); + exit(1); + } + bitmap_size = font.bitmaps.bitmap_size - bitmap_offset; + bitmap = &font.bitmaps.bitmap_data[bitmap_offset]; + if (height && row_size > bitmap_size / height) { + perror("bitmap is smaller than expected"); + exit(1); + } + printf("\n"); + + font_ascent = 15; + font_descent = 5; + + extra_left = mtx.left_side_bearing > 0 ? (size_t)mtx.left_side_bearing : 0; + extra_right = mtx.character_width > mtx.right_side_bearing ? (size_t)(mtx.character_width - mtx.right_side_bearing) : 0; + print_width = extra_left + width + extra_right; + + /* If the font's ascent is greater than the character's, print the excees in black */ + if (font_ascent > mtx.character_ascent) { + for (y = 0; y < (size_t)(font_ascent - mtx.character_ascent); y++) { + for (x = 0; x < print_width; x++) + printf("\033[1;30m[]"); + printf("\033[0m\n"); + } + } + + for (y = 0; y < height; y++, bitmap += row_size) { + /* Draw baseline */ + if (y + 1 == mtx.character_ascent) + printf("\033[4m"); + + /* If glyph is offset for caret, print offset in black */ + for (x = 0; x < extra_left; x++) + printf("\033[1;30m[]"); + + /* Draw glyph */ + for (x = 0; x < width; x++) { + bit = font.bitmaps.lsbit ? 7 - x % 8 : x % 8; + byte = x / 8; + if (!font.bitmaps.lsbyte) + byte = ((byte & ~packing) | (packing - (byte & packing))); + + /* Use <> to mark dots outside of the glyph box */ + if (mtx.left_side_bearing < 0 && + x < (size_t)-mtx.left_side_bearing) { + pixel = "<>"; + } else if (mtx.right_side_bearing > mtx.character_width && + x >= mtx.character_width + mtx.left_side_bearing) { + pixel = "<>"; + } else if (mtx.character_ascent > font_ascent && + y < (size_t)(mtx.character_ascent - font_ascent)) { + pixel = "<>"; + } else if (mtx.character_descent > font_descent && + y >= (size_t)(mtx.character_ascent + font_descent)) { + pixel = "<>"; + } else { + pixel = "[]"; + } + + printf("\033[%sm%s", ((bitmap[byte] >> bit) & 1) ? "1;37" : "2;37", pixel); + } + + /* If horizontal advance is larger than character width, print extent in black */ + for (x = 0; x < extra_right; x++) + printf("\033[1;30m[]"); + + printf("\033[0m\n"); + } + + /* If the font's descent is greater than the character's, print the excees in black */ + if (font_descent > mtx.character_descent) { + for (y = 0; y < (size_t)(font_descent - mtx.character_descent); y++) { + for (x = 0; x < print_width; x++) + printf("\033[1;30m[]"); + printf("\033[0m\n"); + } + } +} + + +static void +print_line(const char *str, size_t strlen) /* TODO */ +{ +} + + +int +main(int argc, char *argv[]) +{ + size_t glyph; + if (argc) { + argc--; + argv++; + } + load_file(); + if (!argc) { + print_font(); + } else { + if (libparsepcf_preparse_font(file, len, &font)) { + perror("libparsepcf_preparse_font"); + exit(1); + } + if (argc == 2 && !strcmp(argv[0], "-g")) { + glyph = (size_t)strtoul(argv[1], NULL, 0); + print_glyph(glyph); + } else if (argc == 2 && !strcmp(argv[0], "-x")) { + glyph = (size_t)strtoul(argv[1], NULL, 16); + print_glyph(glyph); + } else { + for (; argc--; argv++) + print_line(*argv, strlen(*argv)); + } + libparsepcf_destroy_preparsed_font(&font); + } + free(file); + return 0; +} diff --git a/libparsepcf.h b/libparsepcf.h new file mode 100644 index 0000000..b3731d6 --- /dev/null +++ b/libparsepcf.h @@ -0,0 +1,234 @@ +/* See LICENSE file for copyright and license details. */ +#ifndef LIBPARSEPCF_H +#define LIBPARSEPCF_H + +#include <stddef.h> +#include <stdint.h> + + +/* Based on documentation from FontForge: https://fontforge.org/docs/techref/pcf-format.html */ + + +struct libparsepcf_table { + uint32_t type; + uint32_t format; + uint32_t size; + uint32_t offset; +}; + +/* Table types: */ +#define LIBPARSEPCF_PROPERTIES (UINT32_C(1) << 0) +#define LIBPARSEPCF_ACCELERATORS (UINT32_C(1) << 1) +#define LIBPARSEPCF_METRICS (UINT32_C(1) << 2) +#define LIBPARSEPCF_BITMAPS (UINT32_C(1) << 3) +#define LIBPARSEPCF_INK_METRICS (UINT32_C(1) << 4) +#define LIBPARSEPCF_BDF_ENCODINGS (UINT32_C(1) << 5) +#define LIBPARSEPCF_SWIDTHS (UINT32_C(1) << 6) +#define LIBPARSEPCF_GLYPH_NAMES (UINT32_C(1) << 7) +#define LIBPARSEPCF_BDF_ACCELERATORS (UINT32_C(1) << 8) + +int libparsepcf_get_table_count(const char *, size_t, size_t *countp); +int libparsepcf_get_tables(const char *, size_t, struct libparsepcf_table *tables, size_t first, size_t count); + + + +struct libparsepcf_properties { + size_t property_count; + size_t strings_size; + const char *strings; +}; + +struct libparsepcf_property_subtable { + const char *name; + int is_string_property; + union { + int32_t signed_value; + const char *string_value; + } value; +}; + +int libparsepcf_get_properties(const char *, size_t, + const struct libparsepcf_table *, + struct libparsepcf_properties *); + +int libparsepcf_get_property_subtable(const char *, size_t, + const struct libparsepcf_table *, + const struct libparsepcf_properties *, + struct libparsepcf_property_subtable *, size_t, size_t); + + + +struct libparsepcf_metrics { + int16_t left_side_bearing; + int16_t right_side_bearing; + int16_t character_width; + int16_t character_ascent; + int16_t character_descent; + uint16_t character_attributes; +}; + +int libparsepcf_get_metrics_count(const char *, size_t, const struct libparsepcf_table *, size_t *); + +int libparsepcf_get_metrics(const char *, size_t, + const struct libparsepcf_table *, + struct libparsepcf_metrics *, size_t, size_t); + + + +struct libparsepcf_glyph_names { + size_t glyph_count; + size_t strings_size; + const char *strings; +}; + +int libparsepcf_get_glyph_names(const char *, size_t, + const struct libparsepcf_table *, + struct libparsepcf_glyph_names *); + +int libparsepcf_get_glyph_name_subtable(const char *, size_t, + const struct libparsepcf_table *, + const struct libparsepcf_glyph_names *, + const char **, size_t, size_t); + + + +struct libparsepcf_bitmaps { + size_t glyph_count; + size_t bitmap_size; + size_t bit_packing; + size_t row_padding; + int lsbyte; + int lsbit; + const uint8_t *bitmap_data; +}; + +int libparsepcf_get_bitmaps(const char *, size_t, + const struct libparsepcf_table *, + struct libparsepcf_bitmaps *); + +int libparsepcf_get_bitmap_offsets(const char *, size_t, + const struct libparsepcf_table *, + const struct libparsepcf_bitmaps *, + size_t *, size_t, size_t); + + + +struct libparsepcf_encoding { + uint16_t min_byte2; + uint16_t max_byte2; + uint16_t min_byte1; + uint16_t max_byte1; + uint16_t default_char; + size_t glyph_count; + /* If min_byte1 == 0 and max_byte1 == 0 (single byte encoding): + * glyph_index = glyph_indices[encoding - min_char_or_byte2] + * not included if encoding > max_byte2 or glyph_index == 0xFFFF + * + * Otherwise (dual byte encoding): + * [min_byte1, max_byte1] = allowed range of the more signficant of the 2 bytes (the first byte) + * [min_byte2, max_byte2] = allowed range of the less signficant of the 2 bytes (the second byte) + * e1 = encoding[0] - min_byte1 + * e2 = encoding[1] - min_byte2 + * glyph_index = glyph_indices[e1 * (max_byte2 - min_byte2 + 1) + e2] + * not included if out of range or or glyph_index == 0xFFFF + */ +}; + +#define LIBPARSEPCF_NOT_ENCODED ((size_t)0xFFFFUL) + +int libparsepcf_get_encoding(const char *, size_t, + const struct libparsepcf_table *, + struct libparsepcf_encoding *); + +int libparsepcf_get_glyph_indices(const char *, size_t, + const struct libparsepcf_table *, + const struct libparsepcf_encoding *, + size_t *, size_t, size_t); + + + +struct libparsepcf_accelerators { + /** + * Whether metrics[i].right_side_bearing - metrics[i].character_width + * less than or equal to .min_bounds.left_side_bearing for all i + */ + uint8_t no_overlap : 1; + uint8_t constant_metrics : 1; + /** + * .constant_metrics and, for all characters, left side bearing = 0, + * right side bearing = character width, ascent = .font_ascent, and + * descent = .font_descent + */ + uint8_t terminal_font : 1; + uint8_t constant_width : 1; + /** + * Whether all inked bits are inside the glyph's box + */ + uint8_t ink_inside : 1; + /** + * Whether the ink metrics differ from the metrics for some glyph + */ + uint8_t ink_metrics : 1; + uint8_t draw_rtl : 1; + /** + * If 0, .ink_min_bounds and .ink_max_bounds are just copies of + * .min_bounds and max_bounds + */ + uint8_t have_ink_bounds : 1; + int32_t font_ascent; + int32_t font_descent; + int32_t max_overlap; + struct libparsepcf_metrics min_bounds; + struct libparsepcf_metrics max_bounds; + struct libparsepcf_metrics min_ink_bounds; + struct libparsepcf_metrics max_ink_bounds; +}; + +int libparsepcf_get_accelerators(const char *, size_t, + const struct libparsepcf_table *, + struct libparsepcf_accelerators *); + + + +int libparsepcf_get_swidth_count(const char *, size_t, const struct libparsepcf_table *, size_t *); + +int libparsepcf_get_swidths(const char *, size_t, const struct libparsepcf_table *, int32_t *, size_t, size_t); + + + +struct libparsepcf_font { + const struct libparsepcf_table *prob_table; + struct libparsepcf_properties props; + + const struct libparsepcf_table *accel_table; + struct libparsepcf_accelerators accels; + + const struct libparsepcf_table *mtx_table; + size_t metrics; + + const struct libparsepcf_table *inkmtx_table; + size_t ink_metrics; + + const struct libparsepcf_table *enc_table; + struct libparsepcf_encoding encoding; + + const struct libparsepcf_table *bitmap_table; + struct libparsepcf_bitmaps bitmaps; + + const struct libparsepcf_table *swidth_table; + size_t swidths; + + const struct libparsepcf_table *name_table; + struct libparsepcf_glyph_names names; + + size_t glyph_count; + struct libparsepcf_table *_tables; +}; + +int libparsepcf_preparse_font(const char *, size_t, struct libparsepcf_font *); + +void libparsepcf_destroy_preparsed_font(struct libparsepcf_font *); + + + +#endif diff --git a/libparsepcf_destroy_preparsed_font.c b/libparsepcf_destroy_preparsed_font.c new file mode 100644 index 0000000..36ec493 --- /dev/null +++ b/libparsepcf_destroy_preparsed_font.c @@ -0,0 +1,10 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +void +libparsepcf_destroy_preparsed_font(struct libparsepcf_font *font) +{ + free(font->_tables); + memset(font, 0, sizeof(*font)); +} diff --git a/libparsepcf_get_accelerators.c b/libparsepcf_get_accelerators.c new file mode 100644 index 0000000..af3224a --- /dev/null +++ b/libparsepcf_get_accelerators.c @@ -0,0 +1,83 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + + +int +libparsepcf_get_accelerators(const char *file, size_t size, + const struct libparsepcf_table *table, + struct libparsepcf_accelerators *out) +{ + int msb = table->format & LIBPARSEPCF_BYTE; + int with_ink_bounds = table->format & LIBPARSEPCF_ACCELERATOR_WITH_INK_BOUNDS; + size_t pos; + + (void) size; + + if (table->size < 24 + (with_ink_bounds ? 4 : 2) * 12) + goto ebfont; + + pos = table->offset; + + if (table->format != libparsepcf_parse_lsb_uint32__(&file[pos])) + goto ebfont; + pos += 4; + + out->no_overlap = !!file[pos + 0]; + out->constant_metrics = !!file[pos + 1]; + out->terminal_font = !!file[pos + 2]; + out->constant_width = !!file[pos + 3]; + out->ink_inside = !!file[pos + 4]; + out->ink_metrics = !!file[pos + 5]; + out->draw_rtl = !!file[pos + 6]; + out->have_ink_bounds = !!with_ink_bounds; + pos += 8; + + out->font_ascent = PARSE_INT32(&file[pos + 0], msb); + out->font_descent = PARSE_INT32(&file[pos + 4], msb); + out->max_overlap = PARSE_INT32(&file[pos + 8], msb); + pos += 12; + + out->min_bounds.left_side_bearing = PARSE_INT16(&file[pos + 0], msb); + out->min_bounds.right_side_bearing = PARSE_INT16(&file[pos + 2], msb); + out->min_bounds.character_width = PARSE_INT16(&file[pos + 4], msb); + out->min_bounds.character_ascent = PARSE_INT16(&file[pos + 6], msb); + out->min_bounds.character_descent = PARSE_INT16(&file[pos + 8], msb); + out->min_bounds.character_attributes = PARSE_UINT16(&file[pos + 10], msb); + pos += 12; + + out->max_bounds.left_side_bearing = PARSE_INT16(&file[pos + 0], msb); + out->max_bounds.right_side_bearing = PARSE_INT16(&file[pos + 2], msb); + out->max_bounds.character_width = PARSE_INT16(&file[pos + 4], msb); + out->max_bounds.character_ascent = PARSE_INT16(&file[pos + 6], msb); + out->max_bounds.character_descent = PARSE_INT16(&file[pos + 8], msb); + out->max_bounds.character_attributes = PARSE_UINT16(&file[pos + 10], msb); + pos += 12; + + if (with_ink_bounds) { + out->min_ink_bounds.left_side_bearing = PARSE_INT16(&file[pos + 0], msb); + out->min_ink_bounds.right_side_bearing = PARSE_INT16(&file[pos + 2], msb); + out->min_ink_bounds.character_width = PARSE_INT16(&file[pos + 4], msb); + out->min_ink_bounds.character_ascent = PARSE_INT16(&file[pos + 6], msb); + out->min_ink_bounds.character_descent = PARSE_INT16(&file[pos + 8], msb); + out->min_ink_bounds.character_attributes = PARSE_UINT16(&file[pos + 10], msb); + pos += 12; + + out->max_ink_bounds.left_side_bearing = PARSE_INT16(&file[pos + 0], msb); + out->max_ink_bounds.right_side_bearing = PARSE_INT16(&file[pos + 2], msb); + out->max_ink_bounds.character_width = PARSE_INT16(&file[pos + 4], msb); + out->max_ink_bounds.character_ascent = PARSE_INT16(&file[pos + 6], msb); + out->max_ink_bounds.character_descent = PARSE_INT16(&file[pos + 8], msb); + out->max_ink_bounds.character_attributes = PARSE_UINT16(&file[pos + 10], msb); + pos += 12; + } else { + memcpy(&out->min_ink_bounds, &out->min_bounds, sizeof(out->min_bounds)); + memcpy(&out->max_ink_bounds, &out->max_bounds, sizeof(out->max_bounds)); + } + + return 0; + +ebfont: + errno = EBFONT; + return -1; +} diff --git a/libparsepcf_get_bitmap_offsets.c b/libparsepcf_get_bitmap_offsets.c new file mode 100644 index 0000000..192b07b --- /dev/null +++ b/libparsepcf_get_bitmap_offsets.c @@ -0,0 +1,28 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +int +libparsepcf_get_bitmap_offsets(const char *file, size_t size, + const struct libparsepcf_table *table, + const struct libparsepcf_bitmaps *meta, + size_t *out, size_t first, size_t count) +{ + int msb = table->format & LIBPARSEPCF_BYTE; + size_t pos = table->offset + 8 + first * 4; + size_t i; + + (void) size; + + for (i = 0; i < count; i++, pos += 4) { + out[i] = (size_t)PARSE_UINT32(&file[pos + 0], msb); + if (out[i] > meta->bitmap_size) + goto ebfont; + } + + return 0; + +ebfont: + errno = EBFONT; + return -1; +} diff --git a/libparsepcf_get_bitmaps.c b/libparsepcf_get_bitmaps.c new file mode 100644 index 0000000..961da96 --- /dev/null +++ b/libparsepcf_get_bitmaps.c @@ -0,0 +1,52 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +int +libparsepcf_get_bitmaps(const char *file, size_t size, + const struct libparsepcf_table *table, + struct libparsepcf_bitmaps *out) +{ + size_t pos, glyph_pad; + int msb = table->format & LIBPARSEPCF_BYTE; + + (void) size; + + if (table->size < 8) + goto ebfont; + + pos = table->offset; + + if (table->format != libparsepcf_parse_lsb_uint32__(&file[pos])) + goto ebfont; + pos += 4; + + out->glyph_count = (size_t)PARSE_UINT32(&file[pos], msb); + pos += 4; + + if (16 > table->size - (pos - table->offset) || + out->glyph_count > (table->size - (pos - table->offset) - 16) / 4) + goto ebfont; + pos += out->glyph_count * 4; + glyph_pad = (size_t)(table->format & LIBPARSEPCF_GLYPH_PAD_MASK); + out->bitmap_size = (size_t)PARSE_UINT32(&file[pos + 4 * glyph_pad], msb); + pos += 16; + + out->bitmap_data = (const void *)&file[pos]; + if (out->bitmap_size > table->size - (pos - table->offset)) + goto ebfont; + + out->bit_packing = (size_t)1 << ((table->format & LIBPARSEPCF_SCAN_UNIT_MASK) >> 4); + out->row_padding = (size_t)1 << ((table->format & LIBPARSEPCF_GLYPH_PAD_MASK) >> 0); + out->lsbyte = !!(table->format & LIBPARSEPCF_BYTE); + out->lsbit = !!(table->format & LIBPARSEPCF_BIT); + + if (out->row_padding < out->bit_packing) + goto ebfont; + + return 0; + +ebfont: + errno = EBFONT; + return -1; +} diff --git a/libparsepcf_get_encoding.c b/libparsepcf_get_encoding.c new file mode 100644 index 0000000..34ccd9b --- /dev/null +++ b/libparsepcf_get_encoding.c @@ -0,0 +1,44 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +int +libparsepcf_get_encoding(const char *file, size_t size, + const struct libparsepcf_table *table, + struct libparsepcf_encoding *out) +{ + int msb = table->format & LIBPARSEPCF_BYTE; + size_t pos; + + (void) size; + + if (table->size < 14) + goto ebfont; + + pos = table->offset; + + if (table->format != libparsepcf_parse_lsb_uint32__(&file[pos])) + goto ebfont; + pos += 4; + + out->min_byte2 = PARSE_UINT16(&file[pos + 0], msb); + out->max_byte2 = PARSE_UINT16(&file[pos + 2], msb); + out->min_byte1 = PARSE_UINT16(&file[pos + 4], msb); + out->max_byte1 = PARSE_UINT16(&file[pos + 6], msb); + out->default_char = PARSE_UINT16(&file[pos + 8], msb); + pos += 10; + + if (out->min_byte2 > out->max_byte2 || out->max_byte2 > 255 || + out->min_byte1 > out->max_byte1 || out->max_byte1 > 255) + goto ebfont; + + out->glyph_count = (size_t)(out->max_byte2 - out->min_byte2 + 1) * (size_t)(out->max_byte1 - out->min_byte1 + 1); + if (out->glyph_count > table->size - (pos - table->offset) / 2) + goto ebfont; + + return 0; + +ebfont: + errno = EBFONT; + return -1; +} diff --git a/libparsepcf_get_glyph_index.c b/libparsepcf_get_glyph_index.c new file mode 100644 index 0000000..86150d6 --- /dev/null +++ b/libparsepcf_get_glyph_index.c @@ -0,0 +1,36 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +int +libparsepcf_get_glyph_index(const char *file, size_t size, + const struct libparsepcf_table *table, + const struct libparsepcf_encoding *meta, + const char *text, size_t *out) +{ + int msb = table->format & LIBPARSEPCF_BYTE; + size_t pos, i, j; + + (void) size; + + i = (size_t)*(const uint8_t *)text; + if (i < (size_t)meta->min_byte1 || i > (size_t)meta->max_byte1) + goto not_encoded; + i -= (size_t)meta->min_byte1; + + if (meta->min_byte2 | meta->max_byte2) { + j = (size_t)*(const uint8_t *)text; + if (j < (size_t)meta->min_byte2 || j > (size_t)meta->max_byte2) + goto not_encoded; + j -= (size_t)meta->min_byte2; + i = i * (meta->max_byte2 - meta->min_byte2 + 1) + j; + } + + pos = table->offset + 14 + i * 2; + *out = (size_t)PARSE_UINT16(&file[pos], msb); + return 0; + +not_encoded: + *out = (size_t)0xFFFF; + return 0; +} diff --git a/libparsepcf_get_glyph_indices.c b/libparsepcf_get_glyph_indices.c new file mode 100644 index 0000000..a1e7ed6 --- /dev/null +++ b/libparsepcf_get_glyph_indices.c @@ -0,0 +1,22 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +int +libparsepcf_get_glyph_indices(const char *file, size_t size, + const struct libparsepcf_table *table, + const struct libparsepcf_encoding *meta, + size_t *out, size_t first, size_t count) +{ + int msb = table->format & LIBPARSEPCF_BYTE; + size_t pos = 14 + first * 2; + size_t i; + + (void) size; + (void) meta; + + for (i = 0; i < count; i++, pos += 2) + out[i] = (size_t)PARSE_UINT16(&file[pos], msb); + + return 0; +} diff --git a/libparsepcf_get_glyph_name_subtable.c b/libparsepcf_get_glyph_name_subtable.c new file mode 100644 index 0000000..578bf87 --- /dev/null +++ b/libparsepcf_get_glyph_name_subtable.c @@ -0,0 +1,34 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +int +libparsepcf_get_glyph_name_subtable(const char *file, size_t size, + const struct libparsepcf_table *table, + const struct libparsepcf_glyph_names *meta, + const char **out, size_t first, size_t count) +{ + size_t pos, i, off; + int msb = table->format & LIBPARSEPCF_BYTE; + + (void) size; + + if (table->size < 8) + goto ebfont; + + pos = table->offset + 8 + first * 4; + for (i = 0; i < count; i++, pos += 4) { + off = (size_t)PARSE_UINT32(&file[pos], msb); + if (off > meta->strings_size) + goto ebfont; + out[i] = &meta->strings[off]; + if (!memchr(out[i], 0, meta->strings_size - off)) + goto ebfont; + } + + return 0; + +ebfont: + errno = EBFONT; + return -1; +} diff --git a/libparsepcf_get_glyph_names.c b/libparsepcf_get_glyph_names.c new file mode 100644 index 0000000..ca690a4 --- /dev/null +++ b/libparsepcf_get_glyph_names.c @@ -0,0 +1,43 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +int +libparsepcf_get_glyph_names(const char *file, size_t size, + const struct libparsepcf_table *table, + struct libparsepcf_glyph_names *out) +{ + size_t pos; + int msb = table->format & LIBPARSEPCF_BYTE; + + (void) size; + + if (table->size < 8) + goto ebfont; + + pos = table->offset; + + if (table->format != libparsepcf_parse_lsb_uint32__(&file[pos])) + goto ebfont; + pos += 4; + + out->glyph_count = (size_t)PARSE_UINT32(&file[pos], msb); + pos += 4; + + if (4 > table->size - (pos - table->offset) || + out->glyph_count > (table->size - (pos - table->offset) - 4) / 4) + goto ebfont; + pos += out->glyph_count * 4; + out->strings_size = (size_t)PARSE_UINT32(&file[pos], msb); + pos += 4; + + out->strings = &file[pos]; + if (out->strings_size > table->size - (pos - table->offset)) + goto ebfont; + + return 0; + +ebfont: + errno = EBFONT; + return -1; +} diff --git a/libparsepcf_get_metrics.c b/libparsepcf_get_metrics.c new file mode 100644 index 0000000..5e2fbe0 --- /dev/null +++ b/libparsepcf_get_metrics.c @@ -0,0 +1,50 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +int +libparsepcf_get_metrics(const char *file, size_t size, + const struct libparsepcf_table *table, + struct libparsepcf_metrics *out, size_t first, size_t count) +{ + size_t pos, i; + int msb = table->format & LIBPARSEPCF_BYTE; + int compressed = table->format & LIBPARSEPCF_COMPRESSED_METRICS; + + (void) size; + + pos = table->offset + (compressed ? 6 : 8); + pos += first * (compressed ? 5 : 12); + + if (compressed) { + for (i = 0; i < count; i++, pos += 5) { + out[i].left_side_bearing = (int16_t)((int)((const uint8_t *)file)[pos + 0] - 128); + out[i].right_side_bearing = (int16_t)((int)((const uint8_t *)file)[pos + 1] - 128); + out[i].character_width = (int16_t)((int)((const uint8_t *)file)[pos + 2] - 128); + out[i].character_ascent = (int16_t)((int)((const uint8_t *)file)[pos + 3] - 128); + out[i].character_descent = (int16_t)((int)((const uint8_t *)file)[pos + 4] - 128); + out[i].character_attributes = 0; + if (out[i].left_side_bearing > out[i].right_side_bearing || + (int32_t)out[i].character_ascent < -(int32_t)out[i].character_descent) + goto ebfont; + } + } else { + for (i = 0; i < count; i++, pos += 12) { + out[i].left_side_bearing = PARSE_INT16(&file[pos + 0], msb); + out[i].right_side_bearing = PARSE_INT16(&file[pos + 2], msb); + out[i].character_width = PARSE_INT16(&file[pos + 4], msb); + out[i].character_ascent = PARSE_INT16(&file[pos + 6], msb); + out[i].character_descent = PARSE_INT16(&file[pos + 8], msb); + out[i].character_attributes = PARSE_UINT16(&file[pos + 10], msb); + if (out[i].left_side_bearing > out[i].right_side_bearing || + (int32_t)out[i].character_ascent < -(int32_t)out[i].character_descent) + goto ebfont; + } + } + + return 0; + +ebfont: + errno = EBFONT; + return -1; +} diff --git a/libparsepcf_get_metrics_count.c b/libparsepcf_get_metrics_count.c new file mode 100644 index 0000000..fab5544 --- /dev/null +++ b/libparsepcf_get_metrics_count.c @@ -0,0 +1,38 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +int +libparsepcf_get_metrics_count(const char *file, size_t size, const struct libparsepcf_table *table, size_t *countp) +{ + size_t pos; + int msb = table->format & LIBPARSEPCF_BYTE; + int compressed = table->format & LIBPARSEPCF_COMPRESSED_METRICS; + + if (table->size < (compressed ? 6 : 8)) + goto ebfont; + + pos = table->offset; + + if (table->format != libparsepcf_parse_lsb_uint32__(&file[pos])) + goto ebfont; + pos += 4; + + if (compressed) { + *countp = (size_t)PARSE_UINT16(&file[pos], msb); + pos += 2; + if (*countp > (size - pos) / 5) + goto ebfont; + } else { + *countp = (size_t)PARSE_UINT32(&file[pos], msb); + pos += 4; + if (*countp > (size - pos) / 12) + goto ebfont; + } + + return 0; + +ebfont: + errno = EBFONT; + return -1; +} diff --git a/libparsepcf_get_properties.c b/libparsepcf_get_properties.c new file mode 100644 index 0000000..f586a5c --- /dev/null +++ b/libparsepcf_get_properties.c @@ -0,0 +1,46 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +int +libparsepcf_get_properties(const char *file, size_t size, + const struct libparsepcf_table *table, + struct libparsepcf_properties *out) +{ + size_t pos; + int msb = table->format & LIBPARSEPCF_BYTE; + + (void) size; + + if (table->size < 8) + goto ebfont; + + pos = table->offset; + + if (table->format != libparsepcf_parse_lsb_uint32__(&file[pos])) + goto ebfont; + pos += 4; + + out->property_count = (size_t)PARSE_UINT32(&file[pos], msb); + pos += 4; + + if (4 > table->size - (pos - table->offset) || + out->property_count > (table->size - (pos - table->offset) - 4) / 9) + goto ebfont; + pos += out->property_count * 9; + pos += (4 - (out->property_count & 3)) & 3; + if (pos - table->offset > table->size - 4) + goto ebfont; + out->strings_size = (size_t)PARSE_UINT32(&file[pos], msb); + pos += 4; + + out->strings = &file[pos]; + if (out->strings_size > table->size - (pos - table->offset)) + goto ebfont; + + return 0; + +ebfont: + errno = EBFONT; + return -1; +} diff --git a/libparsepcf_get_property_subtable.c b/libparsepcf_get_property_subtable.c new file mode 100644 index 0000000..d19498c --- /dev/null +++ b/libparsepcf_get_property_subtable.c @@ -0,0 +1,47 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +int +libparsepcf_get_property_subtable(const char *file, size_t size, + const struct libparsepcf_table *table, + const struct libparsepcf_properties *meta, + struct libparsepcf_property_subtable *props, + size_t first, size_t count) +{ + int msb = table->format & LIBPARSEPCF_BYTE; + size_t pos = table->offset + 8 + first * 9; + size_t i, off, maxlen; + + (void) size; + + for (i = 0; i < count; i++, pos += 9) { + off = (size_t)PARSE_UINT32(&file[pos + 0], msb); + if (off > meta->strings_size) + goto ebfont; + maxlen = meta->strings_size - off; + props[i].name = &meta->strings[off]; + if (!memchr(props[i].name, 0, maxlen)) + goto ebfont; + + props[i].is_string_property = !!file[pos + 4]; + + if (props[i].is_string_property) { + off = (size_t)PARSE_UINT32(&file[pos + 5], msb); + if (off > meta->strings_size) + goto ebfont; + maxlen = meta->strings_size - off; + props[i].value.string_value = &meta->strings[off]; + if (!memchr(props[i].value.string_value, 0, maxlen)) + goto ebfont; + } else { + props[i].value.signed_value = PARSE_INT32(&file[pos + 5], msb); + } + } + + return 0; + +ebfont: + errno = EBFONT; + return -1; +} diff --git a/libparsepcf_get_swidth_count.c b/libparsepcf_get_swidth_count.c new file mode 100644 index 0000000..90a5f4a --- /dev/null +++ b/libparsepcf_get_swidth_count.c @@ -0,0 +1,31 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +int +libparsepcf_get_swidth_count(const char *file, size_t size, const struct libparsepcf_table *table, size_t *countp) +{ + size_t pos; + int msb = table->format & LIBPARSEPCF_BYTE; + + if (table->size < 8) + goto ebfont; + + pos = table->offset; + + if (table->format != libparsepcf_parse_lsb_uint32__(&file[pos])) + goto ebfont; + pos += 4; + + *countp = (size_t)PARSE_UINT32(&file[pos], msb); + pos += 4; + + if (*countp > (size - pos) / 4) + goto ebfont; + + return 0; + +ebfont: + errno = EBFONT; + return -1; +} diff --git a/libparsepcf_get_swidths.c b/libparsepcf_get_swidths.c new file mode 100644 index 0000000..d350b74 --- /dev/null +++ b/libparsepcf_get_swidths.c @@ -0,0 +1,20 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +int +libparsepcf_get_swidths(const char *file, size_t size, + const struct libparsepcf_table *table, + int32_t *out, size_t first, size_t count) +{ + int msb = table->format & LIBPARSEPCF_BYTE; + size_t pos = table->offset + 8 + 4 * first; + size_t i; + + (void) size; + + for (i = 0; i < count; i++, pos += 4) + out[i] = PARSE_INT32(&file[pos], msb); + + return 0; +} diff --git a/libparsepcf_get_table_count.c b/libparsepcf_get_table_count.c new file mode 100644 index 0000000..0eb8741 --- /dev/null +++ b/libparsepcf_get_table_count.c @@ -0,0 +1,23 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +int +libparsepcf_get_table_count(const char *file, size_t size, size_t *countp) +{ + uint32_t count; + + if (size < 8 || file[0] != 1 || file[1] != 'f' || file[2] != 'c' || file[3] != 'p') + goto ebfont; + + count = libparsepcf_parse_lsb_uint32__(&file[4]); + if (count > (size - 8) / 16) + goto ebfont; + + *countp = (size_t)count; + return 0; + +ebfont: + errno = EBFONT; + return -1; +} diff --git a/libparsepcf_get_tables.c b/libparsepcf_get_tables.c new file mode 100644 index 0000000..56cffc2 --- /dev/null +++ b/libparsepcf_get_tables.c @@ -0,0 +1,39 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +int +libparsepcf_get_tables(const char *file, size_t size, struct libparsepcf_table *tables, size_t first, size_t count) +{ + size_t pos = 8 + first * 16; + size_t i; + + for (i = 0; i < count; i++, pos += 16) { + tables[i].type = libparsepcf_parse_lsb_uint32__(&file[pos + 0]); + tables[i].format = libparsepcf_parse_lsb_uint32__(&file[pos + 4]); + tables[i].size = libparsepcf_parse_lsb_uint32__(&file[pos + 8]); + tables[i].offset = libparsepcf_parse_lsb_uint32__(&file[pos + 12]); + + if ((size_t)tables[i].offset > size) + goto ebfont; + + /* For some reasons files specify table sizes such that they + * actually except the boundary of the file, despite not using + * that much data. Don't including this fix, but instead check + * that the table size is not too large, will break your + * favourite fonts. */ + if ((size_t)tables[i].size > size - (size_t)tables[i].offset) { +#if SIZE_MAX > UINT32_MAX + if (size - (size_t)tables[i].offset > (size_t)UINT32_MAX) + goto ebfont; +#endif + tables[i].size = (uint32_t)(size - (size_t)tables[i].offset); + } + } + + return 0; + +ebfont: + errno = EBFONT; + return -1; +} diff --git a/libparsepcf_parse_int16_from_unsigned__.c b/libparsepcf_parse_int16_from_unsigned__.c new file mode 100644 index 0000000..3d61ef8 --- /dev/null +++ b/libparsepcf_parse_int16_from_unsigned__.c @@ -0,0 +1,13 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +int16_t +libparsepcf_parse_int16_from_unsigned__(uint16_t u) +{ + if (u & UINT16_C(0x8000)) { + return (int16_t)(uint16_t)~u - 1; + } else { + return (int16_t)u; + } +} diff --git a/libparsepcf_parse_int32_from_unsigned__.c b/libparsepcf_parse_int32_from_unsigned__.c new file mode 100644 index 0000000..37de799 --- /dev/null +++ b/libparsepcf_parse_int32_from_unsigned__.c @@ -0,0 +1,13 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +int32_t +libparsepcf_parse_int32_from_unsigned__(uint32_t u) +{ + if (u & UINT32_C(0x80000000)) { + return (int32_t)(uint32_t)~u - 1; + } else { + return (int32_t)u; + } +} diff --git a/libparsepcf_parse_lsb_uint16__.c b/libparsepcf_parse_lsb_uint16__.c new file mode 100644 index 0000000..47d9a0d --- /dev/null +++ b/libparsepcf_parse_lsb_uint16__.c @@ -0,0 +1,12 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +uint16_t +libparsepcf_parse_lsb_uint16__(const void *buf) +{ + const uint8_t *bs = buf; + uint16_t a = (uint16_t)bs[0] << 0; + uint16_t b = (uint16_t)bs[1] << 8; + return a | b; +} diff --git a/libparsepcf_parse_lsb_uint32__.c b/libparsepcf_parse_lsb_uint32__.c new file mode 100644 index 0000000..6ea37ab --- /dev/null +++ b/libparsepcf_parse_lsb_uint32__.c @@ -0,0 +1,14 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +uint32_t +libparsepcf_parse_lsb_uint32__(const void *buf) +{ + const uint8_t *bs = buf; + uint32_t a = (uint32_t)bs[0] << 0; + uint32_t b = (uint32_t)bs[1] << 8; + uint32_t c = (uint32_t)bs[2] << 16; + uint32_t d = (uint32_t)bs[3] << 24; + return a | b | c | d; +} diff --git a/libparsepcf_parse_msb_uint16__.c b/libparsepcf_parse_msb_uint16__.c new file mode 100644 index 0000000..9c899c4 --- /dev/null +++ b/libparsepcf_parse_msb_uint16__.c @@ -0,0 +1,12 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +uint16_t +libparsepcf_parse_msb_uint16__(const void *buf) +{ + const uint8_t *bs = buf; + uint16_t a = (uint16_t)bs[0] << 8; + uint16_t b = (uint16_t)bs[1] << 0; + return a | b; +} diff --git a/libparsepcf_parse_msb_uint32__.c b/libparsepcf_parse_msb_uint32__.c new file mode 100644 index 0000000..a3dfdc1 --- /dev/null +++ b/libparsepcf_parse_msb_uint32__.c @@ -0,0 +1,14 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +uint32_t +libparsepcf_parse_msb_uint32__(const void *buf) +{ + const uint8_t *bs = buf; + uint32_t a = (uint32_t)bs[0] << 24; + uint32_t b = (uint32_t)bs[1] << 16; + uint32_t c = (uint32_t)bs[2] << 8; + uint32_t d = (uint32_t)bs[3] << 0; + return a | b | c | d; +} diff --git a/libparsepcf_preparse_font.c b/libparsepcf_preparse_font.c new file mode 100644 index 0000000..5874b50 --- /dev/null +++ b/libparsepcf_preparse_font.c @@ -0,0 +1,104 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +int +libparsepcf_preparse_font(const char *file, size_t size, struct libparsepcf_font *font) +{ + size_t min = SIZE_MAX, max = 0, table_i, table_n; + struct libparsepcf_table *tables = NULL; + + if (libparsepcf_get_table_count(file, size, &table_n)) + goto fail; + tables = calloc(table_n, sizeof(*tables)); + if (!tables) + goto fail; + if (libparsepcf_get_tables(file, size, tables, 0, table_n)) + goto fail; + + for (table_i = 0; table_i < table_n; table_i++) { + if (tables[table_i].type == LIBPARSEPCF_PROPERTIES) { + font->prob_table = &tables[table_i]; + if (libparsepcf_get_properties(file, size, &tables[table_i], &font->props)) + goto fail; + } else if (tables[table_i].type == LIBPARSEPCF_ACCELERATORS && !font->accel_table) { + font->accel_table = &tables[table_i]; + if (libparsepcf_get_accelerators(file, size, &tables[table_i], &font->accels)) + goto fail; + } else if (tables[table_i].type == LIBPARSEPCF_BDF_ACCELERATORS) { + font->accel_table = &tables[table_i]; + if (libparsepcf_get_accelerators(file, size, &tables[table_i], &font->accels)) + goto fail; + } else if (tables[table_i].type == LIBPARSEPCF_METRICS) { + font->mtx_table = &tables[table_i]; + if (libparsepcf_get_metrics_count(file, size, &tables[table_i], &font->metrics)) + goto fail; + } else if (tables[table_i].type == LIBPARSEPCF_INK_METRICS) { + font->inkmtx_table = &tables[table_i]; + if (libparsepcf_get_metrics_count(file, size, &tables[table_i], &font->ink_metrics)) + goto fail; + } else if (tables[table_i].type == LIBPARSEPCF_BDF_ENCODINGS) { + font->enc_table = &tables[table_i]; + if (libparsepcf_get_encoding(file, size, &tables[table_i], &font->encoding)) + goto fail; + } else if (tables[table_i].type == LIBPARSEPCF_BITMAPS) { + font->bitmap_table = &tables[table_i]; + if (libparsepcf_get_bitmaps(file, size, &tables[table_i], &font->bitmaps)) + goto fail; + } else if (tables[table_i].type == LIBPARSEPCF_GLYPH_NAMES) { + font->name_table = &tables[table_i]; + if (libparsepcf_get_glyph_names(file, size, &tables[table_i], &font->names)) + goto fail; + } else if (tables[table_i].type == LIBPARSEPCF_SWIDTHS) { + font->swidth_table = &tables[table_i]; + if (libparsepcf_get_swidth_count(file, size, &tables[table_i], &font->swidths)) + goto fail; + } + } + + if (font->mtx_table) { + if (font->metrics < min) + min = font->metrics; + if (font->metrics > max) + max = font->metrics; + } + if (font->inkmtx_table) { + if (font->ink_metrics < min) + min = font->ink_metrics; + if (font->ink_metrics > max) + max = font->ink_metrics; + } + if (font->bitmap_table) { + if (font->bitmaps.glyph_count < min) + min = font->bitmaps.glyph_count; + if (font->bitmaps.glyph_count > max) + max = font->bitmaps.glyph_count; + } + if (font->name_table) { + if (font->names.glyph_count < min) + min = font->names.glyph_count; + if (font->names.glyph_count > max) + max = font->names.glyph_count; + } + if (font->swidth_table) { + if (font->swidths < min) + min = font->swidths; + if (font->swidths > max) + max = font->swidths; + } + + if (min != max || !font->mtx_table) + goto ebfont; + if (!font->inkmtx_table && font->accel_table && font->accels.ink_metrics) + goto ebfont; + font->glyph_count = min; + font->_tables = tables; + + return 0; + +ebfont: + errno = EBFONT; +fail: + free(tables); + return -1; +} diff --git a/mk/linux.mk b/mk/linux.mk new file mode 100644 index 0000000..d016d31 --- /dev/null +++ b/mk/linux.mk @@ -0,0 +1,4 @@ +LIBEXT = so +LIBFLAGS = -shared -Wl,-soname,lib$(LIB_NAME).$(LIBEXT).$(LIB_MAJOR) +LIBMAJOREXT = $(LIBEXT).$(LIB_MAJOR) +LIBMINOREXT = $(LIBEXT).$(LIB_VERSION) diff --git a/mk/macos.mk b/mk/macos.mk new file mode 100644 index 0000000..bd92de6 --- /dev/null +++ b/mk/macos.mk @@ -0,0 +1,4 @@ +LIBEXT = dylib +LIBFLAGS = -dynamiclib +LIBMAJOREXT = $(LIB_MAJOR).$(LIBEXT) +LIBMINOREXT = $(LIB_VERSION).$(LIBEXT) diff --git a/mk/windows.mk b/mk/windows.mk new file mode 100644 index 0000000..e9602e1 --- /dev/null +++ b/mk/windows.mk @@ -0,0 +1,4 @@ +LIBEXT = dll +LIBFLAGS = -mdll +LIBMAJOREXT = $(LIB_MAJOR).$(LIBEXT) +LIBMINOREXT = $(LIB_VERSION).$(LIBEXT) |