summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile19
-rw-r--r--include/rufl.h24
-rw-r--r--src/Makefile3
-rw-r--r--src/rufl_character_set_test.c26
-rw-r--r--src/rufl_decompose.c2
-rw-r--r--src/rufl_dump_state.c65
-rw-r--r--src/rufl_init.c1088
-rw-r--r--src/rufl_internal.h159
-rw-r--r--src/rufl_invalidate_cache.c2
-rw-r--r--src/rufl_metrics.c36
-rw-r--r--src/rufl_paint.c153
-rw-r--r--src/rufl_quit.c24
-rw-r--r--src/rufl_substitution_table.c1143
-rw-r--r--test/INDEX8
-rw-r--r--test/Makefile14
-rw-r--r--test/data/oldfminit/Allerta (renamed from test/data/Encoding)0
-rw-r--r--test/data/oldfminit/INDEX10
-rw-r--r--test/data/oldfminit/Latin1270
-rw-r--r--test/data/oldfminit/brokenencoding.cfg28
-rw-r--r--test/data/oldfminit/latin1.cfg28
-rw-r--r--test/data/oldfminit/mergeumap.cfg40
-rw-r--r--test/data/oldfminit/nomapping.cfg19
-rw-r--r--test/data/oldfminit/symbol.cfg28
-rw-r--r--test/harness-priv.h45
-rw-r--r--test/harness.c139
-rw-r--r--test/harness.h10
-rw-r--r--test/manyfonts.c100
-rw-r--r--test/mocks.c671
-rw-r--r--test/nofonts.c20
-rw-r--r--test/oldfminit.c353
-rw-r--r--test/olducsinit.c140
-rw-r--r--test/rufl_chars.c68
-rw-r--r--test/rufl_test.c21
-rw-r--r--test/testutils.h123
-rw-r--r--test/ucsinit.c140
35 files changed, 4395 insertions, 624 deletions
diff --git a/Makefile b/Makefile
index 1e44ea3..b7a4abe 100644
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,6 @@
# Component settings
COMPONENT := rufl
-COMPONENT_VERSION := 0.0.5
+COMPONENT_VERSION := 0.1.0
# Default to a static library
COMPONENT_TYPE ?= lib-static
@@ -9,12 +9,15 @@ PREFIX ?= /opt/netsurf
NSSHARED ?= $(PREFIX)/share/netsurf-buildsystem
include $(NSSHARED)/makefiles/Makefile.tools
-TESTRUNNER := $(ECHO)
+TESTRUNNER := $(PERL) $(NSTESTTOOLS)/testrunner.pl
# Toolchain flags
WARNFLAGS := -Wall -W -Wundef -Wpointer-arith -Wcast-align \
-Wwrite-strings -Wstrict-prototypes -Wmissing-prototypes \
- -Wmissing-declarations -Wnested-externs -pedantic
+ -Wmissing-declarations -Wnested-externs
+ifeq ($(findstring -riscos,$(HOST)),-riscos)
+ WARNFLAGS := $(WARNFLAGS) -pedantic
+endif
# BeOS/Haiku/AmigaOS4 standard library headers create warnings
ifneq ($(BUILD),i586-pc-haiku)
ifneq ($(findstring amigaos,$(BUILD)),amigaos)
@@ -28,12 +31,20 @@ else
# __inline__ is a GCCism
CFLAGS := $(CFLAGS) -Dinline="__inline__"
endif
+CFLAGS := $(CFLAGS) -D_DEFAULT_SOURCE -D_XOPEN_SOURCE=500
# OSLib
ifneq ($(findstring clean,$(MAKECMDGOALS)),clean)
- ifeq ($(BUILD),arm-unknown-riscos)
+ ifeq ($(findstring -riscos,$(HOST)),-riscos)
CFLAGS := $(CFLAGS) -I$(PREFIX)/include
LDFLAGS := $(LDFLAGS) -lOSLib32
+ TESTLDFLAGS := $(TESTLDFLAGS) -static
+ else
+ # Regardless of the host platform we're building for, we
+ # still need the RISC OS build environment because we need the
+ # OSLib headers.
+ # XXX: is there a way to avoid this path being hard-coded?
+ CFLAGS := $(CFLAGS) -I/opt/netsurf/arm-unknown-riscos/env/include
endif
endif
diff --git a/include/rufl.h b/include/rufl.h
index 767022e..e7d0987 100644
--- a/include/rufl.h
+++ b/include/rufl.h
@@ -8,6 +8,7 @@
#ifndef RUFL_H
#define RUFL_H
+#include <inttypes.h>
#include <stdbool.h>
#include <stdlib.h>
#include "oslib/os.h"
@@ -53,7 +54,7 @@ extern os_error *rufl_fm_error;
/** List of available font families. */
extern const char **rufl_family_list;
/** Number of entries in rufl_family_list. */
-extern unsigned int rufl_family_list_entries;
+extern size_t rufl_family_list_entries;
/** Menu of font families. */
extern void *rufl_family_menu;
@@ -124,7 +125,7 @@ rufl_code rufl_split(const char *font_family, rufl_style font_style,
/** Type of callback function for rufl_paint_callback(). */
typedef void (*rufl_callback_t)(void *context,
const char *font_name, unsigned int font_size,
- const char *s8, unsigned short *s16, unsigned int n,
+ const uint8_t *s8, const uint32_t *s32, unsigned int n,
int x, int y);
@@ -154,10 +155,10 @@ rufl_code rufl_decompose_glyph(const char *font_family,
*/
rufl_code rufl_font_metrics(const char *font_family, rufl_style font_style,
- os_box *bbox, int *xkern, int *ykern, int *italic,
- int *ascent, int *descent,
- int *xheight, int *cap_height,
- signed char *uline_position, unsigned char *uline_thickness);
+ os_box *bbox, int32_t *xkern, int32_t *ykern, int32_t *italic,
+ int32_t *ascent, int32_t *descent,
+ int32_t *xheight, int32_t *cap_height,
+ int8_t *uline_position, uint8_t *uline_thickness);
/**
@@ -167,9 +168,9 @@ rufl_code rufl_font_metrics(const char *font_family, rufl_style font_style,
rufl_code rufl_glyph_metrics(const char *font_family,
rufl_style font_style, unsigned int font_size,
const char *string, size_t length,
- int *x_bearing, int *y_bearing,
- int *width, int *height,
- int *x_advance, int *y_advance);
+ int32_t *x_bearing, int32_t *y_bearing,
+ int32_t *width, int32_t *height,
+ int32_t *x_advance, int32_t *y_advance);
/**
@@ -177,15 +178,14 @@ rufl_code rufl_glyph_metrics(const char *font_family,
*/
rufl_code rufl_font_bbox(const char *font_family, rufl_style font_style,
- unsigned int font_size,
- int *bbox);
+ unsigned int font_size, os_box *bbox);
/**
* Dump the internal library state to stdout.
*/
-void rufl_dump_state(void);
+void rufl_dump_state(bool verbose);
/**
diff --git a/src/Makefile b/src/Makefile
index 7592083..22df8df 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -1,7 +1,8 @@
# Sources
DIR_SOURCES := rufl_character_set_test.c rufl_decompose.c rufl_dump_state.c \
rufl_find.c rufl_init.c rufl_invalidate_cache.c \
- rufl_metrics.c rufl_paint.c rufl_quit.c
+ rufl_metrics.c rufl_paint.c rufl_substitution_table.c \
+ rufl_quit.c
ifeq ($(toolchain),norcroft)
DIR_SOURCES := $(DIR_SOURCES) strfuncs.c
diff --git a/src/rufl_character_set_test.c b/src/rufl_character_set_test.c
index 45fbcaf..2e97894 100644
--- a/src/rufl_character_set_test.c
+++ b/src/rufl_character_set_test.c
@@ -12,18 +12,28 @@
* Test if a character set contains a character.
*
* \param charset character set
- * \param c character code
+ * \param u Unicode codepoint
* \return true if present, false if absent
*/
-bool rufl_character_set_test(struct rufl_character_set *charset,
- unsigned int c)
+bool rufl_character_set_test(const struct rufl_character_set *charset,
+ uint32_t u)
{
- unsigned int block = c >> 8;
- unsigned int byte = (c >> 3) & 31;
- unsigned int bit = c & 7;
+ unsigned int plane = u >> 16;
+ unsigned int block = (u >> 8) & 0xff;
+ unsigned int byte = (u >> 3) & 31;
+ unsigned int bit = u & 7;
- if (256 <= block)
+ if (17 <= plane)
+ return false;
+
+ /* Look for the plane we want */
+ while (PLANE_ID(charset->metadata) != plane &&
+ EXTENSION_FOLLOWS(charset->metadata)) {
+ charset = (void *)(((uint8_t *)charset) +
+ PLANE_SIZE(charset->metadata));
+ }
+ if (PLANE_ID(charset->metadata) != plane)
return false;
if (charset->index[block] == BLOCK_EMPTY)
@@ -31,7 +41,7 @@ bool rufl_character_set_test(struct rufl_character_set *charset,
else if (charset->index[block] == BLOCK_FULL)
return true;
else {
- unsigned char z = charset->block[charset->index[block]][byte];
+ uint8_t z = charset->block[charset->index[block]][byte];
return z & (1 << bit);
}
}
diff --git a/src/rufl_decompose.c b/src/rufl_decompose.c
index edf9748..2085e8f 100644
--- a/src/rufl_decompose.c
+++ b/src/rufl_decompose.c
@@ -8,7 +8,7 @@
#include <assert.h>
#include <stdio.h>
-#include "oslib/font.h"
+#include <oslib/font.h>
#include "rufl_internal.h"
diff --git a/src/rufl_dump_state.c b/src/rufl_dump_state.c
index 06a1f22..f466333 100644
--- a/src/rufl_dump_state.c
+++ b/src/rufl_dump_state.c
@@ -9,16 +9,16 @@
#include "rufl_internal.h"
-static void rufl_dump_character_set(struct rufl_character_set *charset);
+static void rufl_dump_character_set_list(
+ const struct rufl_character_set *charset);
static void rufl_dump_unicode_map(struct rufl_unicode_map *umap);
-static void rufl_dump_substitution_table(void);
/**
* Dump the internal library state to stdout.
*/
-void rufl_dump_state(void)
+void rufl_dump_state(bool verbose)
{
unsigned int i, j;
@@ -27,12 +27,12 @@ void rufl_dump_state(void)
printf(" %u \"%s\"\n", i, rufl_font_list[i].identifier);
if (rufl_font_list[i].charset) {
printf(" ");
- rufl_dump_character_set(rufl_font_list[i].charset);
+ rufl_dump_character_set_list(rufl_font_list[i].charset);
printf("\n");
} else {
printf(" (no charset table)\n");
}
- if (rufl_font_list[i].umap) {
+ if (verbose && rufl_font_list[i].umap) {
for (j = 0; j < rufl_font_list[i].num_umaps; j++) {
struct rufl_unicode_map *map =
rufl_font_list[i].umap + j;
@@ -65,7 +65,7 @@ void rufl_dump_state(void)
}
printf("rufl_substitution_table:\n");
- rufl_dump_substitution_table();
+ rufl_substitution_table_dump();
}
@@ -75,28 +75,45 @@ void rufl_dump_state(void)
* \param charset character set to print
*/
-void rufl_dump_character_set(struct rufl_character_set *charset)
+static void rufl_dump_character_set(const struct rufl_character_set *charset)
{
- unsigned int u, t;
+ unsigned int u, t, plane = PLANE_ID(charset->metadata) << 16;
u = 0;
while (u != 0x10000) {
- while (u != 0x10000 && !rufl_character_set_test(charset, u))
+ while (u != 0x10000 &&
+ !rufl_character_set_test(charset, plane + u))
u++;
if (u != 0x10000) {
- if (!rufl_character_set_test(charset, u + 1)) {
- printf("%x ", u);
+ if (!rufl_character_set_test(charset, plane + u + 1)) {
+ printf("%x ", plane + u);
u++;
} else {
t = u;
- while (rufl_character_set_test(charset, u))
+ while (u != 0x10000 && rufl_character_set_test(
+ charset, plane + u))
u++;
- printf("%x-%x ", t, u - 1);
+ printf("%x-%x ", plane + t, plane + u - 1);
}
}
}
}
+/**
+ * Dump a representation of a character set list to stdout.
+ *
+ * \param charset character set to print
+ */
+
+void rufl_dump_character_set_list(const struct rufl_character_set *charset)
+{
+ while (EXTENSION_FOLLOWS(charset->metadata)) {
+ rufl_dump_character_set(charset);
+ charset = (void *)(((uint8_t *)charset) +
+ PLANE_SIZE(charset->metadata));
+ }
+ rufl_dump_character_set(charset);
+}
/**
* Dump a representation of a unicode map to stdout.
@@ -114,25 +131,3 @@ void rufl_dump_unicode_map(struct rufl_unicode_map *umap)
for (i = 0; i != umap->entries; i++)
printf("%x:%x ", umap->map[i].u, umap->map[i].c);
}
-
-
-/**
- * Dump a representation of the substitution table to stdout.
- */
-
-void rufl_dump_substitution_table(void)
-{
- unsigned int font;
- unsigned int u, t;
-
- u = 0;
- while (u != 0x10000) {
- t = u;
- font = rufl_substitution_table[t];
- while (u != 0x10000 && font == rufl_substitution_table[u])
- u++;
- if (font != NOT_AVAILABLE)
- printf(" %x-%x => %u \"%s\"\n", t, u - 1,
- font, rufl_font_list[font].identifier);
- }
-}
diff --git a/src/rufl_init.c b/src/rufl_init.c
index b441edc..a4fe618 100644
--- a/src/rufl_init.c
+++ b/src/rufl_init.c
@@ -14,6 +14,8 @@
#include <string.h>
#include <strings.h>
#include <search.h>
+#include <sys/stat.h>
+#include <sys/types.h>
#include <oslib/font.h>
#include <oslib/hourglass.h>
#include <oslib/os.h>
@@ -23,20 +25,30 @@
#include <oslib/wimpreadsysinfo.h>
#include "rufl_internal.h"
+/* Both of the below options are currently disabled as we only parse
+ * Encoding files when using a non-UCS FontManager. */
+/* Enable support for /uniXXXX and /uXXXX[XXXX] form glyph "names" */
+#define SUPPORT_UCS_GLYPH_NAMES 0
+/* Enable support for parsing UCS FM sparse encoding files */
+#define SUPPORT_UCS_SPARSE_ENCODING 0
-struct rufl_font_list_entry *rufl_font_list = 0;
+struct rufl_font_list_entry *rufl_font_list = NULL;
size_t rufl_font_list_entries = 0;
-const char **rufl_family_list = 0;
-unsigned int rufl_family_list_entries = 0;
-struct rufl_family_map_entry *rufl_family_map = 0;
-os_error *rufl_fm_error = 0;
-void *rufl_family_menu = 0;
-unsigned short *rufl_substitution_table = 0;
+const char **rufl_family_list = NULL;
+size_t rufl_family_list_entries = 0;
+struct rufl_family_map_entry *rufl_family_map = NULL;
+os_error *rufl_fm_error = NULL;
+void *rufl_family_menu = NULL;
struct rufl_cache_entry rufl_cache[rufl_CACHE_SIZE];
-int rufl_cache_time = 0;
+uint32_t rufl_cache_time = 0;
bool rufl_old_font_manager = false;
+static bool rufl_broken_font_enumerate_characters = false;
wimp_w rufl_status_w = 0;
char rufl_status_buffer[80];
+#if 1 /* ndef NDEBUG */
+bool rufl_log_got_start_time;
+time_t rufl_log_start_time;
+#endif
/** An entry in rufl_weight_table. */
struct rufl_weight_table_entry {
@@ -58,11 +70,13 @@ const struct rufl_weight_table_entry rufl_weight_table[] = {
{ "ExtraLight", 1 },
{ "Heavy", 8 },
{ "Light", 2 },
+ { "MedBold", 6 },
{ "Medium", 5 },
{ "Regular", 4 },
{ "Semi", 6 },
{ "SemiBold", 6 },
{ "SemiLight", 3 },
+ { "Thin", 1 },
{ "UltraBlack", 9 },
{ "UltraBold", 9 },
};
@@ -73,17 +87,19 @@ static rufl_code rufl_init_add_font(const char *identifier,
const char *local_name);
static int rufl_weight_table_cmp(const void *keyval, const void *datum);
static rufl_code rufl_init_scan_font(unsigned int font);
-static rufl_code rufl_init_scan_font_no_enumerate(unsigned int font);
static bool rufl_is_space(unsigned int u);
static rufl_code rufl_init_scan_font_old(unsigned int font_index);
static rufl_code rufl_init_scan_font_in_encoding(const char *font_name,
const char *encoding, struct rufl_character_set *charset,
struct rufl_unicode_map *umap, unsigned int *last);
-static rufl_code rufl_init_read_encoding(font_f font,
+static rufl_code rufl_init_populate_unicode_map(font_f f,
struct rufl_unicode_map *umap);
+static rufl_code rufl_init_read_encoding(font_f font,
+ rufl_code (*callback)(void *pw,
+ uint32_t glyph_idx, uint32_t ucs4),
+ void *pw);
static int rufl_glyph_map_cmp(const void *keyval, const void *datum);
static int rufl_unicode_map_cmp(const void *z1, const void *z2);
-static rufl_code rufl_init_substitution_table(void);
static rufl_code rufl_save_cache(void);
static rufl_code rufl_load_cache(void);
static int rufl_font_list_cmp(const void *keyval, const void *datum);
@@ -101,7 +117,6 @@ static void rufl_init_status_close(void);
rufl_code rufl_init(void)
{
- bool rufl_broken_font_enumerate_characters = false;
unsigned int changes = 0;
unsigned int i;
int fm_version;
@@ -175,7 +190,7 @@ rufl_code rufl_init(void)
xhourglass_off();
return code;
}
- LOG("%zu faces, %u families", rufl_font_list_entries,
+ LOG("%zu faces, %zu families", rufl_font_list_entries,
rufl_family_list_entries);
code = rufl_load_cache();
@@ -198,8 +213,6 @@ rufl_code rufl_init(void)
(float) i / rufl_font_list_entries);
if (rufl_old_font_manager)
code = rufl_init_scan_font_old(i);
- else if (rufl_broken_font_enumerate_characters)
- code = rufl_init_scan_font_no_enumerate(i);
else
code = rufl_init_scan_font(i);
if (code != rufl_OK) {
@@ -213,9 +226,9 @@ rufl_code rufl_init(void)
xhourglass_leds(2, 0, 0);
xhourglass_colours(0x0000ff, 0x00ffff, &old_sand, &old_glass);
- code = rufl_init_substitution_table();
+ code = rufl_substitution_table_init();
if (code != rufl_OK) {
- LOG("rufl_init_substitution_table: 0x%x", code);
+ LOG("rufl_substitution_table_init: 0x%x", code);
rufl_quit();
xhourglass_off();
return code;
@@ -264,7 +277,9 @@ rufl_code rufl_init_font_list(void)
font_list_context context = 0;
char identifier[80], local_name[80];
- while (context != -1) {
+ /* Permit up to 65535 font faces (we rely on 16bits of storage
+ * being sufficient in the substitution tables. */
+ while (context != -1 && rufl_font_list_entries < UINT16_MAX) {
/* read identifier */
rufl_fm_error = xfont_list_fonts((byte *)identifier,
font_RETURN_FONT_NAME |
@@ -351,8 +366,9 @@ rufl_code rufl_init_add_font(const char *identifier, const char *local_name)
rufl_font_list[rufl_font_list_entries].identifier = strdup(identifier);
if (!rufl_font_list[rufl_font_list_entries].identifier)
return rufl_OUT_OF_MEMORY;
- rufl_font_list[rufl_font_list_entries].charset = 0;
- rufl_font_list[rufl_font_list_entries].umap = 0;
+ rufl_font_list[rufl_font_list_entries].charset = NULL;
+ rufl_font_list[rufl_font_list_entries].umap = NULL;
+ rufl_font_list[rufl_font_list_entries].num_umaps = 0;
rufl_font_list_entries++;
/* determine family, weight, and slant */
@@ -436,47 +452,163 @@ int rufl_weight_table_cmp(const void *keyval, const void *datum)
return strcasecmp(key, entry->name);
}
-/**
- * Scan a font for available characters.
- */
+static struct rufl_character_set *rufl_init_alloc_plane(uint8_t index)
+{
+ struct rufl_character_set *charset;
+ unsigned int u;
-rufl_code rufl_init_scan_font(unsigned int font_index)
+ charset = calloc(1, sizeof *charset);
+ if (charset != NULL) {
+ /* Set plane ID. Extension/size must be filled in by caller */
+ charset->metadata |= ((index & 0x1f) << 26);
+ for (u = 0; u != 256; u++)
+ charset->index[u] = BLOCK_EMPTY;
+ }
+
+ return charset;
+}
+
+static void rufl_init_shrinkwrap_plane(struct rufl_character_set *charset)
+{
+ unsigned int last_used = PLANE_SIZE(charset->metadata);
+ unsigned int u, bit, byte;
+
+ /* Determine which blocks are full, and mark them as such */
+ for (u = 0; u != 256; u++) {
+ const unsigned int block = charset->index[u];
+
+ if (block == BLOCK_EMPTY)
+ continue;
+
+ bit = 0xff;
+
+ for (byte = 0; byte != 32; byte++)
+ bit &= charset->block[block][byte];
+
+
+ if (bit != 0xff)
+ continue;
+
+ /* Block is full */
+
+ /* Move subsequent blocks up and rewrite their indices */
+ memmove(charset->block[block],
+ charset->block[block+1],
+ (254-(block+1)) * 32);
+ for (byte = 0; byte != 256; byte++) {
+ if (charset->index[byte] < BLOCK_EMPTY &&
+ charset->index[byte] > block) {
+ charset->index[byte]--;
+ }
+ }
+
+ /* Now mark this block as full */
+ charset->index[u] = BLOCK_FULL;
+ last_used--;
+ }
+
+ /* Fill in this plane's size now we know it */
+ charset->metadata = (charset->metadata & 0xffff0000) |
+ (offsetof(struct rufl_character_set, block) +
+ 32 * last_used);
+}
+
+static struct rufl_character_set *rufl_init_shrinkwrap_planes(
+ struct rufl_character_set *planes[17])
{
- char font_name[80];
- int x_out, y_out;
- unsigned int byte, bit;
- unsigned int last_used = 0;
- unsigned int string[2] = { 0, 0 };
- unsigned int u, next;
struct rufl_character_set *charset;
- struct rufl_character_set *charset2;
- font_f font;
- font_scan_block block = { { 0, 0 }, { 0, 0 }, -1, { 0, 0, 0, 0 } };
+ unsigned int size = 0, pos, u;
- /*LOG("font %u \"%s\"", font_index,
- rufl_font_list[font_index].identifier);*/
+ /* Shrink-wrap each plane, accumulating total required size as we go */
+ for (u = 0; u < 17; u++) {
+ if (planes[u]) {
+ rufl_init_shrinkwrap_plane(planes[u]);
+ size += PLANE_SIZE(planes[u]->metadata);
+ }
+ }
- charset = calloc(1, sizeof *charset);
+ charset = malloc(size);
if (!charset)
- return rufl_OUT_OF_MEMORY;
- for (u = 0; u != 256; u++)
- charset->index[u] = BLOCK_EMPTY;
+ return NULL;
- snprintf(font_name, sizeof font_name, "%s\\EUTF8",
- rufl_font_list[font_index].identifier);
+ /* Copy planes into output, backwards, setting the extension bit for
+ * all but the last plane present */
+ pos = size;
+ for (u = 17; u > 0; u--) {
+ if (!planes[u-1])
+ continue;
- rufl_fm_error = xfont_find_font(font_name, 160, 160, 0, 0, &font, 0, 0);
- if (rufl_fm_error) {
- LOG("xfont_find_font(\"%s\"): 0x%x: %s", font_name,
- rufl_fm_error->errnum, rufl_fm_error->errmess);
- free(charset);
- return rufl_OK;
+ /* Set E bit if not the last plane */
+ if (pos != size)
+ planes[u-1]->metadata |= (1u<<31);
+
+ pos -= PLANE_SIZE(planes[u-1]->metadata);
+ memcpy(((uint8_t *)charset) + pos, planes[u-1],
+ PLANE_SIZE(planes[u-1]->metadata));
}
- /* Scan through mapped characters */
- for (u = 0; u != (unsigned int) -1; u = next) {
- unsigned int internal;
+ return charset;
+}
+
+static rufl_code rufl_init_enumerate_characters(const char *font_name,
+ font_f font,
+ rufl_code (*callback)(void *pw,
+ uint32_t glyph_idx, uint32_t ucs4),
+ void *pw)
+{
+ unsigned int u = 0, next, internal;
+ rufl_code result = rufl_OK;
+
+ if (rufl_broken_font_enumerate_characters) {
+ /* We know that any codepoints in the first chunk will
+ * be missed because Font_EnumerateCharacters is broken
+ * on this version of the Font Manager. Find the first
+ * codepoint it will report. */
+ unsigned int first;
+ rufl_fm_error = xfont_enumerate_characters(font, 0,
+ (int *) &first, (int *) &internal);
+ if (rufl_fm_error) {
+ LOG("xfont_enumerate_characters(\"%s\", "
+ "U+%x, ...): 0x%x: %s",
+ font_name, 0,
+ rufl_fm_error->errnum,
+ rufl_fm_error->errmess);
+ return rufl_FONT_MANAGER_ERROR;
+ }
+ if (first == (unsigned int) -1) {
+ /* Font has no defined characters */
+ return rufl_OK;
+ }
+
+ /* Search the entire space up to the first codepoint it
+ * reported. */
+ for (u = 1; u != first; u++) {
+ rufl_fm_error = xfont_enumerate_characters(font, u,
+ (int *) &next, (int *) &internal);
+ if (rufl_fm_error) {
+ LOG("xfont_enumerate_characters(\"%s\", "
+ "U+%x, ...): 0x%x: %s",
+ font_name, u,
+ rufl_fm_error->errnum,
+ rufl_fm_error->errmess);
+ return rufl_FONT_MANAGER_ERROR;
+ }
+
+ /* Skip unmapped characters */
+ if (internal == (unsigned int) -1)
+ continue;
+
+ /* Character is mapped, emit it */
+ result = callback(pw, internal, u);
+ if (result != rufl_OK)
+ return result;
+ }
+ /* Now fall through to the normal path */
+ }
+
+ /* Scan through mapped characters */
+ for (; u != (unsigned int) -1; u = next) {
rufl_fm_error = xfont_enumerate_characters(font, u,
(int *) &next, (int *) &internal);
if (rufl_fm_error) {
@@ -485,133 +617,134 @@ rufl_code rufl_init_scan_font(unsigned int font_index)
font_name, u,
rufl_fm_error->errnum,
rufl_fm_error->errmess);
- xfont_lose_font(font);
- free(charset);
- return rufl_OK;
+ result = rufl_FONT_MANAGER_ERROR;
+ break;
}
- /* Skip DELETE and C0/C1 controls */
- if (u < 0x0020 || (0x007f <= u && u <= 0x009f))
- continue;
-
- /* Skip astral characters */
- if (u > 0xffff)
- continue;
-
/* Skip unmapped characters */
if (internal == (unsigned int) -1)
continue;
- if (u % 0x200 == 0)
- rufl_init_status(0, 0);
-
- /* Character is mapped, let's see if it's really there */
- string[0] = u;
- rufl_fm_error = xfont_scan_string(font, (char *) string,
- font_RETURN_BBOX | font_GIVEN32_BIT |
- font_GIVEN_FONT | font_GIVEN_LENGTH |
- font_GIVEN_BLOCK,
- 0x7fffffff, 0x7fffffff,
- &block, 0, 4,
- 0, &x_out, &y_out, 0);
- if (rufl_fm_error)
+ /* Character is mapped, emit it */
+ result = callback(pw, internal, u);
+ if (result != rufl_OK)
break;
+ }
- if (block.bbox.x0 == 0x20000000) {
- /* absent (no definition) */
- } else if (x_out == 0 && y_out == 0 &&
- block.bbox.x0 == 0 && block.bbox.y0 == 0 &&
- block.bbox.x1 == 0 && block.bbox.y1 == 0) {
- /* absent (empty) */
- } else if (block.bbox.x0 == 0 && block.bbox.y0 == 0 &&
- block.bbox.x1 == 0 && block.bbox.y1 == 0 &&
- !rufl_is_space(u)) {
- /* absent (space but not a space character - some
- * fonts do this) */
- } else {
- /* present */
- if (charset->index[u >> 8] == BLOCK_EMPTY) {
- charset->index[u >> 8] = last_used;
- last_used++;
- if (last_used == 254)
- /* too many characters */
- break;
- }
+ return result;
+}
- byte = (u >> 3) & 31;
- bit = u & 7;
- charset->block[charset->index[u >> 8]][byte] |=
- 1 << bit;
- }
- }
+static rufl_code find_plane_cb(void *pw, uint32_t glyph_idx, uint32_t ucs4)
+{
+ struct rufl_character_set **planes = pw;
- xfont_lose_font(font);
+ (void) glyph_idx;
+
+ /* Skip DELETE and C0/C1 controls */
+ if (ucs4 > 0x0020 && (ucs4 < 0x007f || 0x009f < ucs4))
+ planes[(ucs4 >> 16) & 0x1f] = (struct rufl_character_set *) 1;
+
+ return rufl_OK;
+}
+
+struct find_glyph_ctx {
+ const char *font_name;
+ font_f font;
+ struct rufl_character_set **planes;
+};
+
+static rufl_code find_glyph_cb(void *pw, uint32_t glyph_idx, uint32_t ucs4)
+{
+ struct find_glyph_ctx *ctx = pw;
+ int x_out, y_out;
+ unsigned int string[2] = { 0, 0 };
+ font_scan_block block = { { 0, 0 }, { 0, 0 }, -1, { 0, 0, 0, 0 } };
+
+ (void) glyph_idx;
+
+ /* Skip DELETE and C0/C1 controls */
+ if (ucs4 < 0x0020 || (0x007f <= ucs4 && ucs4 <= 0x009f))
+ return rufl_OK;
+ if (ucs4 % 0x200 == 0)
+ rufl_init_status(0, 0);
+
+ string[0] = ucs4;
+ rufl_fm_error = xfont_scan_string(ctx->font, (char *) string,
+ font_RETURN_BBOX | font_GIVEN32_BIT |
+ font_GIVEN_FONT | font_GIVEN_LENGTH |
+ font_GIVEN_BLOCK,
+ 0x7fffffff, 0x7fffffff,
+ &block, 0, 4,
+ 0, &x_out, &y_out, 0);
if (rufl_fm_error) {
- free(charset);
LOG("xfont_scan_string(\"%s\", U+%x, ...): 0x%x: %s",
- font_name, u,
+ ctx->font_name, ucs4,
rufl_fm_error->errnum, rufl_fm_error->errmess);
return rufl_FONT_MANAGER_ERROR;
}
- /* Determine which blocks are full, and mark them as such */
- for (u = 0; u != 256; u++) {
- if (charset->index[u] == BLOCK_EMPTY)
- continue;
-
- bit = 0xff;
-
- for (byte = 0; byte != 32; byte++)
- bit &= charset->block[u][byte];
-
- if (bit == 0xff) {
- /* Block is full */
- charset->index[u] = BLOCK_FULL;
-
- for (byte = 0; byte != 32; byte++)
- charset->block[u][byte] = 0;
+ if (block.bbox.x0 == 0x20000000) {
+ /* absent (no definition) */
+ } else if (x_out == 0 && y_out == 0 &&
+ block.bbox.x0 == 0 && block.bbox.y0 == 0 &&
+ block.bbox.x1 == 0 && block.bbox.y1 == 0) {
+ /* absent (empty) */
+ } else if (block.bbox.x0 == 0 && block.bbox.y0 == 0 &&
+ block.bbox.x1 == 0 && block.bbox.y1 == 0 &&
+ !rufl_is_space(ucs4)) {
+ /* absent (space but not a space character - some
+ * fonts do this) */
+ } else {
+ /* present */
+ const unsigned int plane = (ucs4 >> 16) & 0x1f;
+ const unsigned int blk = (ucs4 >> 8) & 0xff;
+ const unsigned int byte = (ucs4 >> 3) & 31;
+ const unsigned int bit = ucs4 & 7;
+
+ /* Allocate block, if it's currently empty */
+ if (ctx->planes[plane]->index[blk] == BLOCK_EMPTY) {
+ unsigned int last_used =
+ PLANE_SIZE(ctx->planes[plane]->metadata);
+ if (last_used < BLOCK_EMPTY) {
+ ctx->planes[plane]->index[blk] = last_used;
+ ctx->planes[plane]->metadata =
+ (ctx->planes[plane]->metadata &
+ 0xffff0000) |
+ (last_used + 1);
+ }
}
- }
- /* shrink-wrap */
- charset->size = offsetof(struct rufl_character_set, block) +
- 32 * last_used;
- charset2 = realloc(charset, charset->size);
- if (!charset2) {
- free(charset);
- return rufl_OUT_OF_MEMORY;
+ /* Set bit for codepoint in bitmap, if bitmap exists */
+ if (ctx->planes[plane]->index[blk] < BLOCK_EMPTY) {
+ ctx->planes[plane]->block[
+ ctx->planes[plane]->index[blk]
+ ][byte] |= 1 << bit;
+ }
}
- rufl_font_list[font_index].charset = charset;
-
return rufl_OK;
}
/**
- * Scan a font for available characters (version without character enumeration)
+ * Scan a font for available characters
*/
-rufl_code rufl_init_scan_font_no_enumerate(unsigned int font_index)
+rufl_code rufl_init_scan_font(unsigned int font_index)
{
char font_name[80];
- int x_out, y_out;
- unsigned int byte, bit;
- unsigned int block_count = 0;
- unsigned int last_used = 0;
- unsigned int string[2] = { 0, 0 };
- unsigned int u;
+ struct rufl_character_set *planes[17];
struct rufl_character_set *charset;
- struct rufl_character_set *charset2;
font_f font;
- font_scan_block block = { { 0, 0 }, { 0, 0 }, -1, { 0, 0, 0, 0 } };
+ unsigned int plane;
+ struct find_glyph_ctx ctx;
+ rufl_code rc;
/*LOG("font %u \"%s\"", font_index,
rufl_font_list[font_index].identifier);*/
- charset = calloc(1, sizeof *charset);
- if (!charset)
- return rufl_OUT_OF_MEMORY;
+ for (plane = 0; plane < 17; plane++)
+ planes[plane] = NULL;
snprintf(font_name, sizeof font_name, "%s\\EUTF8",
rufl_font_list[font_index].identifier);
@@ -620,90 +753,55 @@ rufl_code rufl_init_scan_font_no_enumerate(unsigned int font_index)
if (rufl_fm_error) {
LOG("xfont_find_font(\"%s\"): 0x%x: %s", font_name,
rufl_fm_error->errnum, rufl_fm_error->errmess);
- free(charset);
return rufl_OK;
}
- /* scan through all characters */
- for (u = 0x0020; u != 0x10000; u++) {
- if (u == 0x007f) {
- /* skip DELETE and C1 controls */
- u = 0x009f;
- continue;
- }
-
- if (u % 0x200 == 0)
- rufl_init_status(0, 0);
-
- string[0] = u;
- rufl_fm_error = xfont_scan_string(font, (char *) string,
- font_RETURN_BBOX | font_GIVEN32_BIT |
- font_GIVEN_FONT | font_GIVEN_LENGTH |
- font_GIVEN_BLOCK,
- 0x7fffffff, 0x7fffffff,
- &block, 0, 4,
- 0, &x_out, &y_out, 0);
- if (rufl_fm_error)
- break;
-
- if (block.bbox.x0 == 0x20000000) {
- /* absent (no definition) */
- } else if (x_out == 0 && y_out == 0 &&
- block.bbox.x0 == 0 && block.bbox.y0 == 0 &&
- block.bbox.x1 == 0 && block.bbox.y1 == 0) {
- /* absent (empty) */
- } else if (block.bbox.x0 == 0 && block.bbox.y0 == 0 &&
- block.bbox.x1 == 0 && block.bbox.y1 == 0 &&
- !rufl_is_space(u)) {
- /* absent (space but not a space character - some
- * fonts do this) */
- } else {
- /* present */
- byte = (u >> 3) & 31;
- bit = u & 7;
- charset->block[last_used][byte] |= 1 << bit;
+ /* First pass: find the planes we need */
+ rc = rufl_init_enumerate_characters(font_name, font,
+ find_plane_cb, planes);
+ if (rc != rufl_OK)
+ return rc;
- block_count++;
- }
+ /* Allocate the planes */
+ for (plane = 0; plane < 17; plane++) {
+ if (!planes[plane])
+ continue;
- if ((u + 1) % 256 == 0) {
- /* end of block */
- if (block_count == 0)
- charset->index[u >> 8] = BLOCK_EMPTY;
- else if (block_count == 256) {
- charset->index[u >> 8] = BLOCK_FULL;
- for (byte = 0; byte != 32; byte++)
- charset->block[last_used][byte] = 0;
- } else {
- charset->index[u >> 8] = last_used;
- last_used++;
- if (last_used == 254)
- /* too many characters */
- break;
- }
- block_count = 0;
+ planes[plane] = rufl_init_alloc_plane(plane);
+ if (!planes[plane]) {
+ while (plane > 0)
+ free(planes[plane-1]);
+ xfont_lose_font(font);
+ return rufl_OUT_OF_MEMORY;
}
}
- xfont_lose_font(font);
+ /* Second pass: populate the planes */
+ ctx.font_name = font_name;
+ ctx.font = font;
+ ctx.planes = planes;
- if (rufl_fm_error) {
- free(charset);
- LOG("xfont_scan_string(\"%s\", U+%x, ...): 0x%x: %s",
- font_name, u,
- rufl_fm_error->errnum, rufl_fm_error->errmess);
- return rufl_FONT_MANAGER_ERROR;
+ rc = rufl_init_enumerate_characters(font_name, font,
+ find_glyph_cb, &ctx);
+ if (rc != rufl_OK) {
+ for (plane = 0; plane < 17; plane++)
+ free(planes[plane]);
+ xfont_lose_font(font);
+ return rc;
}
- /* shrink-wrap */
- charset->size = offsetof(struct rufl_character_set, block) +
- 32 * last_used;
- charset2 = realloc(charset, charset->size);
- if (!charset2) {
- free(charset);
+ xfont_lose_font(font);
+
+ charset = rufl_init_shrinkwrap_planes(planes);
+ if (!charset) {
+ for (plane = 0; plane < 17; plane++)
+ free(planes[plane]);
return rufl_OUT_OF_MEMORY;
}
+ for (plane = 0; plane < 17; plane++)
+ free(planes[plane]);
+
rufl_font_list[font_index].charset = charset;
return rufl_OK;
@@ -723,6 +821,9 @@ bool rufl_is_space(unsigned int u)
/**
* Scan a font for available characters (old font manager version).
+ * By definition, no astral characters are supported when using a non-UCS
+ * Font Manager (font encodings are defined using PostScript glyph names
+ * which, per the Glyph list, can only fall in the Basic Multilingual Plane)
*/
rufl_code rufl_init_scan_font_old(unsigned int font_index)
@@ -785,7 +886,21 @@ rufl_code rufl_init_scan_font_old(unsigned int font_index)
code = rufl_init_scan_font_in_encoding(font_name, encoding,
charset, umap + (num_umaps - 1), &last_used);
- if (code != rufl_OK) {
+ /* Not finding the font isn't fatal */
+ if (code == rufl_FONT_MANAGER_ERROR &&
+ (rufl_fm_error->errnum ==
+ error_FONT_NOT_FOUND ||
+ rufl_fm_error->errnum ==
+ error_FILE_NOT_FOUND ||
+ rufl_fm_error->errnum ==
+ error_FONT_ENCODING_NOT_FOUND ||
+ /* Neither is a too modern font */
+ rufl_fm_error->errnum ==
+ error_FONT_TOO_MANY_CHUNKS)) {
+ /* Ensure we reuse the currently allocated umap */
+ num_umaps--;
+ rufl_fm_error = NULL;
+ } else if (code != rufl_OK) {
LOG("rufl_init_scan_font_in_encoding(\"%s\", \"%s\", "
"...): 0x%x (0x%x: %s)",
font_name, encoding, code,
@@ -794,23 +909,11 @@ rufl_code rufl_init_scan_font_old(unsigned int font_index)
code == rufl_FONT_MANAGER_ERROR ?
rufl_fm_error->errmess : "");
- /* Not finding the font isn't fatal */
- if (code != rufl_FONT_MANAGER_ERROR ||
- (rufl_fm_error->errnum !=
- error_FONT_NOT_FOUND &&
- rufl_fm_error->errnum !=
- error_FILE_NOT_FOUND &&
- rufl_fm_error->errnum !=
- error_FONT_ENCODING_NOT_FOUND)) {
- free(charset);
- for (i = 0; i < num_umaps; i++)
- free((umap + i)->encoding);
- free(umap);
- return code;
- }
-
- /* Ensure we reuse the currently allocated umap */
- num_umaps--;
+ free(charset);
+ for (i = 0; i < num_umaps; i++)
+ free((umap + i)->encoding);
+ free(umap);
+ return code;
} else {
/* If this mapping is identical to an existing one,
* then we can discard it */
@@ -823,6 +926,7 @@ rufl_code rufl_init_scan_font_old(unsigned int font_index)
memcmp(a->map, b->map,
sizeof a->map) == 0) {
/* Found identical map; discard */
+ free(b->encoding);
num_umaps--;
break;
}
@@ -849,7 +953,21 @@ rufl_code rufl_init_scan_font_old(unsigned int font_index)
code = rufl_init_scan_font_in_encoding(font_name, NULL,
charset, umap, &last_used);
- if (code != rufl_OK) {
+ /* Not finding the font isn't fatal */
+ if (code == rufl_FONT_MANAGER_ERROR &&
+ (rufl_fm_error->errnum ==
+ error_FONT_NOT_FOUND ||
+ rufl_fm_error->errnum ==
+ error_FILE_NOT_FOUND ||
+ rufl_fm_error->errnum ==
+ error_FONT_ENCODING_NOT_FOUND ||
+ /* Neither is a too modern font */
+ rufl_fm_error->errnum ==
+ error_FONT_TOO_MANY_CHUNKS)) {
+ /* Ensure we reuse the currently allocated umap */
+ num_umaps--;
+ rufl_fm_error = NULL;
+ } else if (code != rufl_OK) {
LOG("rufl_init_scan_font_in_encoding(\"%s\", NULL, "
"...): 0x%x (0x%x: %s)",
font_name, code,
@@ -858,22 +976,11 @@ rufl_code rufl_init_scan_font_old(unsigned int font_index)
code == rufl_FONT_MANAGER_ERROR ?
rufl_fm_error->errmess : "");
- /* Not finding the font isn't fatal */
- if (code != rufl_FONT_MANAGER_ERROR ||
- (rufl_fm_error->errnum !=
- error_FONT_NOT_FOUND &&
- rufl_fm_error->errnum !=
- error_FILE_NOT_FOUND &&
- rufl_fm_error->errnum !=
- error_FONT_ENCODING_NOT_FOUND)) {
- free(charset);
- for (i = 0; i < num_umaps; i++)
- free((umap + i)->encoding);
- free(umap);
- return code;
- }
-
- num_umaps--;
+ free(charset);
+ for (i = 0; i < num_umaps; i++)
+ free((umap + i)->encoding);
+ free(umap);
+ return code;
}
}
@@ -884,10 +991,12 @@ rufl_code rufl_init_scan_font_old(unsigned int font_index)
return rufl_OK;
}
- /* shrink-wrap */
- charset->size = offsetof(struct rufl_character_set, block) +
- 32 * last_used;
- charset2 = realloc(charset, charset->size);
+ /* Shrink-wrap. We only have the one plane so fill in last_used
+ * as expected by the shrinkwrapping helper and then resize the
+ * resulting charset manually. */
+ charset->metadata = (charset->metadata & 0xffff0000) | last_used;
+ rufl_init_shrinkwrap_plane(charset);
+ charset2 = realloc(charset, PLANE_SIZE(charset->metadata));
if (!charset2) {
for (i = 0; i < num_umaps; i++)
free((umap + i)->encoding);
@@ -896,7 +1005,7 @@ rufl_code rufl_init_scan_font_old(unsigned int font_index)
return rufl_OUT_OF_MEMORY;
}
- rufl_font_list[font_index].charset = charset;
+ rufl_font_list[font_index].charset = charset2;
rufl_font_list[font_index].umap = umap;
rufl_font_list[font_index].num_umaps = num_umaps;
@@ -930,19 +1039,41 @@ rufl_code rufl_init_scan_font_in_encoding(const char *font_name,
rufl_fm_error = xfont_find_font(buf, 160, 160, 0, 0, &font, 0, 0);
if (rufl_fm_error) {
- LOG("xfont_find_font(\"%s\"): 0x%x: %s", buf,
- rufl_fm_error->errnum, rufl_fm_error->errmess);
+ /* Leave it to our caller to log, if they wish */
return rufl_FONT_MANAGER_ERROR;
}
- code = rufl_init_read_encoding(font, umap);
+ code = rufl_init_populate_unicode_map(font, umap);
if (code != rufl_OK) {
LOG("rufl_init_read_encoding(\"%s\", ...): 0x%x",
buf, code);
+ umap->encoding = NULL;
xfont_lose_font(font);
return code;
}
+ /* Detect attempts to use UCS fonts with a non-UCS Font Manager.
+ * There is a bug in all known non-UCS Font Managers which is
+ * often triggered by scanning many fonts. The Font Manager will
+ * attempt to dereference a bogus pointer (at the start of
+ * getbbox_unscaled) and thus cause an abort in SVC mode.
+ * Fallout can be as (relatively) benign as the application
+ * crashing or escalate to an entire system freeze requiring
+ * a reset. As there are no "good" outcomes here, and we do
+ * not have a time machine to go back and fix long-ago released
+ * Font Managers, ensure we ignore UCS fonts here. */
+ if ((uintptr_t) umap->encoding > 256) {
+ static os_error err = {
+ error_FONT_TOO_MANY_CHUNKS, "Rejecting UCS font"};
+ LOG("%s", "Rejecting UCS font");
+ umap->encoding = NULL;
+ xfont_lose_font(font);
+ rufl_fm_error = &err;
+ return rufl_FONT_MANAGER_ERROR;
+ }
+ /* Eliminate all trace of our (ab)use of the encoding field */
+ umap->encoding = NULL;
+
for (i = 0; i != umap->entries; i++) {
u = umap->map[i].u;
string[0] = umap->map[i].c;
@@ -1003,27 +1134,184 @@ rufl_code rufl_init_scan_font_in_encoding(const char *font_name,
return rufl_OK;
}
+static rufl_code rufl_init_umap_cb(void *pw, uint32_t glyph_idx, uint32_t ucs4)
+{
+ struct rufl_unicode_map *umap = pw;
+ rufl_code result = rufl_OK;
+
+ /* Ignore first 32 character codes (these are control chars) */
+ if (glyph_idx > 31 && glyph_idx < 256 && umap->entries < 256 &&
+ ucs4 != (uint32_t) -1) {
+ umap->map[umap->entries].u = ucs4;
+ umap->map[umap->entries].c = glyph_idx;
+ umap->entries++;
+ }
+ /* Stash the total number of encoding file entries so that
+ * rufl_init_scan_font_in_encoding can detect the presence of a
+ * UCS font on a non-UCS capable system. It will clean up for us. */
+ umap->encoding = (void *) ((uintptr_t) (glyph_idx + 1));
+
+ return result;
+}
/**
- * Parse an encoding file and fill in a rufl_unicode_map.
+ * Populate a unicode map by parsing the font's encoding file
*/
-rufl_code rufl_init_read_encoding(font_f font,
+rufl_code rufl_init_populate_unicode_map(font_f f,
struct rufl_unicode_map *umap)
{
+ rufl_code result;
+
+ umap->entries = 0;
+
+ result = rufl_init_read_encoding(f, rufl_init_umap_cb, umap);
+ if (result == rufl_OK) {
+ /* sort by unicode */
+ qsort(umap->map, umap->entries, sizeof umap->map[0],
+ rufl_unicode_map_cmp);
+ }
+ return result;
+}
+
+#if SUPPORT_UCS_GLYPH_NAMES || SUPPORT_UCS_SPARSE_ENCODING
+static int fromhex(char val, bool permit_lc)
+{
+ if ('0' <= val && val <= '9')
+ return val - '0';
+ else if ('A' <= val && val <= 'F')
+ return val - 'A' + 10;
+ else if (permit_lc && 'a' <= val && val <= 'f')
+ return val - 'a' + 10;
+ return -1;
+}
+#endif
+
+static rufl_code emit_codepoint(char s[200], unsigned int i,
+ rufl_code (*callback)(void *pw,
+ uint32_t glyph_idx, uint32_t ucs4),
+ void *pw)
+{
+ struct rufl_glyph_map_entry *entry;
+ rufl_code result = rufl_OK;
+
+#if SUPPORT_UCS_SPARSE_ENCODING
+ if (s[0] != '/') {
+ /* Sparse encoding entry: [XX;]XXXX;NNN..;.... */
+ uint32_t val = 0;
+ int digits;
+
+ if (s[2] == ';' && fromhex(s[0], true) != -1 &&
+ fromhex(s[1], true) != -1) {
+ /* Skip leading "XX;" */
+ s += 3;
+ }
+
+ for (digits = 0; digits < 4; digits++) {
+ int nibble = fromhex(s[digits], true);
+ if (nibble == -1)
+ break;
+ val = (val << 4) | nibble;
+ }
+
+ /* Bail out if the data is not what we expect */
+ if (digits != 4 || s[digits] != ';')
+ return result;
+
+ /* Set the glyph index to the value we found */
+ i = val;
+ /* Advance s to the start of the glyph name */
+ s += digits + 1;
+ /* Terminate the glyph name */
+ for (digits = 0; s[digits] != '\0'; digits++) {
+ if (s[digits] == ';') {
+ s[digits] = '\0';
+ break;
+ }
+ }
+ /* Fall through to the glyph name search */
+ } else
+#endif
+ {
+ /* Skip the leading / */
+ s += 1;
+
+#if SUPPORT_UCS_GLYPH_NAMES
+ if (!rufl_old_font_manager && s[0] == 'u') {
+ /* Handle /uniXXXX and /uXXXX - /uXXXXXXXX.
+ * In the case of /uXXXXX - /uXXXXXXXX, no
+ * leading zeroes are permitted. */
+ int max_digits = 8, off = 1, digits = 0;
+ bool leading_zero = false;
+ uint32_t val = 0;
+
+ if (s[1] == 'n' && s[2] == 'i') {
+ max_digits = 4;
+ off = 3;
+ }
+
+ while (digits < max_digits) {
+ int nibble = fromhex(s[off], false);
+ if (nibble == -1)
+ break;
+ leading_zero = (digits == 0 && nibble == 0);
+ val = (val << 4) | nibble;
+ off++;
+ digits++;
+ }
+ if ((digits == 4 && s[off] == '\0') ||
+ (digits > 4 && s[off] == '\0' &&
+ !leading_zero)) {
+ return callback(pw, i, val);
+ }
+
+ /* Otherwise, let the glyph name search decide */
+ }
+#endif
+ }
+
+ entry = bsearch(s, rufl_glyph_map,
+ rufl_glyph_map_size,
+ sizeof rufl_glyph_map[0],
+ rufl_glyph_map_cmp);
+ if (entry) {
+ /* may be more than one unicode for the glyph
+ * sentinels stop overshooting array */
+ while (strcmp(s, (entry - 1)->glyph_name) == 0)
+ entry--;
+ for (; strcmp(s, entry->glyph_name) == 0; entry++) {
+ result = callback(pw, i, entry->u);
+ if (result != rufl_OK)
+ break;
+ }
+ } else {
+ /* No mapping: inform callback in case it cares */
+ result = callback(pw, i, (uint32_t) -1);
+ }
+
+ return result;
+}
+
+/**
+ * Parse an encoding file
+ */
+
+rufl_code rufl_init_read_encoding(font_f font,
+ rufl_code (*callback)(void *pw,
+ uint32_t glyph_idx, uint32_t ucs4),
+ void *pw)
+{
enum {
STATE_START,
STATE_COMMENT,
STATE_COLLECT,
} state = STATE_START;
- bool emit = false;
- unsigned int u = 0;
+ bool emit = false, done = false;
unsigned int i = 0;
unsigned int n = 0;
int c;
char filename[200];
char s[200];
- struct rufl_glyph_map_entry *entry;
FILE *fp;
rufl_fm_error = xfont_read_encoding_filename(font, filename,
@@ -1035,19 +1323,31 @@ rufl_code rufl_init_read_encoding(font_f font,
}
fp = fopen(filename, "r");
- if (!fp)
- /* many "symbol" fonts have no encoding file: assume Latin 1 */
+ if (!fp && rufl_old_font_manager) {
+ /* many "symbol" fonts have no encoding file */
fp = fopen("Resources:$.Fonts.Encodings.Latin1", "r");
+ }
if (!fp)
return rufl_IO_ERROR;
- while (!feof(fp) && i < 256 && u < 256) {
+ while (!feof(fp) && !done) {
c = fgetc(fp);
if (state == STATE_START) {
if (c == '/') {
- n = 0;
+ s[0] = c;
+ n = 1;
state = STATE_COLLECT;
+#if SUPPORT_UCS_SPARSE_ENCODING
+ } else if (!rufl_old_font_manager &&
+ (('0' <= c && c <= '9') ||
+ ('A' <= c && c <= 'F') ||
+ ('a' <= c && c <= 'f'))) {
+ /* New-style sparse encoding file */
+ s[0] = c;
+ n = 1;
+ state = STATE_COLLECT;
+#endif
} else if (c <= 0x20) {
/* Consume C0 and space */
} else {
@@ -1063,8 +1363,10 @@ rufl_code rufl_init_read_encoding(font_f font,
if ((c >= '0' && c <= '9') ||
(c >= 'a' && c <= 'z') ||
(c >= 'A' && c <= 'Z') ||
- (c == '.') || (c == '_')) {
- /* Printable: append */
+ (c == '.') || (c == '_') ||
+ (c == ';') ||
+ (c == ' ' && s[0] != '/')) {
+ /* Printable (or space in new-style): append */
s[n++] = c;
if (n >= sizeof(s)) {
/* Too long: garbage */
@@ -1083,41 +1385,17 @@ rufl_code rufl_init_read_encoding(font_f font,
}
}
- /* Ignore first 32 character codes (these are control chars) */
- if (emit && i > 31 && i < 256 && u < 256) {
- entry = bsearch(s, rufl_glyph_map,
- rufl_glyph_map_size,
- sizeof rufl_glyph_map[0],
- rufl_glyph_map_cmp);
- if (entry) {
- /* may be more than one unicode for the glyph
- * sentinels stop overshooting array */
- while (strcmp(s, (entry - 1)->glyph_name) == 0)
- entry--;
- for (; strcmp(s, entry->glyph_name) == 0;
- entry++) {
- umap->map[u].u = entry->u;
- umap->map[u].c = i;
- u++;
- if (u == 256)
- break;
- }
- }
- }
-
if (emit) {
- i++;
emit = false;
+ if (emit_codepoint(s, i, callback, pw) != rufl_OK)
+ done = true;
+ i++;
}
}
if (fclose(fp) == EOF)
return rufl_IO_ERROR;
- /* sort by unicode */
- qsort(umap->map, u, sizeof umap->map[0], rufl_unicode_map_cmp);
- umap->entries = u;
-
return rufl_OK;
}
@@ -1142,65 +1420,51 @@ int rufl_unicode_map_cmp(const void *z1, const void *z2)
}
-/**
- * Construct the font substitution table.
- */
-
-rufl_code rufl_init_substitution_table(void)
+static FILE *rufl_open_cache(const char *mode)
{
- unsigned char z;
- unsigned int i;
- unsigned int block, byte, bit;
- unsigned int u;
- unsigned int index;
- const struct rufl_character_set *charset;
-
- rufl_substitution_table = malloc(65536 *
- sizeof rufl_substitution_table[0]);
- if (!rufl_substitution_table) {
- LOG("malloc(%zu) failed", 65536 *
- sizeof rufl_substitution_table[0]);
- return rufl_OUT_OF_MEMORY;
- }
-
- for (u = 0; u != 0x10000; u++)
- rufl_substitution_table[u] = NOT_AVAILABLE;
+ const unsigned int version = rufl_CACHE_VERSION;
+ size_t len;
+ FILE *fp;
+ char fn[PATH_MAX];
+
+ if (!mode)
+ return NULL;
+
+ strcpy(fn, rufl_CACHE_TEMPLATE);
+ len = strlen(fn);
+
+ /* Fill in version suffix */
+ fn[len-4] = "0123456789abcdef"[(version>>12) & 0xf];
+ fn[len-3] = "0123456789abcdef"[(version>> 8) & 0xf];
+ fn[len-2] = "0123456789abcdef"[(version>> 4) & 0xf];
+ fn[len-1] = "0123456789abcdef"[version & 0xf];
+
+ if (mode[0] == 'a' || mode[0] == 'w') {
+ /* Wind back to directory separator */
+ while (len > 0 && fn[len] != '.')
+ len--;
+ if (len == 0) {
+ LOG("%s", "Malformed cache location");
+ return NULL;
+ }
- for (i = 0; i != rufl_font_list_entries; i++) {
- charset = rufl_font_list[i].charset;
- if (!charset)
- continue;
- for (block = 0; block != 256; block++) {
- if (charset->index[block] == BLOCK_EMPTY)
- continue;
- if (charset->index[block] == BLOCK_FULL) {
- for (u = block << 8; u != (block << 8) + 256;
- u++) {
- if (rufl_substitution_table[u] ==
- NOT_AVAILABLE)
- rufl_substitution_table[u] = i;
- }
- continue;
- }
- index = charset->index[block];
- for (byte = 0; byte != 32; byte++) {
- z = charset->block[index][byte];
- if (z == 0)
- continue;
- u = (block << 8) | (byte << 3);
- for (bit = 0; bit != 8; bit++, u++) {
- if (rufl_substitution_table[u] ==
- NOT_AVAILABLE &&
- z & (1 << bit))
- rufl_substitution_table[u] = i;
- }
- }
+ /* Ensure directory exists */
+ fn[len] = '\0';
+ if (mkdir(fn, 0755) == -1 && errno != EEXIST) {
+ LOG("mkdir: 0x%x: %s", errno, strerror(errno));
+ return NULL;
}
+ fn[len] = '.';
}
- return rufl_OK;
-}
+ fp = fopen(fn, mode);
+ if (!fp) {
+ LOG("fopen: 0x%x: %s", errno, strerror(errno));
+ return NULL;;
+ }
+ return fp;
+}
/**
* Save character sets to cache.
@@ -1213,11 +1477,9 @@ rufl_code rufl_save_cache(void)
size_t len;
FILE *fp;
- fp = fopen(rufl_CACHE, "wb");
- if (!fp) {
- LOG("fopen: 0x%x: %s", errno, strerror(errno));
+ fp = rufl_open_cache("wb");
+ if (!fp)
return rufl_OK;
- }
/* cache format version */
if (fwrite(&version, sizeof version, 1, fp) != 1) {
@@ -1235,7 +1497,10 @@ rufl_code rufl_save_cache(void)
}
for (i = 0; i != rufl_font_list_entries; i++) {
- if (!rufl_font_list[i].charset)
+ const struct rufl_character_set *charset =
+ rufl_font_list[i].charset;
+
+ if (!charset)
continue;
/* length of font identifier */
@@ -1253,9 +1518,19 @@ rufl_code rufl_save_cache(void)
return rufl_OK;
}
- /* character set */
- if (fwrite(rufl_font_list[i].charset,
- rufl_font_list[i].charset->size, 1, fp) != 1) {
+ /* character set (all planes) */
+ while (EXTENSION_FOLLOWS(charset->metadata)) {
+ if (fwrite(charset, PLANE_SIZE(charset->metadata),
+ 1, fp) != 1) {
+ LOG("fwrite: 0x%x: %s", errno, strerror(errno));
+ fclose(fp);
+ return rufl_OK;
+ }
+ charset = (void *)(((uint8_t *)charset) +
+ PLANE_SIZE(charset->metadata));
+ }
+ if (fwrite(charset, PLANE_SIZE(charset->metadata),
+ 1, fp) != 1) {
LOG("fwrite: 0x%x: %s", errno, strerror(errno));
fclose(fp);
return rufl_OK;
@@ -1338,18 +1613,17 @@ rufl_code rufl_load_cache(void)
unsigned int i = 0;
bool old_font_manager;
char *identifier;
- size_t len, size;
+ size_t len, size = 0;
+ uint32_t metadata;
FILE *fp;
struct rufl_font_list_entry *entry;
- struct rufl_character_set *charset;
+ struct rufl_character_set *charset = NULL, *cur_charset;
struct rufl_unicode_map *umap = NULL;
- unsigned int num_umaps = 0;
+ size_t num_umaps = 0;
- fp = fopen(rufl_CACHE, "rb");
- if (!fp) {
- LOG("fopen: 0x%x: %s", errno, strerror(errno));
+ fp = rufl_open_cache("rb");
+ if (!fp)
return rufl_OK;
- }
/* cache format version */
if (fread(&version, sizeof version, 1, fp) != 1) {
@@ -1413,33 +1687,52 @@ rufl_code rufl_load_cache(void)
identifier[len] = 0;
/* character set */
- if (fread(&size, sizeof size, 1, fp) != 1) {
- if (feof(fp))
- LOG("fread: %s", "unexpected eof");
- else
- LOG("fread: 0x%x: %s", errno, strerror(errno));
- free(identifier);
- break;
- }
+ do {
+ if (fread(&metadata, sizeof metadata, 1, fp) != 1) {
+ if (feof(fp))
+ LOG("fread: %s", "unexpected eof");
+ else
+ LOG("fread: 0x%x: %s",
+ errno, strerror(errno));
+ free(identifier);
+ break;
+ }
- charset = malloc(size);
- if (!charset) {
- LOG("malloc(%zu) failed", size);
- free(identifier);
- fclose(fp);
- return rufl_OUT_OF_MEMORY;
- }
+ if (!charset) {
+ charset = cur_charset = malloc(
+ PLANE_SIZE(metadata));
+ } else {
+ struct rufl_character_set *c2 = realloc(charset,
+ size + PLANE_SIZE(metadata));
+ if (!c2) {
+ free(charset);
+ }
+ charset = c2;
+ cur_charset = (void *)(((uint8_t *) charset) +
+ size);
+ }
+ if (!charset) {
+ LOG("malloc(%zu) failed", size);
+ free(identifier);
+ fclose(fp);
+ return rufl_OUT_OF_MEMORY;
+ }
- charset->size = size;
- if (fread(charset->index, size - sizeof size, 1, fp) != 1) {
- if (feof(fp))
- LOG("fread: %s", "unexpected eof");
- else
- LOG("fread: 0x%x: %s", errno, strerror(errno));
- free(charset);
- free(identifier);
- break;
- }
+ size += PLANE_SIZE(metadata);
+ cur_charset->metadata = metadata;
+ if (fread(cur_charset->index,
+ PLANE_SIZE(metadata) - sizeof metadata,
+ 1, fp) != 1) {
+ if (feof(fp))
+ LOG("fread: %s", "unexpected eof");
+ else
+ LOG("fread: 0x%x: %s",
+ errno, strerror(errno));
+ free(charset);
+ free(identifier);
+ break;
+ }
+ } while(EXTENSION_FOLLOWS(cur_charset->metadata));
/* unicode map */
if (rufl_old_font_manager) {
@@ -1571,7 +1864,10 @@ rufl_code rufl_load_cache(void)
free(charset);
}
- free(identifier);
+ charset = NULL;
+ size = 0;
+
+ free(identifier);
}
fclose(fp);
@@ -1598,10 +1894,10 @@ rufl_code rufl_init_family_menu(void)
wimp_menu *menu;
unsigned int i;
- menu = malloc(wimp_SIZEOF_MENU(rufl_family_list_entries));
+ menu = malloc(wimp_SIZEOF_MENU(rufl_family_list_entries + 1));
if (!menu)
return rufl_OUT_OF_MEMORY;
- menu->title_data.indirected_text.text = (char *) "Fonts";
+ strcpy(menu->title_data.text, "Fonts");
menu->title_fg = wimp_COLOUR_BLACK;
menu->title_bg = wimp_COLOUR_LIGHT_GREY;
menu->work_fg = wimp_COLOUR_BLACK;
@@ -1622,8 +1918,18 @@ rufl_code rufl_init_family_menu(void)
menu->entries[i].data.indirected_text.size =
strlen(rufl_family_list[i]);
}
- menu->entries[0].menu_flags = wimp_MENU_TITLE_INDIRECTED;
- menu->entries[i - 1].menu_flags |= wimp_MENU_LAST;
+ if (i == 0) {
+ menu->entries[i].menu_flags = wimp_MENU_LAST;
+ menu->entries[i].sub_menu = wimp_NO_SUB_MENU;
+ menu->entries[i].icon_flags = wimp_ICON_TEXT |
+ wimp_ICON_SHADED |
+ (wimp_COLOUR_BLACK << wimp_ICON_FG_COLOUR_SHIFT) |
+ (wimp_COLOUR_WHITE << wimp_ICON_BG_COLOUR_SHIFT);
+ menu->entries[i].data.text[0] = '\0';
+
+ } else {
+ menu->entries[i - 1].menu_flags |= wimp_MENU_LAST;
+ }
rufl_family_menu = menu;
diff --git a/src/rufl_internal.h b/src/rufl_internal.h
index d53c2dd..42ed5c4 100644
--- a/src/rufl_internal.h
+++ b/src/rufl_internal.h
@@ -5,29 +5,88 @@
* Copyright 2006 James Bursa <james@semichrome.net>
*/
+#include <inttypes.h>
#include <limits.h>
-#include "oslib/font.h"
+#include <oslib/font.h>
#include "rufl.h"
#ifdef __CC_NORCROFT
#include "strfuncs.h"
#endif
-/** The available characters in a font. The range which can be represented is
- * 0x0000 to 0xffff. The size of the structure is 4 + 256 + 32 * blocks. A
- * typical * 200 glyph font might have characters in 10 blocks, giving 580
- * bytes. The maximum possible size of the structure is 8388 bytes. Note that
- * since two index values are reserved, fonts with 65280-65024 glyphs may be
- * unrepresentable, if there are no full blocks. This is unlikely. The primary
- * aim of this structure is to make lookup fast. */
+/**
+ * The available Unicode codepoints represented by a font. The entire Unicode
+ * range (U+0000 - U+10FFFF) may be covered by the font, but only codepoints
+ * in the Basic Multilingual Plane (i.e. U+0000 - U+FFFF) can be represented
+ * without the need for extension structures.
+ *
+ * Fonts which provide glyphs for astral characters will set the extension
+ * bit in the structure size field. If set, this indicates that an additional
+ * character set structure follows immediately after this one. The plane id
+ * field in the structure metadata indicates which plane the structure relates
+ * to. Planes are specified in ascending order (as the most commonly used
+ * codepoints occur in earlier planes). Planes for which the font has no
+ * glyphs are omitted entirely.
+ *
+ * Each plane is subdivided into 256 codepoint blocks (each block representing
+ * 256 contiguous codepoints). Note, however, that two index values are
+ * reserved (to indicate full or empty blocks) so only 254 partial blocks may
+ * be represented. As of Unicode 13, all planes have at least two blocks
+ * unused (or, in the case of the surrogate ranges in the Basic Multilingual
+ * Plane, defined as containing no characters), so all valid codepoints should
+ * be representable using this scheme.
+ *
+ * The size of the structure is 4 + 256 + 32 * blocks. A typical 200 glyph
+ * font might represent codepoints in 10 blocks, using 580 bytes of storage.
+ * A plane with glyphs in every block (but no block fully populated) requires
+ * the maximum possible structure size of (4 + 256 + 32 * 254 =) 8388 bytes.
+ * The maximum storage required for (the unlikely scenario of) a font
+ * providing glyphs in every block in each of the 17 Unicode planes is
+ * 17 * 8388 = 142596 bytes.
+ *
+ * The primary aim of this structure is to make lookup fast.
+ */
struct rufl_character_set {
- /** Size of structure / bytes. */
- size_t size;
+ /** Structure metadata.
+ *
+ * This field contains metadata about the structure in the form:
+ *
+ * 3 2 1 0
+ * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |E| PID | Reserved | Size |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ * where:
+ *
+ * extension (E): 1 bit
+ * If set, another character set covering a different plane
+ * follows.
+ *
+ * plane id (PID): 5 bits
+ * The 0-based index of the Unicode plane this structure relates
+ * to. Valid values are in the range [0, 16], where 0 represents
+ * the Basic Multilingual Plane, and 16 represents the
+ * Supplementary Private Use Area - B.
+ *
+ * reserved: 10 bits
+ * These bits are currently unused and must be set to 0.
+ *
+ * size: 16 bits
+ * The total size of this structure, in bytes.
+ */
+ uint32_t metadata;
+# define EXTENSION_FOLLOWS(x) ((x) & (1u<<31))
+# define PLANE_ID(x) (((x) >> 26) & 0x1f)
+# define PLANE_SIZE(x) ((x) & 0xffff)
- /** Index table. Each entry represents a block of 256 characters, so
- * i[k] refers to characters [256*k, 256*(k+1)). The value is either
- * BLOCK_EMPTY, BLOCK_FULL, or an offset into the block table. */
- unsigned char index[256];
+ /** Index table.
+ *
+ * Each entry represents a block of 256 codepoints, so i[k] refers
+ * to codepoints [256*k, 256*(k+1)). The value is either BLOCK_EMPTY,
+ * BLOCK_FULL, or an offset into the block table.
+ * */
+ uint8_t index[256];
/** The block has no characters present. */
# define BLOCK_EMPTY 254
/** All characters in the block are present. */
@@ -35,16 +94,16 @@ struct rufl_character_set {
/** Block table. Each entry is a 256-bit bitmap indicating which
* characters in the block are present and absent. */
- unsigned char block[254][32];
+ uint8_t block[254][32];
};
/** Part of struct rufl_unicode_map. */
struct rufl_unicode_map_entry {
- /** Unicode value. */
- unsigned short u;
+ /** Unicode value (must be in Basic Multilingual Plane). */
+ uint16_t u;
/** Corresponding character. */
- unsigned char c;
+ uint8_t c;
};
@@ -57,7 +116,7 @@ struct rufl_unicode_map {
/** Corresponding encoding name */
char *encoding;
/** Number of valid entries in map. */
- unsigned int entries;
+ size_t entries;
/** Map from Unicode to character code. */
struct rufl_unicode_map_entry map[256];
};
@@ -70,16 +129,16 @@ struct rufl_font_list_entry {
/** Character set of font. */
struct rufl_character_set *charset;
/** Number of Unicode mapping tables */
- unsigned int num_umaps;
+ size_t num_umaps;
/** Mappings from Unicode to character code. */
struct rufl_unicode_map *umap;
/** Family that this font belongs to (index in rufl_family_list and
* rufl_family_map). */
- unsigned int family;
+ uint32_t family;
/** Font weight (0 to 8). */
- unsigned int weight;
+ uint32_t weight;
/** Font slant (0 or 1). */
- unsigned int slant;
+ uint32_t slant;
};
/** List of all available fonts. */
extern struct rufl_font_list_entry *rufl_font_list;
@@ -92,17 +151,14 @@ struct rufl_family_map_entry {
/** This style does not exist in this family. */
# define NO_FONT UINT_MAX
/** Map from weight and slant to index in rufl_font_list, or NO_FONT. */
- unsigned int font[9][2];
+ uint32_t font[9][2];
};
/** Map from font family to fonts, rufl_family_list_entries entries. */
extern struct rufl_family_map_entry *rufl_family_map;
/** No font contains this character. */
-#define NOT_AVAILABLE 65535
-/** Font substitution table. */
-extern unsigned short *rufl_substitution_table;
-
+#define NOT_AVAILABLE 0xffff
/** Number of slots in recent-use cache. This is the maximum number of RISC OS
* font handles that will be used at any time by the library. */
@@ -111,24 +167,24 @@ extern unsigned short *rufl_substitution_table;
/** An entry in rufl_cache. */
struct rufl_cache_entry {
/** Font number (index in rufl_font_list), or rufl_CACHE_*. */
- unsigned int font;
+ uint32_t font;
/** No font cached in this slot. */
#define rufl_CACHE_NONE UINT_MAX
/** Font for rendering hex substitutions in this slot. */
#define rufl_CACHE_CORPUS (UINT_MAX - 1)
/** Font size. */
- unsigned int size;
+ uint32_t size;
/** Font encoding */
const char *encoding;
/** Value of rufl_cache_time when last used. */
- unsigned int last_used;
+ uint32_t last_used;
/** RISC OS font handle. */
font_f f;
};
/** Cache of rufl_CACHE_SIZE most recently used font handles. */
extern struct rufl_cache_entry rufl_cache[rufl_CACHE_SIZE];
/** Counter for measuring age of cache entries. */
-extern int rufl_cache_time;
+extern uint32_t rufl_cache_time;
/** Font manager does not support Unicode. */
extern bool rufl_old_font_manager;
@@ -141,9 +197,13 @@ rufl_code rufl_find_font_family(const char *family, rufl_style font_style,
struct rufl_character_set **charset);
rufl_code rufl_find_font(unsigned int font, unsigned int font_size,
const char *encoding, font_f *fhandle);
-bool rufl_character_set_test(struct rufl_character_set *charset,
- unsigned int c);
+bool rufl_character_set_test(const struct rufl_character_set *charset,
+ uint32_t u);
+rufl_code rufl_substitution_table_init(void);
+void rufl_substitution_table_fini(void);
+unsigned int rufl_substitution_table_lookup(uint32_t u);
+void rufl_substitution_table_dump(void);
#define rufl_utf8_read(s, l, u) \
if (4 <= l && ((s[0] & 0xf8) == 0xf0) && ((s[1] & 0xc0) == 0x80) && \
@@ -151,31 +211,38 @@ bool rufl_character_set_test(struct rufl_character_set *charset,
u = ((s[0] & 0x7) << 18) | ((s[1] & 0x3f) << 12) | \
((s[2] & 0x3f) << 6) | (s[3] & 0x3f); \
s += 4; l -= 4; \
+ if (u < 0x10000) u = 0xfffd; \
} else if (3 <= l && ((s[0] & 0xf0) == 0xe0) && \
((s[1] & 0xc0) == 0x80) && \
((s[2] & 0xc0) == 0x80)) { \
u = ((s[0] & 0xf) << 12) | ((s[1] & 0x3f) << 6) | \
(s[2] & 0x3f); \
s += 3; l -= 3; \
+ if (u < 0x800) u = 0xfffd; \
} else if (2 <= l && ((s[0] & 0xe0) == 0xc0) && \
((s[1] & 0xc0) == 0x80)) { \
u = ((s[0] & 0x3f) << 6) | (s[1] & 0x3f); \
s += 2; l -= 2; \
+ if (u < 0x80) u = 0xfffd; \
} else if ((s[0] & 0x80) == 0) { \
u = s[0]; \
s++; l--; \
} else { \
u = 0xfffd; \
s++; l--; \
+ } \
+ if ((u >= 0xd800 && u <= 0xdfff) || u == 0xfffe || u == 0xffff) { \
+ u = 0xfffd; \
}
-#define rufl_CACHE "<Wimp$ScrapDir>.RUfl_cache"
-#define rufl_CACHE_VERSION 3
+#define rufl_CACHE_TEMPLATE "<Wimp$ScrapDir>.RUfl.CacheNNNN"
+#define rufl_CACHE_VERSION 4
struct rufl_glyph_map_entry {
const char *glyph_name;
- unsigned short u;
+ /* The glyph map contains codepoints in the BMP only */
+ uint16_t u;
};
extern const struct rufl_glyph_map_entry rufl_glyph_map[];
@@ -183,22 +250,20 @@ extern const size_t rufl_glyph_map_size;
#if 1 /*ndef NDEBUG*/
-#ifdef __CC_NORCROFT
-#define __PRETTY_FUNCTION__ __func__
-#endif
#include <time.h>
-bool log_got_start_time;
-time_t log_start_time;
+extern bool rufl_log_got_start_time;
+extern time_t rufl_log_start_time;
#define LOG(format, ...) \
do { \
- if (log_got_start_time == false) { \
- log_start_time = time(NULL); \
- log_got_start_time = true; \
+ if (rufl_log_got_start_time == false) { \
+ rufl_log_start_time = time(NULL); \
+ rufl_log_got_start_time = true; \
} \
\
fprintf(stderr,"(%.6fs) " __FILE__ " %s %i: ", \
- difftime(time(NULL), log_start_time), \
- __PRETTY_FUNCTION__, __LINE__); \
+ difftime(time(NULL), \
+ rufl_log_start_time), \
+ __func__, __LINE__); \
fprintf(stderr, format, __VA_ARGS__); \
fprintf(stderr, "\n"); \
} while (0)
diff --git a/src/rufl_invalidate_cache.c b/src/rufl_invalidate_cache.c
index 65a3897..3ec73d9 100644
--- a/src/rufl_invalidate_cache.c
+++ b/src/rufl_invalidate_cache.c
@@ -5,7 +5,7 @@
* Copyright 2005 James Bursa <james@semichrome.net>
*/
-#include "oslib/font.h"
+#include <oslib/font.h>
#include "rufl_internal.h"
diff --git a/src/rufl_metrics.c b/src/rufl_metrics.c
index af4727f..bbfd6bb 100644
--- a/src/rufl_metrics.c
+++ b/src/rufl_metrics.c
@@ -9,7 +9,7 @@
#include <stdio.h>
#include <stdlib.h>
-#include "oslib/font.h"
+#include <oslib/font.h>
#include "rufl_internal.h"
@@ -19,10 +19,10 @@ static int rufl_unicode_map_search_cmp(const void *keyval, const void *datum);
* Read a font's metrics (sized for a 1pt font)
*/
rufl_code rufl_font_metrics(const char *font_family, rufl_style font_style,
- os_box *bbox, int *xkern, int *ykern, int *italic,
- int *ascent, int *descent,
- int *xheight, int *cap_height,
- signed char *uline_position, unsigned char *uline_thickness)
+ os_box *bbox, int32_t *xkern, int32_t *ykern, int32_t *italic,
+ int32_t *ascent, int32_t *descent,
+ int32_t *xheight, int32_t *cap_height,
+ int8_t *uline_position, uint8_t *uline_thickness)
{
unsigned int font;
font_f f;
@@ -115,13 +115,14 @@ rufl_code rufl_font_metrics(const char *font_family, rufl_style font_style,
rufl_code rufl_glyph_metrics(const char *font_family,
rufl_style font_style, unsigned int font_size,
const char *string, size_t length,
- int *x_bearing, int *y_bearing,
- int *width, int *height,
- int *x_advance, int *y_advance)
+ int32_t *x_bearing, int32_t *y_bearing,
+ int32_t *width, int32_t *height,
+ int32_t *x_advance, int32_t *y_advance)
{
+ const uint8_t *ustring = (const uint8_t *) string;
const char *font_encoding = NULL;
unsigned int font, font1, u;
- unsigned short u1[2];
+ uint32_t u1[2];
struct rufl_character_set *charset;
struct rufl_unicode_map_entry *umap_entry = NULL;
font_f f;
@@ -136,13 +137,14 @@ rufl_code rufl_glyph_metrics(const char *font_family,
if (code != rufl_OK)
return code;
- rufl_utf8_read(string, length, u);
+ rufl_utf8_read(ustring, length, u);
if (charset && rufl_character_set_test(charset, u))
font1 = font;
- else if (u < 0x10000)
- font1 = rufl_substitution_table[u];
- else
- font1 = rufl_CACHE_CORPUS;
+ else {
+ font1 = rufl_substitution_table_lookup(u);
+ if (font1 == NOT_AVAILABLE)
+ font1 = rufl_CACHE_CORPUS;
+ }
/* Old font managers need the font encoding, too */
if (rufl_old_font_manager && font1 != rufl_CACHE_CORPUS) {
@@ -235,7 +237,7 @@ rufl_code rufl_glyph_metrics(const char *font_family,
flags = font_GIVEN_BLOCK | font_GIVEN_LENGTH | font_GIVEN_FONT |
font_RETURN_BBOX;
- u1[0] = (unsigned short)u;
+ u1[0] = u;
u1[1] = 0;
if (font1 == rufl_CACHE_CORPUS) {
@@ -266,8 +268,8 @@ rufl_code rufl_glyph_metrics(const char *font_family,
} else {
/* UCS Font Manager */
rufl_fm_error = xfont_scan_string(f, (const char *)u1,
- flags | font_GIVEN16_BIT,
- 0x7fffffff, 0x7fffffff, &block, 0, 2,
+ flags | font_GIVEN32_BIT,
+ 0x7fffffff, 0x7fffffff, &block, 0, 4,
0, &xa, &ya, 0);
if (rufl_fm_error) {
LOG("xfont_scan_string: 0x%x: %s",
diff --git a/src/rufl_paint.c b/src/rufl_paint.c
index 8b9edf1..9dc0e3f 100644
--- a/src/rufl_paint.c
+++ b/src/rufl_paint.c
@@ -10,7 +10,7 @@
#include <stdlib.h>
#include <string.h>
#include <strings.h>
-#include "oslib/font.h"
+#include <oslib/font.h>
#include "rufl_internal.h"
@@ -27,25 +27,25 @@ static const os_trfm trfm_oblique =
static rufl_code rufl_process(rufl_action action,
const char *font_family, rufl_style font_style,
unsigned int font_size,
- const char *string0, size_t length,
+ const uint8_t *string0, size_t length,
int x, int y, unsigned int flags,
int *width, int click_x, size_t *char_offset, int *actual_x,
rufl_callback_t callback, void *context);
static rufl_code rufl_process_span(rufl_action action,
- unsigned short *s, unsigned int n,
+ uint32_t *s, unsigned int n,
unsigned int font, unsigned int font_size, unsigned int slant,
int *x, int y, unsigned int flags,
int click_x, size_t *offset,
rufl_callback_t callback, void *context);
static rufl_code rufl_process_span_old(rufl_action action,
- unsigned short *s, unsigned int n,
+ uint32_t *s, unsigned int n,
unsigned int font, unsigned int font_size, unsigned int slant,
int *x, int y, unsigned int flags,
int click_x, size_t *offset,
rufl_callback_t callback, void *context);
static int rufl_unicode_map_search_cmp(const void *keyval, const void *datum);
static rufl_code rufl_process_not_available(rufl_action action,
- unsigned short *s, unsigned int n,
+ uint32_t *s, unsigned int n,
unsigned int font_size, int *x, int y,
unsigned int flags,
int click_x, size_t *offset,
@@ -62,8 +62,9 @@ rufl_code rufl_paint(const char *font_family, rufl_style font_style,
int x, int y, unsigned int flags)
{
return rufl_process(rufl_PAINT,
- font_family, font_style, font_size, string,
- length, x, y, flags, 0, 0, 0, 0, 0, 0);
+ font_family, font_style, font_size,
+ (const uint8_t *) string, length,
+ x, y, flags, 0, 0, 0, 0, 0, 0);
}
@@ -77,8 +78,9 @@ rufl_code rufl_width(const char *font_family, rufl_style font_style,
int *width)
{
return rufl_process(rufl_WIDTH,
- font_family, font_style, font_size, string,
- length, 0, 0, 0, width, 0, 0, 0, 0, 0);
+ font_family, font_style, font_size,
+ (const uint8_t *) string, length,
+ 0, 0, 0, width, 0, 0, 0, 0, 0);
}
@@ -94,9 +96,9 @@ rufl_code rufl_x_to_offset(const char *font_family, rufl_style font_style,
size_t *char_offset, int *actual_x)
{
return rufl_process(rufl_X_TO_OFFSET,
- font_family, font_style, font_size, string,
- length, 0, 0, 0, 0,
- click_x, char_offset, actual_x, 0, 0);
+ font_family, font_style, font_size,
+ (const uint8_t *) string, length,
+ 0, 0, 0, 0, click_x, char_offset, actual_x, 0, 0);
}
@@ -111,9 +113,9 @@ rufl_code rufl_split(const char *font_family, rufl_style font_style,
size_t *char_offset, int *actual_x)
{
return rufl_process(rufl_SPLIT,
- font_family, font_style, font_size, string,
- length, 0, 0, 0, 0,
- width, char_offset, actual_x, 0, 0);
+ font_family, font_style, font_size,
+ (const uint8_t *) string, length,
+ 0, 0, 0, 0, width, char_offset, actual_x, 0, 0);
}
@@ -128,8 +130,9 @@ rufl_code rufl_paint_callback(const char *font_family, rufl_style font_style,
rufl_callback_t callback, void *context)
{
return rufl_process(rufl_PAINT_CALLBACK,
- font_family, font_style, font_size, string,
- length, x, y, 0, 0, 0, 0, 0, callback, context);
+ font_family, font_style, font_size,
+ (const uint8_t *) string, length,
+ x, y, 0, 0, 0, 0, 0, callback, context);
}
@@ -139,11 +142,11 @@ rufl_code rufl_paint_callback(const char *font_family, rufl_style font_style,
rufl_code rufl_font_bbox(const char *font_family, rufl_style font_style,
unsigned int font_size,
- int *bbox)
+ os_box *bbox)
{
return rufl_process(rufl_FONT_BBOX,
font_family, font_style, font_size, 0,
- 0, 0, 0, 0, bbox, 0, 0, 0, 0, 0);
+ 0, 0, 0, 0, (int *) bbox, 0, 0, 0, 0, 0);
}
@@ -154,12 +157,12 @@ rufl_code rufl_font_bbox(const char *font_family, rufl_style font_style,
rufl_code rufl_process(rufl_action action,
const char *font_family, rufl_style font_style,
unsigned int font_size,
- const char *string0, size_t length,
+ const uint8_t *string0, size_t length,
int x, int y, unsigned int flags,
int *width, int click_x, size_t *char_offset, int *actual_x,
rufl_callback_t callback, void *context)
{
- unsigned short s[rufl_PROCESS_CHUNK];
+ uint32_t s[rufl_PROCESS_CHUNK];
unsigned int font;
unsigned int font0, font1;
unsigned int n;
@@ -168,7 +171,7 @@ rufl_code rufl_process(rufl_action action,
size_t offset_u;
size_t offset_map[rufl_PROCESS_CHUNK];
unsigned int slant;
- const char *string = string0;
+ const uint8_t *string = string0;
struct rufl_character_set *charset;
rufl_code code;
@@ -225,10 +228,8 @@ rufl_code rufl_process(rufl_action action,
font1 = NOT_AVAILABLE;
else if (charset && rufl_character_set_test(charset, u))
font1 = font;
- else if (u < 0x10000)
- font1 = rufl_substitution_table[u];
else
- font1 = NOT_AVAILABLE;
+ font1 = rufl_substitution_table_lookup(u);
do {
s[0] = u;
offset_map[0] = offset_u;
@@ -244,10 +245,8 @@ rufl_code rufl_process(rufl_action action,
font1 = NOT_AVAILABLE;
else if (charset && rufl_character_set_test(charset, u))
font1 = font;
- else if (u < 0x10000)
- font1 = rufl_substitution_table[u];
else
- font1 = NOT_AVAILABLE;
+ font1 = rufl_substitution_table_lookup(u);
if (font1 == font0)
n++;
}
@@ -295,13 +294,13 @@ rufl_code rufl_process(rufl_action action,
*/
rufl_code rufl_process_span(rufl_action action,
- unsigned short *s, unsigned int n,
+ uint32_t *s, unsigned int n,
unsigned int font, unsigned int font_size, unsigned int slant,
int *x, int y, unsigned int flags,
int click_x, size_t *offset,
rufl_callback_t callback, void *context)
{
- unsigned short *split_point;
+ uint32_t *split_point;
int x_out, y_out;
unsigned int i;
char font_name[80];
@@ -314,7 +313,9 @@ rufl_code rufl_process_span(rufl_action action,
return code;
if (action == rufl_FONT_BBOX) {
- rufl_fm_error = xfont_read_info(f, &x[0], &x[1], &x[2], &x[3]);
+ os_box *bbox = (os_box *) x;
+ rufl_fm_error = xfont_read_info(f, &bbox->x0, &bbox->y0,
+ &bbox->x1, &bbox->y1);
if (rufl_fm_error)
return rufl_FONT_MANAGER_ERROR;
return rufl_OK;
@@ -327,10 +328,10 @@ rufl_code rufl_process_span(rufl_action action,
(oblique ? font_GIVEN_TRFM : 0) |
font_GIVEN_LENGTH |
font_GIVEN_FONT | font_KERN |
- font_GIVEN16_BIT |
+ font_GIVEN32_BIT |
((flags & rufl_BLEND_FONT) ?
font_BLEND_FONT : 0),
- *x, y, 0, &trfm_oblique, n * 2);
+ *x, y, 0, &trfm_oblique, n * 4);
if (rufl_fm_error) {
LOG("xfont_paint: 0x%x: %s",
rufl_fm_error->errnum,
@@ -350,19 +351,19 @@ rufl_code rufl_process_span(rufl_action action,
if (action == rufl_X_TO_OFFSET || action == rufl_SPLIT) {
rufl_fm_error = xfont_scan_string(f, (const char *) s,
font_GIVEN_LENGTH | font_GIVEN_FONT |
- font_KERN | font_GIVEN16_BIT |
+ font_KERN | font_GIVEN32_BIT |
((action == rufl_X_TO_OFFSET) ?
font_RETURN_CARET_POS : 0),
(click_x - *x) * 400, 0x7fffffff, 0, 0,
- n * 2,
+ n * 4,
(char **)(void *)&split_point,
&x_out, &y_out, 0);
*offset = split_point - s;
} else {
rufl_fm_error = xfont_scan_string(f, (const char *) s,
font_GIVEN_LENGTH | font_GIVEN_FONT |
- font_KERN | font_GIVEN16_BIT,
- 0x7fffffff, 0x7fffffff, 0, 0, n * 2,
+ font_KERN | font_GIVEN32_BIT,
+ 0x7fffffff, 0x7fffffff, 0, 0, n * 4,
0, &x_out, &y_out, 0);
}
if (rufl_fm_error) {
@@ -383,13 +384,13 @@ rufl_code rufl_process_span(rufl_action action,
*/
rufl_code rufl_process_span_old(rufl_action action,
- unsigned short *s, unsigned int n,
+ uint32_t *s, unsigned int n,
unsigned int font, unsigned int font_size, unsigned int slant,
int *x, int y, unsigned int flags,
int click_x, size_t *offset,
rufl_callback_t callback, void *context)
{
- char s2[rufl_PROCESS_CHUNK];
+ uint8_t s2[rufl_PROCESS_CHUNK];
char *split_point;
int x_out, y_out;
unsigned int i;
@@ -398,12 +399,15 @@ rufl_code rufl_process_span_old(rufl_action action,
rufl_code code;
if (action == rufl_FONT_BBOX) {
+ os_box *bbox = (os_box *) x;
+
/* Don't need encoding for bounding box */
code = rufl_find_font(font, font_size, NULL, &f);
if (code != rufl_OK)
return code;
- rufl_fm_error = xfont_read_info(f, &x[0], &x[1], &x[2], &x[3]);
+ rufl_fm_error = xfont_read_info(f, &bbox->x0, &bbox->y0,
+ &bbox->x1, &bbox->y1);
if (rufl_fm_error) {
LOG("xfont_read_info: 0x%x: %s",
rufl_fm_error->errnum,
@@ -466,7 +470,8 @@ rufl_code rufl_process_span_old(rufl_action action,
return rufl_FONT_MANAGER_ERROR;
}
- rufl_fm_error = xfont_paint(f, s2, font_OS_UNITS |
+ rufl_fm_error = xfont_paint(f, (char *) s2,
+ font_OS_UNITS |
(oblique ? font_GIVEN_TRFM : 0) |
font_GIVEN_LENGTH | font_GIVEN_FONT |
font_KERN |
@@ -496,7 +501,7 @@ rufl_code rufl_process_span_old(rufl_action action,
/* increment x by width of span */
if (action == rufl_X_TO_OFFSET || action == rufl_SPLIT) {
- rufl_fm_error = xfont_scan_string(f, s2,
+ rufl_fm_error = xfont_scan_string(f, (char *) s2,
font_GIVEN_LENGTH | font_GIVEN_FONT |
font_KERN |
((action == rufl_X_TO_OFFSET) ?
@@ -504,9 +509,9 @@ rufl_code rufl_process_span_old(rufl_action action,
(click_x - *x) * 400, 0x7fffffff,
0, 0, i,
&split_point, &x_out, &y_out, 0);
- *offset += split_point - s2;
+ *offset += split_point - (char *) s2;
} else {
- rufl_fm_error = xfont_scan_string(f, s2,
+ rufl_fm_error = xfont_scan_string(f, (char *) s2,
font_GIVEN_LENGTH | font_GIVEN_FONT |
font_KERN,
0x7fffffff, 0x7fffffff, 0, 0, i,
@@ -546,28 +551,33 @@ int rufl_unicode_map_search_cmp(const void *keyval, const void *datum)
*/
rufl_code rufl_process_not_available(rufl_action action,
- unsigned short *s, unsigned int n,
+ uint32_t *s, unsigned int n,
unsigned int font_size, int *x, int y,
unsigned int flags,
int click_x, size_t *offset,
rufl_callback_t callback, void *context)
{
- char missing[] = "0000";
- int dx = 7 * font_size / 64;
+ uint8_t missing[] = "000000";
+ const int dx = 7 * font_size / 64;
+ const int dx3 = 10.5 * font_size / 64;
int top_y = y + 5 * font_size / 64;
unsigned int i;
font_f f;
rufl_code code;
if (action == rufl_WIDTH) {
- *x += n * dx;
+ for (i = 0; i != n; i++)
+ *x += (s[i] < 0x10000) ? dx : dx3;
return rufl_OK;
} else if (action == rufl_X_TO_OFFSET || action == rufl_SPLIT) {
- if (click_x - *x < (int) (n * dx))
- *offset = (click_x - *x) / dx;
- else
- *offset = n;
- *x += *offset * dx;
+ int width = 0;
+ for (i = 0; i != n; i++) {
+ if (click_x - *x <= width)
+ break;
+ width += (s[i] < 0x10000) ? dx : dx3;
+ }
+ *offset = i;
+ *x += width;
return rufl_OK;
}
@@ -576,45 +586,52 @@ rufl_code rufl_process_not_available(rufl_action action,
return code;
for (i = 0; i != n; i++) {
- missing[0] = "0123456789abcdef"[(s[i] >> 12) & 0xf];
- missing[1] = "0123456789abcdef"[(s[i] >> 8) & 0xf];
- missing[2] = "0123456789abcdef"[(s[i] >> 4) & 0xf];
- missing[3] = "0123456789abcdef"[(s[i] >> 0) & 0xf];
+ int offset = (s[i] < 0x10000) ? 2 : 0;
+ int step = (s[i] < 0x10000) ? 2 : 3;
+
+ missing[0] = "0123456789abcdef"[(s[i] >> 20) & 0xf];
+ missing[1] = "0123456789abcdef"[(s[i] >> 16) & 0xf];
+ missing[2] = "0123456789abcdef"[(s[i] >> 12) & 0xf];
+ missing[3] = "0123456789abcdef"[(s[i] >> 8) & 0xf];
+ missing[4] = "0123456789abcdef"[(s[i] >> 4) & 0xf];
+ missing[5] = "0123456789abcdef"[(s[i] >> 0) & 0xf];
/* first two characters in top row */
if (action == rufl_PAINT) {
- rufl_fm_error = xfont_paint(f, missing, font_OS_UNITS |
- font_GIVEN_LENGTH | font_GIVEN_FONT |
- font_KERN |
+ rufl_fm_error = xfont_paint(f,
+ (char *) (missing + offset),
+ font_OS_UNITS | font_GIVEN_LENGTH |
+ font_GIVEN_FONT | font_KERN |
((flags & rufl_BLEND_FONT) ?
font_BLEND_FONT : 0),
- *x, top_y, 0, 0, 2);
+ *x, top_y, 0, 0, step);
if (rufl_fm_error)
return rufl_FONT_MANAGER_ERROR;
} else {
callback(context, "Corpus.Medium\\ELatin1",
- font_size / 2, missing, 0, 2,
- *x, top_y);
+ font_size / 2, missing + offset, 0,
+ step, *x, top_y);
}
/* last two characters underneath */
if (action == rufl_PAINT) {
- rufl_fm_error = xfont_paint(f, missing + 2,
+ rufl_fm_error = xfont_paint(f,
+ (char *) (missing + offset + step),
font_OS_UNITS |
font_GIVEN_LENGTH | font_GIVEN_FONT |
font_KERN |
((flags & rufl_BLEND_FONT) ?
font_BLEND_FONT : 0),
- *x, y, 0, 0, 2);
+ *x, y, 0, 0, step);
if (rufl_fm_error)
return rufl_FONT_MANAGER_ERROR;
} else {
callback(context, "Corpus.Medium\\ELatin1",
- font_size / 2, missing + 2, 0, 2,
- *x, y);
+ font_size / 2, missing + offset + step,
+ 0, step, *x, y);
}
- *x += dx;
+ *x += (s[i] < 0x10000) ? dx : dx3;
}
return rufl_OK;
diff --git a/src/rufl_quit.c b/src/rufl_quit.c
index fc429c0..039229c 100644
--- a/src/rufl_quit.c
+++ b/src/rufl_quit.c
@@ -6,7 +6,7 @@
*/
#include <stdlib.h>
-#include "oslib/font.h"
+#include <oslib/font.h>
#include "rufl_internal.h"
@@ -24,26 +24,36 @@ void rufl_quit(void)
for (i = 0; i != rufl_font_list_entries; i++) {
free(rufl_font_list[i].identifier);
free(rufl_font_list[i].charset);
+ if (rufl_font_list[i].umap != NULL) {
+ size_t j;
+ for (j = 0; j != rufl_font_list[i].num_umaps; j++) {
+ free((rufl_font_list[i].umap + j)->encoding);
+ }
+ free(rufl_font_list[i].umap);
+ }
}
free(rufl_font_list);
- rufl_font_list = 0;
+ rufl_font_list = NULL;
+ rufl_font_list_entries = 0;
for (i = 0; i != rufl_family_list_entries; i++)
free((void *) rufl_family_list[i]);
free(rufl_family_list);
free(rufl_family_map);
- rufl_family_list = 0;
+ rufl_family_map = NULL;
+ rufl_family_list = NULL;
+ rufl_family_list_entries = 0;
for (i = 0; i != rufl_CACHE_SIZE; i++) {
if (rufl_cache[i].font != rufl_CACHE_NONE) {
xfont_lose_font(rufl_cache[i].f);
rufl_cache[i].font = rufl_CACHE_NONE;
}
- }
+ }
+ rufl_cache_time = 0;
free(rufl_family_menu);
- rufl_family_menu = 0;
+ rufl_family_menu = NULL;
- free(rufl_substitution_table);
- rufl_substitution_table = 0;
+ rufl_substitution_table_fini();
}
diff --git a/src/rufl_substitution_table.c b/src/rufl_substitution_table.c
new file mode 100644
index 0000000..0bdf78d
--- /dev/null
+++ b/src/rufl_substitution_table.c
@@ -0,0 +1,1143 @@
+/*
+ * This file is part of RUfl
+ * Licensed under the MIT License,
+ * http://www.opensource.org/licenses/mit-license
+ * Copyright 2006 James Bursa <james@semichrome.net>
+ */
+
+#include <assert.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+#include "rufl_internal.h"
+
+#undef RUFL_SUBSTITUTION_TABLE_DEBUG
+
+/**
+ * Base type for a substitution table.
+ */
+struct rufl_substitution_table {
+ /** Description of table implementation */
+ const char *desc;
+ /** Look up a Unicode codepoint. */
+ unsigned int (*lookup)(const struct rufl_substitution_table *t,
+ uint32_t u);
+ /** Free the resources used by this table */
+ void (*free)(struct rufl_substitution_table *t);
+ /** Dump the contents of this table to stdout */
+ void (*dump)(const struct rufl_substitution_table *t,
+ unsigned int plane);
+ /** Compute the storage size of this table */
+ size_t (*size)(const struct rufl_substitution_table *t,
+ unsigned int *glyph_count);
+};
+
+/**
+ * Implementation of a substitution table using direct lookup.
+ */
+struct rufl_substitution_table_direct {
+ struct rufl_substitution_table base;
+
+ /** Table index.
+ *
+ * Each entry represents a block of 256 codepoints, so i[k] refers
+ * to codepoints [256*k, 256*(k+1)). The value is an offset into
+ * the block table.
+ */
+ uint8_t index[256];
+
+ /** Bits per block table entry. Will be 8 or 16. */
+ uint8_t bits_per_entry;
+
+ /** Substitution table.
+ *
+ * Entries are the index into rufl_font_list of a font providing a
+ * substitution glyph for this codepoint or NOT_AVAILABLE.
+ *
+ * Note that, although this is defined as a 16bit type,
+ * the actual field width is indicated by bits_per_entry (and,
+ * if bits_per_entry is 8, then (NOT_AVAILABLE & 0xff) represents
+ * a missing glyph).
+ */
+ uint16_t *table;
+};
+
+/**
+ * Implementation of a substitution table using a perfect hash.
+ *
+ * A perfect hash constructed at library initialisation time using the
+ * CHD algorithm. Hash entries are found via a two-step process:
+ *
+ * 1. apply a first-stage hash to the key to find the bucket
+ * in which the corresponding entry should be found.
+ * 2. apply a second-stage hash to the key and the stored
+ * displacement value for the bucket to find the index
+ * into the substitution table.
+ */
+struct rufl_substitution_table_chd {
+ struct rufl_substitution_table base;
+
+ uint32_t num_buckets; /**< Number of buckets in the hash */
+ uint32_t num_slots; /**< Number of slots in the table */
+ /** Substitution table.
+ *
+ * Fields in the substitution table have the following format:
+ *
+ * 3 2 1 0
+ * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Unicode codepoint | Font identifier |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ * where:
+ *
+ * unicode codepoint: 16 bits
+ * The low 16 bits of the Unicode codepoint value.
+ *
+ * font identifier: 16 bits
+ * The index into rufl_font_list of a font providing a
+ * substitution glyph for this codepoint or NOT_AVAILABLE.
+ *
+ * Note that, as the substitution table is sparse and may not be
+ * fully populated, it is necessary to verify that the Unicode
+ * codepoint matches the key being hashed and that the font
+ * identifier is not NOT_AVAILABLE. If either of these tests
+ * fail, no font provides a suitable glyph and the not available
+ * path should be taken.
+ */
+ uint32_t *table;
+ uint8_t bits_per_entry; /**< Bits per displacement bitmap entry */
+ /** Displacement bitmap.
+ *
+ * The displacement values are stored in a bitmap of num_buckets
+ * fields each being bits_per_entry wide. Both values are computed
+ * at runtime.
+ */
+ uint8_t displacement_map[];
+};
+/** Font substitution tables -- one per plane */
+static struct rufl_substitution_table *rufl_substitution_table[17];
+
+/**
+ * Round an unsigned 32bit value up to the next power of 2
+ */
+static uint32_t ceil2(uint32_t val)
+{
+ val--;
+ val |= (val >> 1);
+ val |= (val >> 2);
+ val |= (val >> 4);
+ val |= (val >> 8);
+ val |= (val >> 16);
+ val++;
+ val += (val == 0);
+ return val;
+}
+
+/**
+ * Compute the number of bits needed to store a value
+ */
+static uint32_t bits_needed(uint32_t val)
+{
+ int32_t result = 0;
+
+ if (val == 0)
+ return 1;
+
+ if ((val & (val - 1))) {
+ /* Not a power of 2: round up */
+ val = ceil2(val);
+ /* Will need one fewer bit than we're about to count */
+ result = -1;
+ }
+
+ while (val > 0) {
+ result += 1;
+ val >>= 1;
+ }
+
+ return (uint32_t) result;
+}
+
+/**
+ * Perform one round of MurmurHash2
+ */
+static uint32_t mround(uint32_t val, uint32_t s)
+{
+ val *= 0x5db1e995;
+ val ^= (val >> 24);
+ val *= 0x5db1e995;
+ val ^= (s * 0x5db1e995);
+
+ return val;
+}
+
+/**
+ * Perform the MurmurHash2 mixing step
+ */
+static uint32_t mmix(uint32_t val)
+{
+ val ^= (val >> 13);
+ val *= 0x5db1e995;
+ val ^= (val >> 15);
+
+ return val;
+}
+
+/**
+ * First-stage hash (i.e. g(x)) for substitution table.
+ *
+ * As we know that the input values are Unicode codepoints,
+ * do some trivial bit manipulation, which has reasonable
+ * distribution properties.
+ */
+static uint32_t hash1(uint32_t val)
+{
+ val ^= (val >> 7);
+ val ^= (val << 3);
+ val ^= (val >> 4);
+ return val;
+}
+
+/**
+ * Second-stage hash (i.e. f(d, x)) for substitution table.
+ *
+ * Apply MurmurHash2 to the value and displacement
+ */
+static uint32_t hash2(uint32_t val, uint32_t d)
+{
+ return mmix(mround(val, mround(d, 4)));
+}
+
+/**
+ * Comparison function for table entries.
+ *
+ * We use this when sorting the intermediate table for CHD.
+ */
+static int table_chd_cmp(const void *a, const void *b)
+{
+ /* We're only interested in the CHD metadata here.
+ * (i.e. the computed value of g(x) and the bucket size) */
+ const uint64_t aa = (*(const uint64_t *) a) & 0x000fffff00000000llu;
+ const uint64_t bb = (*(const uint64_t *) b) & 0x000fffff00000000llu;
+
+ if (aa > bb)
+ return -1;
+ else if (aa < bb)
+ return 1;
+ return 0;
+}
+
+/**
+ * Test that all specified bits in a bit map are clear and set them if so.
+ *
+ * \param bitmap Bit map to inspect
+ * \param idx Table of indices to inspect
+ * \param len Number of entries in index table
+ * \return True if all bits were clear. False otherwise.
+ */
+static bool test_and_set_bits(uint8_t *bitmap, const uint32_t *idx, size_t len)
+{
+ unsigned int i;
+ bool result = true;
+
+ /* Test if all specified bits are clear */
+ for (i = 0; i != len; i++) {
+ const uint32_t byte = (idx[i] >> 3);
+ const uint32_t bit = (idx[i] & 0x7);
+
+ result &= ((bitmap[byte] & (1 << bit)) == 0);
+ }
+
+ if (result) {
+ /* They are, so set them */
+ for (i = 0; i != len; i++) {
+ const uint32_t byte = (idx[i] >> 3);
+ const uint32_t bit = (idx[i] & 0x7);
+
+ bitmap[byte] |= (1 << bit);
+ }
+ }
+
+ return result;
+}
+
+static void rufl_substitution_table_free_chd(
+ struct rufl_substitution_table *t)
+{
+ free(((struct rufl_substitution_table_chd *)t)->table);
+ free(t);
+}
+
+
+static unsigned int rufl_substitution_table_lookup_chd(
+ const struct rufl_substitution_table *ts, uint32_t u)
+{
+ const struct rufl_substitution_table_chd *t = (const void *) ts;
+ uint32_t displacement = 0;
+ uint32_t f, g = hash1(u & 0xffff) & (t->num_buckets - 1);
+ uint32_t bits_to_read = t->bits_per_entry;
+ uint32_t offset_bits = g * bits_to_read;
+ const uint8_t *pread = &t->displacement_map[offset_bits >> 3];
+
+ offset_bits &= 7;
+
+ while (bits_to_read > 0) {
+ uint32_t space_available = (8 - offset_bits);
+ if (space_available > bits_to_read)
+ space_available = bits_to_read;
+
+ displacement <<= space_available;
+ displacement |= (*pread & (0xff >> offset_bits)) >>
+ (8 - space_available - offset_bits);
+
+ offset_bits += space_available;
+ if (offset_bits >= 8) {
+ pread++;
+ offset_bits = 0;
+ }
+ bits_to_read -= space_available;
+ }
+
+ f = hash2((u & 0xffff), displacement) & (t->num_slots - 1);
+
+ if ((t->table[f] & 0xffff) != NOT_AVAILABLE &&
+ ((t->table[f] >> 16) & 0xffff) == (u & 0xffff))
+ return t->table[f] & 0xffff;
+
+ return NOT_AVAILABLE;
+}
+
+static int table_dump_cmp(const void *a, const void *b)
+{
+ const uint32_t aa = (*(const uint32_t *) a);
+ const uint32_t bb = (*(const uint32_t *) b);
+
+ if (aa > bb)
+ return 1;
+ else if (aa < bb)
+ return -1;
+ return 0;
+}
+
+static void rufl_substitution_table_dump_chd(
+ const struct rufl_substitution_table *ts, unsigned int plane)
+{
+ const struct rufl_substitution_table_chd *t = (const void *) ts;
+ unsigned int font;
+ unsigned int u, prev;
+ uint32_t *table;
+
+ table = malloc(t->num_slots * sizeof(*table));
+ if (table == NULL)
+ return;
+
+ memcpy(table, t->table, t->num_slots * sizeof(*table));
+
+ qsort(table, t->num_slots, sizeof(*table), table_dump_cmp);
+
+ u = 0;
+ while (u < t->num_slots) {
+ prev = u;
+ font = table[prev] & 0xffff;
+ while (u < t->num_slots && font == (table[u] & 0xffff) &&
+ ((u == prev) ||
+ ((table[u - 1] >> 16) ==
+ ((table[u] >> 16) - 1))))
+ u++;
+ if (font != NOT_AVAILABLE)
+ printf(" %x-%x => %u \"%s\"\n",
+ (plane << 16) | (table[prev] >> 16),
+ (plane << 16) | (table[u - 1] >> 16),
+ font, rufl_font_list[font].identifier);
+ }
+
+ free(table);
+}
+
+static size_t rufl_substitution_table_size_chd(
+ const struct rufl_substitution_table *ts,
+ unsigned int *glyph_count)
+{
+ const struct rufl_substitution_table_chd *t = (const void *) ts;
+ size_t size = sizeof(*t);
+ unsigned int count = 0;
+ uint32_t i;
+
+ /* Add on displacement map size */
+ size += (t->num_buckets * t->bits_per_entry + 7) >> 3;
+
+ /* Add on table size */
+ size += t->num_slots * sizeof(*t->table);
+
+ /* Count glyphs */
+ for (i = 0; i < t->num_slots; i++) {
+ if ((t->table[i] & 0xffff) != NOT_AVAILABLE)
+ count++;
+ }
+ if (glyph_count != NULL)
+ *glyph_count = count;
+
+ return size;
+}
+
+/**
+ * Create the final substitution table from the intermediate parts
+ *
+ * \param table Substitution table
+ * \param table_entries Number of entries in table
+ * \param buckets Number of CHD buckets
+ * \param range Number of slots in final table
+ * \param max_displacement max(displacements)
+ * \param displacements Table of displacement values. One per bucket.
+ * \param substitution_table Location to receive result.
+ */
+static rufl_code create_substitution_table_chd(uint64_t *table,
+ size_t table_entries, uint32_t buckets, uint32_t range,
+ uint32_t max_displacement, uint32_t *displacements,
+ struct rufl_substitution_table **substitution_table)
+{
+ struct rufl_substitution_table_chd *subst_table;
+ uint64_t *t64;
+ size_t subst_table_size;
+ unsigned int i;
+
+#ifdef RUFL_SUBSTITUTION_TABLE_DEBUG
+ LOG("max displacement of %u requires %u bits",
+ max_displacement, bits_needed(max_displacement));
+#endif
+
+ subst_table_size = offsetof(struct rufl_substitution_table_chd,
+ displacement_map) +
+ ((buckets * bits_needed(max_displacement) + 7) >> 3);
+
+ subst_table = calloc(subst_table_size, 1);
+ if (!subst_table)
+ return rufl_OUT_OF_MEMORY;
+
+ /* We know there are at least table_entries in the table, but
+ * we should now resize it to the size of the target hashtable.
+ * We still want each entry to be 64bits wide at this point. */
+ t64 = realloc(table, range * sizeof(*t64));
+ if (!t64) {
+ free(subst_table);
+ return rufl_OUT_OF_MEMORY;
+ }
+ /* Initialise unused slots */
+ for (i = table_entries; i < range; i++) {
+ t64[i] = NOT_AVAILABLE;
+ }
+
+ subst_table->base.desc = "CHD";
+ subst_table->base.lookup = rufl_substitution_table_lookup_chd;
+ subst_table->base.free = rufl_substitution_table_free_chd;
+ subst_table->base.dump = rufl_substitution_table_dump_chd;
+ subst_table->base.size = rufl_substitution_table_size_chd;
+ subst_table->num_buckets = buckets;
+ subst_table->num_slots = range;
+ subst_table->bits_per_entry = bits_needed(max_displacement);
+ subst_table->table = (uint32_t *) t64;
+
+ /* Fill in displacement map */
+ //XXX: compress map using Fredriksson-Nikitin encoding?
+ for (i = 0; i < buckets; i++) {
+ uint32_t offset_bits = i * subst_table->bits_per_entry;
+ uint32_t bits_to_write = subst_table->bits_per_entry;
+ uint8_t *pwrite =
+ &subst_table->displacement_map[offset_bits >> 3];
+
+ offset_bits &= 7;
+
+ while (bits_to_write > 0) {
+ uint32_t space_available = (8 - offset_bits);
+ uint32_t mask = 0, mask_idx;
+
+ if (space_available > bits_to_write)
+ space_available = bits_to_write;
+
+ for (mask_idx = 0; mask_idx != space_available;
+ mask_idx++) {
+ mask <<= 1;
+ mask |= 1;
+ }
+
+ *pwrite |= ((displacements[i] >>
+ (bits_to_write - space_available)) & mask) <<
+ (8 - offset_bits - space_available);
+ pwrite++;
+ offset_bits = 0;
+ bits_to_write -= space_available;
+ }
+ }
+
+ /* Shuffle table data so the indices match the hash values */
+ for (i = 0; i < table_entries; ) {
+ uint32_t f, g;
+ uint64_t tmp;
+
+ if (t64[i] == NOT_AVAILABLE) {
+ i++;
+ continue;
+ }
+
+ g = ((t64[i] >> 32) & 0xffff);
+ f = hash2((t64[i] >> 16) & 0xffff,
+ displacements[g]) & (range - 1);
+
+ /* Exchange this entry with the one in the slot at f.*/
+ if (f != i) {
+ tmp = t64[f];
+ t64[f] = t64[i];
+ t64[i] = tmp;
+ } else {
+ /* Reconsider this slot unless it already
+ * had the correct entry */
+ i++;
+ }
+ }
+ /* Strip all the CHD metadata out of the final table.
+ * We can simply drop the top 32bits of each entry by
+ * compacting the entries. */
+ for (i = 0; i < range; i++) {
+ subst_table->table[i] = t64[i] & 0xffffffffu;
+ }
+
+ /* Shrink the table to its final size. If this fails, leave
+ * the existing data intact as it's correct -- we just have
+ * twice the storage usage we need. */
+ subst_table->table = realloc(t64,
+ range * sizeof(*subst_table->table));
+ if (!subst_table->table)
+ subst_table->table = (uint32_t *) t64;
+
+ *substitution_table = &subst_table->base;
+
+#ifdef RUFL_SUBSTITUTION_TABLE_DEBUG
+ LOG("table size(%zu) entries %zu buckets(%u@%ubpe => %u)",
+ subst_table->num_slots * sizeof(*subst_table->table),
+ table_entries,
+ subst_table->num_buckets,
+ subst_table->bits_per_entry,
+ (subst_table->num_buckets *
+ subst_table->bits_per_entry + 7) >> 3);
+#endif
+
+ return rufl_OK;
+}
+
+/**
+ * Compute a perfect hash to address the substitution table.
+ *
+ * We use the CHD algorithm to do this.
+ * (https://doi.org/10.1007/978-3-642-04128-0_61 ;
+ * http://cmph.sourceforge.net/papers/esa09.pdf)
+ *
+ * A more recent alternative might be RecSplit
+ * (https://arxiv.org/abs/1910.06416v2).
+ *
+ * \param table Pre-filled table of raw substitution data
+ * \param table_entries Number of entries in the table
+ * \param substitution_table Location to receive result
+ */
+static rufl_code chd(uint64_t *table, size_t table_entries,
+ struct rufl_substitution_table **substitution_table)
+{
+ /** Number of buckets assuming an average bucket size of 4 */
+ const uint32_t buckets = ceil2((table_entries + 3) & ~3);
+ /** Number of output hash slots assuming a load factor of 1 */
+ const uint32_t range = ceil2(table_entries);
+ uint32_t bucket_size, max_displacement = 0;
+ unsigned int i;
+ uint8_t *entries_per_bucket, *bitmap;
+ uint32_t *displacements;
+ rufl_code result = rufl_OK;
+
+#ifdef RUFL_SUBSTITUTION_TABLE_DEBUG
+ LOG("hashing %zu entries into %u buckets with range %u",
+ table_entries, buckets, range);
+#endif
+
+ entries_per_bucket = calloc(buckets, sizeof(*entries_per_bucket));
+ if (!entries_per_bucket)
+ return rufl_OUT_OF_MEMORY;
+
+ /* Round up bitmap size to the next byte boundary */
+ bitmap = calloc(((range + 7) & ~7) >> 3, 1);
+ if (!bitmap) {
+ free(entries_per_bucket);
+ return rufl_OUT_OF_MEMORY;
+ }
+
+ displacements = calloc(buckets, sizeof(*displacements));
+ if (!displacements) {
+ free(bitmap);
+ free(entries_per_bucket);
+ return rufl_OUT_OF_MEMORY;
+ }
+
+ /* Compute g(x) for each entry, placing them into buckets */
+ for (i = 0; i < table_entries; i++) {
+ uint64_t g = hash1((table[i] >> 16) & 0xffff) & (buckets - 1);
+
+ /* Insert hash into entry (it's 16 bits at most,
+ * so use bits 32-47) */
+ table[i] |= ((g & 0xffff) << 32);
+
+ entries_per_bucket[g]++;
+ }
+
+ /* Inject bucket size into entries */
+ for (i = 0; i < table_entries; i++) {
+ uint32_t g = ((table[i] >> 32) & 0xffff);
+
+ /* With a target bucket size of 4, do not expect
+ * >= twice that number of entries in the largest
+ * bucket. If there are, the hash function needs
+ * work (we allocate 4 bits for the bucket size,
+ * so should have sufficient headroom). */
+ if (entries_per_bucket[g] >= 8)
+ LOG("unexpectedly large bucket %u",
+ entries_per_bucket[g]);
+
+ /* Stash bucket size into bits 48-51 of the entry */
+ table[i] |= ((uint64_t)entries_per_bucket[g] << 48);
+ }
+
+ /* Bits 52-63 of table entries are currently unused */
+
+ free(entries_per_bucket);
+
+ /* Sort entries in descending bucket size order */
+ qsort(table, table_entries, sizeof(*table), table_chd_cmp);
+
+ /* Compute f(x) for each bucket, finding a unique mapping */
+ for (i = 0; i < table_entries; i += bucket_size) {
+ const uint32_t g = ((table[i] >> 32) & 0xffff);
+ uint32_t hashes[8], num_hashes;
+ uint32_t d = 0;
+
+ bucket_size = ((table[i] >> 48) & 0xf);
+
+ do {
+ uint32_t j, k;
+
+ d++;
+ num_hashes = 0;
+
+ for (j = 0; j != bucket_size; j++) {
+ uint32_t f = hash2(
+ (table[i+j] >> 16) & 0xffff, d) &
+ (range - 1);
+ for (k = 0; k < num_hashes; k++) {
+ if (f == hashes[k])
+ break;
+ }
+ if (k == num_hashes) {
+ hashes[num_hashes] = f;
+ num_hashes++;
+ }
+ }
+ } while (num_hashes != bucket_size || !test_and_set_bits(
+ bitmap, hashes, num_hashes));
+
+ displacements[g] = d;
+ if (d > max_displacement)
+ max_displacement = d;
+ }
+
+ free(bitmap);
+
+ result = create_substitution_table_chd(table, table_entries,
+ buckets, range, max_displacement, displacements,
+ substitution_table);
+ free(displacements);
+
+ return result;
+}
+
+static size_t rufl_substitution_table_estimate_size_chd(size_t table_entries,
+ size_t blocks_used)
+{
+ size_t size = sizeof(struct rufl_substitution_table_chd);
+
+ (void) blocks_used;
+
+ /** Number of buckets assuming an average bucket size of 4 */
+ const uint32_t buckets = ceil2((table_entries + 3) & ~3);
+ /** Number of output hash slots assuming a load factor of 1 */
+ const uint32_t range = ceil2(table_entries);
+
+ /* Conservatively assume 6 bits per displacement map entry */
+ size += (buckets * 6 + 7) >> 3;
+
+ /* Add on table size */
+ size += range * 4;
+
+ return size;
+}
+
+/****************************************************************************/
+
+static void rufl_substitution_table_free_direct(
+ struct rufl_substitution_table *t)
+{
+ free(((struct rufl_substitution_table_direct *)t)->table);
+ free(t);
+}
+
+
+static unsigned int rufl_substitution_table_lookup_direct(
+ const struct rufl_substitution_table *ts, uint32_t u)
+{
+ const struct rufl_substitution_table_direct *t = (const void *) ts;
+ uint32_t block = (u >> 8) & 0xff;
+ uint32_t slot = (u & 0xff);
+ unsigned int font;
+
+ if (t->bits_per_entry == 8) {
+ font = ((uint8_t *) t->table)[t->index[block] * 256 + slot];
+ if (font == (NOT_AVAILABLE & 0xff))
+ font = NOT_AVAILABLE;
+ } else
+ font = t->table[t->index[block] * 256 + slot];
+
+ return font;
+}
+
+static void rufl_substitution_table_dump_direct(
+ const struct rufl_substitution_table *ts, unsigned int plane)
+{
+ const struct rufl_substitution_table_direct *t = (const void *) ts;
+ unsigned int font, na;
+ unsigned int u, prev;
+ uint8_t *t8 = (uint8_t *) t->table;
+
+ na = NOT_AVAILABLE & ((t->bits_per_entry == 8) ? 0xff : 0xffff);
+
+#define LOOKUP(u) (t->bits_per_entry == 8 \
+ ? t8[t->index[(u >> 8) & 0xff] * 256 + (u & 0xff)] \
+ : t->table[t->index[(u >> 8) & 0xff] * 256 + (u & 0xff)])
+
+ u = 0;
+ while (u < 0x10000) {
+ prev = u;
+ font = LOOKUP(u);
+ while (u < 0x10000 && font == LOOKUP(u))
+ u++;
+ if (font != na)
+ printf(" %x-%x => %u \"%s\"\n",
+ (plane << 16) | prev,
+ (plane << 16) | (u - 1),
+ font, rufl_font_list[font].identifier);
+ }
+
+#undef LOOKUP
+}
+
+static size_t rufl_substitution_table_size_direct(
+ const struct rufl_substitution_table *ts,
+ unsigned int *glyph_count)
+{
+ const struct rufl_substitution_table_direct *t = (const void *) ts;
+ size_t size = sizeof(*t);
+ unsigned int i, block_idx = 0;
+ unsigned int count = 0, na;
+
+ /* Find the largest block index (blocks are contiguous) */
+ for (i = 0; i < 256; i++)
+ if (t->index[i] > block_idx)
+ block_idx = t->index[i];
+
+ /* Add on table size */
+ size += (t->bits_per_entry * (block_idx + 1) * 256) >> 3;
+
+ /* Count glyphs */
+ na = NOT_AVAILABLE & ((t->bits_per_entry == 8) ? 0xff : 0xffff);
+ for (i = 0; i < 0x10000; i++) {
+ const uint8_t *t8 = (const uint8_t *) t->table;
+
+#define LOOKUP(u) (t->bits_per_entry == 8 \
+ ? t8[t->index[(u >> 8) & 0xff] * 256 + (u & 0xff)] \
+ : t->table[t->index[(u >> 8) & 0xff] * 256 + (u & 0xff)])
+
+ if (LOOKUP(i) != na)
+ count++;
+#undef LOOKUP
+ }
+ if (glyph_count != NULL)
+ *glyph_count = count;
+
+ return size;
+}
+
+/**
+ * Construct a direct-mapped substitution table
+ */
+static rufl_code direct(uint64_t *table, size_t table_entries,
+ size_t blocks_used, const uint8_t block_histogram[256],
+ struct rufl_substitution_table **substitution_table)
+{
+ struct rufl_substitution_table_direct *subst_table;
+ size_t blocks_needed, table_size;
+ unsigned int i, block;
+
+ subst_table = calloc(sizeof(*subst_table), 1);
+ if (!subst_table)
+ return rufl_OUT_OF_MEMORY;
+
+ subst_table->base.desc = "Direct";
+ subst_table->base.lookup = rufl_substitution_table_lookup_direct;
+ subst_table->base.free = rufl_substitution_table_free_direct;
+ subst_table->base.dump = rufl_substitution_table_dump_direct;
+ subst_table->base.size = rufl_substitution_table_size_direct;
+ /* We can use 8bits per entry if there are fewer than 255 fonts */
+ subst_table->bits_per_entry = rufl_font_list_entries < 255 ? 8 : 16;
+
+ /* Need one extra block if there's at least one free */
+ blocks_needed = (blocks_used < 256) ?
+ (blocks_used + 1) : blocks_used;
+
+ table_size = (256 * blocks_needed * subst_table->bits_per_entry) >> 3;
+
+ /* Populate block index */
+ for (i = 0, block = 0; i != 256; i++) {
+ if (block_histogram[i] == 0) {
+ subst_table->index[i] = blocks_used;
+ } else {
+ subst_table->index[i] = block;
+ block++;
+ }
+ }
+
+#ifdef RUFL_SUBSTITUTION_TABLE_DEBUG
+ LOG("blocks-used = %zu blocks-needed = %zu"
+ " bits-per-entry = %u size = %zu",
+ blocks_used, blocks_needed, subst_table->bits_per_entry,
+ table_size);
+#endif
+
+ /* Allocate table */
+ //XXX: can we just rearrange the existing one in-place?
+ subst_table->table = malloc(table_size * (subst_table->bits_per_entry >> 3));
+ if (!subst_table->table) {
+ free(subst_table);
+ return rufl_OUT_OF_MEMORY;
+ }
+ /* Fill it with NOT_AVAILABLE */
+ if (subst_table->bits_per_entry == 8) {
+ memset(subst_table->table, (NOT_AVAILABLE & 0xff), table_size);
+ } else {
+ for (i = 0; i < table_size; i++)
+ subst_table->table[i] = NOT_AVAILABLE;
+ }
+
+ /* Populate the table */
+ for (i = 0; i < table_entries; i++) {
+ uint64_t val = table[i];
+ uint32_t slot = (val >> 16) & 0xff;
+ block = subst_table->index[(val >> 24) & 0xff];
+
+ if (subst_table->bits_per_entry == 8) {
+ uint8_t *t8 = (uint8_t *) subst_table->table;
+ t8[256 * block + slot] = (val & 0xff);
+ } else {
+ subst_table->table[256 * block + slot] = (val & 0xffff);
+ }
+ }
+
+ free(table);
+
+ *substitution_table = &subst_table->base;
+
+ return rufl_OK;
+}
+
+static size_t rufl_substitution_table_estimate_size_direct(size_t table_entries,
+ size_t blocks_used)
+{
+ size_t size = sizeof(struct rufl_substitution_table_direct);
+
+ (void) table_entries;
+
+ /* Add one for empty block */
+ if (blocks_used < 256)
+ blocks_used += 1;
+
+ /* We can use 8bits per entry if there are fewer than 255 fonts */
+ size += blocks_used * 256 * (rufl_font_list_entries < 255 ? 1 : 2);
+
+ return size;
+}
+
+/****************************************************************************/
+
+/**
+ * Populate the substitution map for a given block
+ */
+static void fill_map_for_block(const struct rufl_character_set **charsets,
+ uint32_t block, uint16_t map_for_block[256])
+{
+ unsigned int i, u;
+
+ for (i = 0; i != rufl_font_list_entries; i++) {
+ if (!charsets[i])
+ continue;
+
+ if (charsets[i]->index[block] == BLOCK_FULL) {
+ for (u = 0; u != 256; u++)
+ if (map_for_block[u] == NOT_AVAILABLE)
+ map_for_block[u] = i;
+ } else if (charsets[i]->index[block] != BLOCK_EMPTY) {
+ const uint8_t *blk = charsets[i]->block[
+ charsets[i]->index[block]];
+ for (u = 0; u != 256; u++) {
+ if (map_for_block[u] == NOT_AVAILABLE &&
+ (blk[(u>>3)] & (1 << (u&7)))) {
+ map_for_block[u] = i;
+ }
+ }
+ }
+ }
+}
+
+/**
+ * Create a substitution table for the plane specified
+ */
+static rufl_code create_substitution_table_for_plane(unsigned int plane)
+{
+ unsigned int i;
+ unsigned int block;
+ unsigned int u;
+ const struct rufl_character_set **charsets;
+ const struct rufl_character_set *charset;
+ unsigned int num_charsets;
+ uint64_t *table;
+ size_t table_size;
+ size_t table_entries;
+ uint8_t block_histogram[256];
+ size_t blocks_used;
+ size_t direct_size, chd_size;
+ rufl_code result;
+
+ charsets = malloc(rufl_font_list_entries * sizeof(*charsets));
+ if (!charsets) {
+ LOG("malloc(%zu) failed",
+ rufl_font_list_entries * sizeof(*charsets));
+ return rufl_OUT_OF_MEMORY;
+ }
+
+ /* Find fonts that have charsets for this plane */
+ num_charsets = 0;
+ for (i = 0; i != rufl_font_list_entries; i++) {
+ charset = rufl_font_list[i].charset;
+ if (!charset) {
+ charsets[i] = NULL;
+ continue;
+ }
+
+ while (PLANE_ID(charset->metadata) != plane &&
+ EXTENSION_FOLLOWS(charset->metadata)) {
+ charset = (void *)(((uint8_t *)charset) +
+ PLANE_SIZE(charset->metadata));
+ }
+ if (PLANE_ID(charset->metadata) != plane)
+ charset = NULL;
+ charsets[i] = charset;
+ num_charsets++;
+ }
+ if (num_charsets == 0) {
+#ifdef RUFL_SUBSTITUTION_TABLE_DEBUG
+ LOG("no charsets for plane %u", plane);
+#endif
+ rufl_substitution_table[plane] = NULL;
+ free(charsets);
+ return rufl_OK;
+ }
+
+ table = malloc(1024 * sizeof(*table));
+ if (!table) {
+ LOG("malloc(%zu) failed", 1024 * sizeof(*table));
+ free(charsets);
+ return rufl_OUT_OF_MEMORY;
+ }
+ table_size = 1024;
+ table_entries = 0;
+
+ /* Process each block, finding fonts that have glyphs */
+ blocks_used = 0;
+ memset(block_histogram, 0, 256);
+ for (block = 0; block != 256; block++) {
+ size_t prev_table_entries = table_entries;
+ uint16_t map_for_block[256];
+ for (i = 0; i != 256; i++)
+ map_for_block[i] = NOT_AVAILABLE;
+
+ fill_map_for_block(charsets, block, map_for_block);
+
+ /* Merge block map into table */
+ for (i = 0; i != 256; i++) {
+ if (map_for_block[i] == NOT_AVAILABLE)
+ continue;
+
+ u = (block << 8) | i;
+ table[table_entries] = (u << 16) | map_for_block[i];
+ if (++table_entries == table_size) {
+ uint64_t *tmp = realloc(table,
+ 2 * table_size *
+ sizeof(*table));
+ if (!tmp) {
+ LOG("realloc(%zu) failed",
+ 2 * table_size *
+ sizeof(*table));
+ free(table);
+ return rufl_OUT_OF_MEMORY;
+ }
+
+ table = tmp;
+ table_size *= 2;
+ }
+ }
+
+ block_histogram[block] = (table_entries != prev_table_entries);
+ if (block_histogram[block] != 0)
+ blocks_used++;
+ }
+
+ if (table_entries == 0) {
+#ifdef RUFL_SUBSTITUTION_TABLE_DEBUG
+ LOG("no glyphs for plane %u", plane);
+#endif
+ rufl_substitution_table[plane] = NULL;
+ free(table);
+ free(charsets);
+ return rufl_OK;
+ }
+
+ /* Build final substitution table using whichever implementation
+ * estimates the smallest storage requirements. */
+ direct_size = rufl_substitution_table_estimate_size_direct(
+ table_entries, blocks_used);
+ chd_size = rufl_substitution_table_estimate_size_chd(
+ table_entries, blocks_used);
+ if (direct_size <= chd_size) {
+ result = direct(table, table_entries, blocks_used,
+ block_histogram,
+ &rufl_substitution_table[plane]);
+ } else {
+ result = chd(table, table_entries,
+ &rufl_substitution_table[plane]);
+ }
+
+#ifdef RUFL_SUBSTITUTION_TABLE_DEBUG
+ LOG("plane %u: table-entries = %zu blocks-used = %zu"
+ " estimated-direct-size = %zu estimated-chd-size = %zu"
+ " actual-size = %zu",
+ plane, table_entries, blocks_used,
+ direct_size, chd_size,
+ rufl_substitution_table[plane] ?
+ rufl_substitution_table[plane]->size(
+ rufl_substitution_table[plane],
+ NULL) : 0);
+#endif
+
+ free(charsets);
+
+ return result;
+}
+
+/**
+ * Construct the font substitution table.
+ */
+
+rufl_code rufl_substitution_table_init(void)
+{
+ unsigned int plane;
+ rufl_code rc;
+
+ for (plane = 0; plane < 17; plane++) {
+ rc = create_substitution_table_for_plane(plane);
+ if (rc != rufl_OK) {
+ while (plane > 0) {
+ plane--;
+ if (!rufl_substitution_table[plane])
+ continue;
+ rufl_substitution_table[plane]->free(
+ rufl_substitution_table[plane]);
+ }
+ return rc;
+ }
+ }
+
+ return rufl_OK;
+}
+
+/**
+ * Destroy the substitution table and clean up its resources
+ */
+
+void rufl_substitution_table_fini(void)
+{
+ unsigned int plane;
+
+ for (plane = 0; plane < 17; plane++) {
+ if (rufl_substitution_table[plane] != NULL)
+ rufl_substitution_table[plane]->free(
+ rufl_substitution_table[plane]);
+ rufl_substitution_table[plane] = NULL;
+ }
+}
+
+/**
+ * Look up a Unicode codepoint in the substitution table
+ */
+
+unsigned int rufl_substitution_table_lookup(uint32_t u)
+{
+ unsigned int plane = (u >> 16) & 0x1f;
+
+ if (17 <= plane || !rufl_substitution_table[plane])
+ return NOT_AVAILABLE;
+
+ return rufl_substitution_table[plane]->lookup(
+ rufl_substitution_table[plane], u);
+}
+
+/**
+ * Dump a representation of the substitution table to stdout.
+ */
+
+void rufl_substitution_table_dump(void)
+{
+ unsigned int plane, glyphs = 0;
+ size_t size = 0;
+
+ for (plane = 0; plane < 17; plane++) {
+ if (!rufl_substitution_table[plane])
+ continue;
+ rufl_substitution_table[plane]->dump(
+ rufl_substitution_table[plane], plane);
+ }
+
+ for (plane = 0; plane < 17; plane++) {
+ size_t plane_size;
+ unsigned int plane_glyphs;
+ const char *plane_desc;
+
+ if (!rufl_substitution_table[plane]) {
+ plane_size = 0;
+ plane_glyphs = 0;
+ plane_desc = "None";
+ } else {
+ plane_size = rufl_substitution_table[plane]->size(
+ rufl_substitution_table[plane], &plane_glyphs);
+ plane_desc = rufl_substitution_table[plane]->desc;
+ }
+ size += plane_size;
+ glyphs += plane_glyphs;
+ printf(" Storage for plane %2d: %8zu bytes %7u glyphs (%s)\n",
+ plane + 1, plane_size, plane_glyphs, plane_desc);
+ }
+
+ printf(" Total substitution table storage: %8zu bytes %7u glyphs\n",
+ size + sizeof(rufl_substitution_table), glyphs);
+}
diff --git a/test/INDEX b/test/INDEX
new file mode 100644
index 0000000..417b032
--- /dev/null
+++ b/test/INDEX
@@ -0,0 +1,8 @@
+# Index for testcases
+#
+# Test Description DataDir
+nofonts Ensure a lack of fonts "works"
+ucsinit Ensure that UCS FM initialisation works
+olducsinit Ensure that UCS FM (pre 3.64) initialisation works
+oldfminit Ensure that non-UCS FM initialisation works oldfminit
+manyfonts Ensure that more than 256 fonts works
diff --git a/test/Makefile b/test/Makefile
index 2bfa58b..65c3170 100644
--- a/test/Makefile
+++ b/test/Makefile
@@ -1,4 +1,16 @@
# Tests
-DIR_TEST_ITEMS := rufl_test:rufl_test.c rufl_chars:rufl_chars.c
+ifeq ($(findstring -riscos,$(HOST)),-riscos)
+ DIR_TEST_ITEMS := $(DIR_TEST_ITEMS) \
+ rufl_test:rufl_test.c \
+ rufl_chars:rufl_chars.c
+ # We do not want to run tests if building for RISC OS
+ TESTRUNNER := echo
+else
+ DIR_TEST_ITEMS := nofonts:nofonts.c;harness.c;mocks.c \
+ oldfminit:oldfminit.c;harness.c;mocks.c \
+ olducsinit:olducsinit.c;harness.c;mocks.c \
+ ucsinit:ucsinit.c;harness.c;mocks.c \
+ manyfonts:manyfonts.c;harness.c;mocks.c
+endif
include $(NSBUILD)/Makefile.subdir
diff --git a/test/data/Encoding b/test/data/oldfminit/Allerta
index e5b633e..e5b633e 100644
--- a/test/data/Encoding
+++ b/test/data/oldfminit/Allerta
diff --git a/test/data/oldfminit/INDEX b/test/data/oldfminit/INDEX
new file mode 100644
index 0000000..c2d250f
--- /dev/null
+++ b/test/data/oldfminit/INDEX
@@ -0,0 +1,10 @@
+# Index file for non-UCS FM initialisation tests
+#
+# Test Description
+
+latin1.cfg Simple Latin1 Encoding
+mergeumap.cfg Merge identical umaps
+nomapping.cfg Fonts with no mapping
+symbol.cfg Simple symbol fonts
+
+brokenencoding.cfg Garbage encoding file
diff --git a/test/data/oldfminit/Latin1 b/test/data/oldfminit/Latin1
new file mode 100644
index 0000000..6821aa8
--- /dev/null
+++ b/test/data/oldfminit/Latin1
@@ -0,0 +1,270 @@
+% Acorn_Latin1Encoding 1.00 0
+
+%%RISCOS_BasedOn 0
+%%RISCOS_Alphabet 101
+
+% These first characters are for use by PostScript printer driver ONLY,
+% they are not accessible using the RISC OS font manager.
+/ring
+/circumflex
+/tilde
+/dotlessi
+/.notdef
+/.notdef
+/.notdef
+/.notdef
+/.notdef
+/.notdef
+/.notdef
+/.notdef
+/.notdef
+/.notdef
+/.notdef
+/.notdef
+/.notdef
+/.notdef
+/.notdef
+/.notdef
+/.notdef
+/.notdef
+/.notdef
+/.notdef
+/.notdef
+/.notdef
+/.notdef
+/.notdef
+/.notdef
+/.notdef
+/.notdef
+/.notdef
+
+/space
+/exclam
+/quotedbl
+/numbersign
+/dollar
+/percent
+/ampersand
+/quotesingle
+/parenleft
+/parenright
+/asterisk
+/plus
+/comma
+/hyphen
+/period
+/slash
+/zero
+/one
+/two
+/three
+/four
+/five
+/six
+/seven
+/eight
+/nine
+/colon
+/semicolon
+/less
+/equal
+/greater
+/question
+
+/at
+/A
+/B
+/C
+/D
+/E
+/F
+/G
+/H
+/I
+/J
+/K
+/L
+/M
+/N
+/O
+/P
+/Q
+/R
+/S
+/T
+/U
+/V
+/W
+/X
+/Y
+/Z
+/bracketleft
+/backslash
+/bracketright
+/asciicircum
+/underscore
+
+/grave
+/a
+/b
+/c
+/d
+/e
+/f
+/g
+/h
+/i
+/j
+/k
+/l
+/m
+/n
+/o
+/p
+/q
+/r
+/s
+/t
+/u
+/v
+/w
+/x
+/y
+/z
+/braceleft
+/bar
+/braceright
+/asciitilde
+/.notdef
+
+/Euro
+/Wcircumflex
+/wcircumflex
+/.notdef
+/.notdef
+/Ycircumflex
+/ycircumflex
+/special1
+/special2
+/special3
+/special4
+/special5
+/ellipsis
+/trademark
+/perthousand
+/bullet
+/quoteleft
+/quoteright
+/guilsinglleft
+/guilsinglright
+/quotedblleft
+/quotedblright
+/quotedblbase
+/endash
+/emdash
+/minus
+/OE
+/oe
+/dagger
+/daggerdbl
+/fi
+/fl
+
+/space
+/exclamdown
+/cent
+/sterling
+/currency
+/yen
+/brokenbar
+/section
+/dieresis
+/copyright
+/ordfeminine
+/guillemotleft
+/logicalnot
+/hyphen
+/registered
+/macron
+/degree
+/plusminus
+/twosuperior
+/threesuperior
+/acute
+/mu
+/paragraph
+/periodcentered
+/cedilla
+/onesuperior
+/ordmasculine
+/guillemotright
+/onequarter
+/onehalf
+/threequarters
+/questiondown
+
+/Agrave
+/Aacute
+/Acircumflex
+/Atilde
+/Adieresis
+/Aring
+/AE
+/Ccedilla
+/Egrave
+/Eacute
+/Ecircumflex
+/Edieresis
+/Igrave
+/Iacute
+/Icircumflex
+/Idieresis
+/Eth
+/Ntilde
+/Ograve
+/Oacute
+/Ocircumflex
+/Otilde
+/Odieresis
+/multiply
+/Oslash
+/Ugrave
+/Uacute
+/Ucircumflex
+/Udieresis
+/Yacute
+/Thorn
+/germandbls
+
+/agrave
+/aacute
+/acircumflex
+/atilde
+/adieresis
+/aring
+/ae
+/ccedilla
+/egrave
+/eacute
+/ecircumflex
+/edieresis
+/igrave
+/iacute
+/icircumflex
+/idieresis
+/eth
+/ntilde
+/ograve
+/oacute
+/ocircumflex
+/otilde
+/odieresis
+/divide
+/oslash
+/ugrave
+/uacute
+/ucircumflex
+/udieresis
+/yacute
+/thorn
+/ydieresis
diff --git a/test/data/oldfminit/brokenencoding.cfg b/test/data/oldfminit/brokenencoding.cfg
new file mode 100644
index 0000000..b5e12c1
--- /dev/null
+++ b/test/data/oldfminit/brokenencoding.cfg
@@ -0,0 +1,28 @@
+# Configuration for broken encoding file
+
+%expumaps Corpus.Bold 0
+%expumaps Corpus.Bold.Oblique 0
+%expumaps Corpus.Medium 1
+%expumaps Corpus.Medium.Oblique 0
+%expumaps Homerton.Bold 0
+%expumaps Homerton.Bold.Oblique 0
+%expumaps Homerton.Medium 1
+%expumaps Homerton.Medium.Oblique 0
+%expumaps Trinity.Bold 0
+%expumaps Trinity.Bold.Italic 0
+%expumaps Trinity.Medium 1
+%expumaps Trinity.Medium.Italic 0
+
+# Font name Encoding name Filename
+Corpus.Bold Latin1 Allerta
+Corpus.Bold.Oblique Latin1 Allerta
+Corpus.Medium Latin1 Latin1
+Corpus.Medium.Oblique Latin1 Allerta
+Homerton.Bold Latin1 Allerta
+Homerton.Bold.Oblique Latin1 Allerta
+Homerton.Medium Latin1 Latin1
+Homerton.Medium.Oblique Latin1 Allerta
+Trinity.Bold Latin1 Allerta
+Trinity.Bold.Italic Latin1 Allerta
+Trinity.Medium Latin1 Latin1
+Trinity.Medium.Italic Latin1 Allerta
diff --git a/test/data/oldfminit/latin1.cfg b/test/data/oldfminit/latin1.cfg
new file mode 100644
index 0000000..646582a
--- /dev/null
+++ b/test/data/oldfminit/latin1.cfg
@@ -0,0 +1,28 @@
+# Configuration for Latin1 language fonts
+
+%expumaps Corpus.Bold 1
+%expumaps Corpus.Bold.Oblique 1
+%expumaps Corpus.Medium 1
+%expumaps Corpus.Medium.Oblique 1
+%expumaps Homerton.Bold 1
+%expumaps Homerton.Bold.Oblique 1
+%expumaps Homerton.Medium 1
+%expumaps Homerton.Medium.Oblique 1
+%expumaps Trinity.Bold 1
+%expumaps Trinity.Bold.Italic 1
+%expumaps Trinity.Medium 1
+%expumaps Trinity.Medium.Italic 1
+
+# Font name Encoding name Filename
+Corpus.Bold Latin1 Latin1
+Corpus.Bold.Oblique Latin1 Latin1
+Corpus.Medium Latin1 Latin1
+Corpus.Medium.Oblique Latin1 Latin1
+Homerton.Bold Latin1 Latin1
+Homerton.Bold.Oblique Latin1 Latin1
+Homerton.Medium Latin1 Latin1
+Homerton.Medium.Oblique Latin1 Latin1
+Trinity.Bold Latin1 Latin1
+Trinity.Bold.Italic Latin1 Latin1
+Trinity.Medium Latin1 Latin1
+Trinity.Medium.Italic Latin1 Latin1
diff --git a/test/data/oldfminit/mergeumap.cfg b/test/data/oldfminit/mergeumap.cfg
new file mode 100644
index 0000000..654a0bf
--- /dev/null
+++ b/test/data/oldfminit/mergeumap.cfg
@@ -0,0 +1,40 @@
+# Configuration for merging duplicate umaps
+
+%expumaps Corpus.Bold 1
+%expumaps Corpus.Bold.Oblique 1
+%expumaps Corpus.Medium 1
+%expumaps Corpus.Medium.Oblique 1
+%expumaps Homerton.Bold 1
+%expumaps Homerton.Bold.Oblique 1
+%expumaps Homerton.Medium 1
+%expumaps Homerton.Medium.Oblique 1
+%expumaps Trinity.Bold 1
+%expumaps Trinity.Bold.Italic 1
+%expumaps Trinity.Medium 1
+%expumaps Trinity.Medium.Italic 1
+
+# Font name Encoding name Filename
+Corpus.Bold Latin1 Latin1
+Corpus.Bold.Oblique Latin1 Latin1
+Corpus.Medium Latin1 Latin1
+Corpus.Medium.Oblique Latin1 Latin1
+Corpus.Bold Latin2 Latin1
+Corpus.Bold.Oblique Latin2 Latin1
+Corpus.Medium Latin2 Latin1
+Corpus.Medium.Oblique Latin2 Latin1
+Homerton.Bold Latin1 Latin1
+Homerton.Bold.Oblique Latin1 Latin1
+Homerton.Medium Latin1 Latin1
+Homerton.Medium.Oblique Latin1 Latin1
+Homerton.Bold Latin2 Latin1
+Homerton.Bold.Oblique Latin2 Latin1
+Homerton.Medium Latin2 Latin1
+Homerton.Medium.Oblique Latin2 Latin1
+Trinity.Bold Latin1 Latin1
+Trinity.Bold.Italic Latin1 Latin1
+Trinity.Medium Latin1 Latin1
+Trinity.Medium.Italic Latin1 Latin1
+Trinity.Bold Latin2 Latin1
+Trinity.Bold.Italic Latin2 Latin1
+Trinity.Medium Latin2 Latin1
+Trinity.Medium.Italic Latin2 Latin1
diff --git a/test/data/oldfminit/nomapping.cfg b/test/data/oldfminit/nomapping.cfg
new file mode 100644
index 0000000..18de2fb
--- /dev/null
+++ b/test/data/oldfminit/nomapping.cfg
@@ -0,0 +1,19 @@
+# Configuration for fonts with no mapping
+
+%expumaps Corpus.Bold 0
+%expumaps Corpus.Bold.Oblique 0
+%expumaps Corpus.Medium 1
+%expumaps Corpus.Medium.Oblique 0
+%expumaps Homerton.Bold 0
+%expumaps Homerton.Bold.Oblique 0
+%expumaps Homerton.Medium 1
+%expumaps Homerton.Medium.Oblique 0
+%expumaps Trinity.Bold 0
+%expumaps Trinity.Bold.Italic 0
+%expumaps Trinity.Medium 1
+%expumaps Trinity.Medium.Italic 0
+
+# Font name Encoding name Filename
+Corpus.Medium Latin1 Latin1
+Homerton.Medium Latin1 Latin1
+Trinity.Medium Latin1 Latin1
diff --git a/test/data/oldfminit/symbol.cfg b/test/data/oldfminit/symbol.cfg
new file mode 100644
index 0000000..ca7c760
--- /dev/null
+++ b/test/data/oldfminit/symbol.cfg
@@ -0,0 +1,28 @@
+# Configuration for symbol fonts
+
+%expumaps Corpus.Bold 1
+%expumaps Corpus.Bold.Oblique 1
+%expumaps Corpus.Medium 1
+%expumaps Corpus.Medium.Oblique 1
+%expumaps Homerton.Bold 1
+%expumaps Homerton.Bold.Oblique 1
+%expumaps Homerton.Medium 1
+%expumaps Homerton.Medium.Oblique 1
+%expumaps Trinity.Bold 1
+%expumaps Trinity.Bold.Italic 1
+%expumaps Trinity.Medium 1
+%expumaps Trinity.Medium.Italic 1
+
+# Font name Encoding name Filename
+Corpus.Bold Symbol Latin1
+Corpus.Bold.Oblique Symbol Latin1
+Corpus.Medium Symbol Latin1
+Corpus.Medium.Oblique Symbol Latin1
+Homerton.Bold Symbol Latin1
+Homerton.Bold.Oblique Symbol Latin1
+Homerton.Medium Symbol Latin1
+Homerton.Medium.Oblique Symbol Latin1
+Trinity.Bold Symbol Latin1
+Trinity.Bold.Italic Symbol Latin1
+Trinity.Medium Symbol Latin1
+Trinity.Medium.Italic Symbol Latin1
diff --git a/test/harness-priv.h b/test/harness-priv.h
new file mode 100644
index 0000000..60d5135
--- /dev/null
+++ b/test/harness-priv.h
@@ -0,0 +1,45 @@
+#ifndef rufl_test_harness_priv_h_
+#define rufl_test_harness_priv_h_
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <inttypes.h>
+
+#include "harness.h"
+
+typedef struct {
+ unsigned int refcnt;
+ size_t name; /* Index of name in names array */
+#define FONT_ENCODING_SYMBOL ((size_t) -1) /* Symbol, not language, font */
+ size_t encoding; /* Index of encoding in encodings array */
+ int xsize; /* XSize of this font */
+ int ysize; /* YSize if this font */
+ int xres; /* XResolution of this font */
+ int yres; /* YResolution of this font */
+} rufl_test_harness_sized_font;
+
+typedef struct {
+ int fm_version;
+ bool fm_ucs;
+ bool fm_broken_fec;
+
+ const char **font_names;
+ size_t n_font_names;
+
+ const char **encodings;
+ size_t n_encodings;
+
+ /* n_font_names * (n_encodings + 1) entries */
+ char **encoding_filenames;
+
+ /* At most 256 active font handles */
+ rufl_test_harness_sized_font fonts[256];
+ int current_font;
+
+ char *buffer;
+ int buffer_flags;
+} rufl_test_harness_t;
+
+extern rufl_test_harness_t *h;
+
+#endif
diff --git a/test/harness.c b/test/harness.c
new file mode 100644
index 0000000..8f8ea9a
--- /dev/null
+++ b/test/harness.c
@@ -0,0 +1,139 @@
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "harness-priv.h"
+
+rufl_test_harness_t *h = NULL;
+
+static void rufl_test_harness_free(void)
+{
+ size_t ni, ei;
+
+ free(h->font_names);
+ free(h->encodings);
+ if (h->encoding_filenames != NULL) {
+ for (ni = 0; ni != h->n_font_names; ni++) {
+ for (ei = 0; ei != h->n_encodings + 1; ei++) {
+ free(h->encoding_filenames[
+ (ni * (h->n_encodings + 1)) + ei]);
+ }
+ }
+ }
+ free(h->encoding_filenames);
+ free(h);
+}
+
+void rufl_test_harness_init(int fm_version, bool fm_ucs, bool preload)
+{
+ h = calloc(1, sizeof(*h));
+ assert(h != NULL);
+
+ h->fm_version = fm_version;
+ h->fm_ucs = fm_ucs;
+ h->fm_broken_fec = fm_version < 364;
+
+ if (preload) {
+ /* Register ROM fonts as a convenience */
+ rufl_test_harness_register_font("Corpus.Bold");
+ rufl_test_harness_register_font("Corpus.Bold.Oblique");
+ rufl_test_harness_register_font("Corpus.Medium");
+ rufl_test_harness_register_font("Corpus.Medium.Oblique");
+ rufl_test_harness_register_font("Homerton.Bold");
+ rufl_test_harness_register_font("Homerton.Bold.Oblique");
+ rufl_test_harness_register_font("Homerton.Medium");
+ rufl_test_harness_register_font("Homerton.Medium.Oblique");
+ rufl_test_harness_register_font("Trinity.Bold");
+ rufl_test_harness_register_font("Trinity.Bold.Italic");
+ rufl_test_harness_register_font("Trinity.Medium");
+ rufl_test_harness_register_font("Trinity.Medium.Italic");
+
+ /* Register encodings as a convenience */
+ rufl_test_harness_register_encoding("Cyrillic");
+ rufl_test_harness_register_encoding("Greek");
+ rufl_test_harness_register_encoding("Hebrew");
+ rufl_test_harness_register_encoding("Latin1");
+ rufl_test_harness_register_encoding("Latin2");
+ rufl_test_harness_register_encoding("Latin3");
+ rufl_test_harness_register_encoding("Latin4");
+ rufl_test_harness_register_encoding("Latin5");
+ rufl_test_harness_register_encoding("Latin6");
+ rufl_test_harness_register_encoding("Latin7");
+ rufl_test_harness_register_encoding("Latin8");
+ rufl_test_harness_register_encoding("Latin9");
+ rufl_test_harness_register_encoding("Latin10");
+ if (fm_ucs)
+ rufl_test_harness_register_encoding("UTF8");
+ rufl_test_harness_register_encoding("Welsh");
+ }
+
+ atexit(rufl_test_harness_free);
+}
+
+void rufl_test_harness_register_font(const char *name)
+{
+ const char **names;
+
+ /* Encoding paths must be registered last */
+ assert(h->encoding_filenames == NULL);
+
+ names = realloc(h->font_names,
+ (h->n_font_names + 1) * sizeof(*names));
+ assert(names != NULL);
+
+ h->font_names = names;
+
+ h->font_names[h->n_font_names++] = name;
+}
+
+void rufl_test_harness_register_encoding(const char *encoding)
+{
+ const char **encodings;
+
+ /* Encoding paths must be registered last */
+ assert(h->encoding_filenames == NULL);
+
+ encodings = realloc(h->encodings,
+ (h->n_encodings + 1) * sizeof(*encodings));
+ assert(encodings != NULL);
+
+ h->encodings = encodings;
+
+ h->encodings[h->n_encodings++] = encoding;
+}
+
+void rufl_test_harness_set_font_encoding(const char *fontname,
+ const char *encoding, const char *path)
+{
+ size_t ni, ei;
+
+ if (h->encoding_filenames == NULL) {
+ h->encoding_filenames = calloc(
+ h->n_font_names * (h->n_encodings + 1),
+ sizeof(*h->encoding_filenames));
+ assert(h->encoding_filenames != NULL);
+ }
+
+ /* Find font index */
+ for (ni = 0; ni < h->n_font_names; ni++) {
+ if (strcmp(h->font_names[ni], fontname) == 0)
+ break;
+ }
+ assert(ni != h->n_font_names);
+
+ /* Find encoding index */
+ if (strcmp("Symbol", encoding) == 0) {
+ ei = h->n_encodings;
+ } else {
+ for (ei = 0; ei < h->n_encodings; ei++) {
+ if (strcmp(h->encodings[ei], encoding) == 0)
+ break;
+ }
+ assert(ei != h->n_encodings);
+ }
+
+ if (h->encoding_filenames[(ni * (h->n_encodings + 1)) + ei] != NULL)
+ free(h->encoding_filenames[(ni * (h->n_encodings + 1)) + ei]);
+ h->encoding_filenames[(ni * (h->n_encodings + 1)) + ei] = strdup(path);
+ assert(h->encoding_filenames[(ni * (h->n_encodings + 1)) + ei] != NULL);
+}
diff --git a/test/harness.h b/test/harness.h
new file mode 100644
index 0000000..b62617f
--- /dev/null
+++ b/test/harness.h
@@ -0,0 +1,10 @@
+#ifndef rufl_test_harness_h_
+#define rufl_test_harness_h_
+
+void rufl_test_harness_init(int fm_version, bool fm_ucs, bool preload);
+void rufl_test_harness_register_font(const char *name);
+void rufl_test_harness_register_encoding(const char *encoding);
+void rufl_test_harness_set_font_encoding(const char *fontname,
+ const char *encoding, const char *path);
+
+#endif
diff --git a/test/manyfonts.c b/test/manyfonts.c
new file mode 100644
index 0000000..bc4b01e
--- /dev/null
+++ b/test/manyfonts.c
@@ -0,0 +1,100 @@
+/*
+ * XXX: This test currently needs the following patch to be valid.
+ * We need a way of ensuring the library chooses the direct substitution
+ * table format (which basically means we need to flood a plane with glyphs)
+ *
+ * diff --git a/src/rufl_substitution_table.c b/src/rufl_substitution_table.c
+ * index f5de7d8..2b58b72 100644
+ * --- a/src/rufl_substitution_table.c
+ * +++ b/src/rufl_substitution_table.c
+ * @@ -1019,7 +1019,7 @@ static rufl_code create_substitution_table_for_plane(unsigned int plane)
+ * table_entries, blocks_used);
+ * chd_size = rufl_substitution_table_estimate_size_chd(
+ * table_entries, blocks_used);
+ * - if (direct_size <= chd_size) {
+ * + if (1 || direct_size <= chd_size) {
+ * result = direct(table, table_entries, blocks_used,
+ * block_histogram,
+ * &rufl_substitution_table[plane]);
+ */
+
+#include <ftw.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "rufl.h"
+
+#include "harness.h"
+#include "testutils.h"
+
+static char template[] = "/tmp/manyfontsXXXXXX";
+static const char *ptmp = NULL;
+
+static int ftw_cb(const char *path, const struct stat *sb,
+ int typeflag, struct FTW *ftwbuf)
+{
+ (void) sb;
+ (void) typeflag;
+ (void) ftwbuf;
+
+ remove(path);
+
+ return 0;
+}
+
+static void cleanup(void)
+{
+ if (ptmp == NULL)
+ return;
+
+ nftw(ptmp, ftw_cb, FOPEN_MAX, FTW_DEPTH | FTW_MOUNT | FTW_PHYS);
+}
+
+int main(int argc, const char **argv)
+{
+ char *names[300];
+ int x;
+
+ UNUSED(argc);
+ UNUSED(argv);
+
+ ptmp = mkdtemp(template);
+ assert(NULL != ptmp);
+ atexit(cleanup);
+ chdir(ptmp);
+
+ rufl_test_harness_init(380, true, true);
+
+ for (x = 0; x < 300; x++) {
+ char buf[64];
+ sprintf(buf, "Font%03d", x);
+ names[x] = strdup(buf);
+ rufl_test_harness_register_font(names[x]);
+ }
+
+ assert(rufl_OK == rufl_init());
+ assert(NULL == rufl_fm_error);
+ assert(303 == rufl_family_list_entries);
+ assert(NULL != rufl_family_menu);
+
+ rufl_dump_state(true);
+
+ rufl_quit();
+
+ /* Reinit -- should load cache */
+ assert(rufl_OK == rufl_init());
+ assert(NULL == rufl_fm_error);
+ assert(303 == rufl_family_list_entries);
+ assert(NULL != rufl_family_menu);
+ /* Done for real this time */
+ rufl_quit();
+
+ for (x = 0; x < 300; x++) {
+ free(names[x]);
+ }
+
+ printf("PASS\n");
+
+ return 0;
+}
diff --git a/test/mocks.c b/test/mocks.c
new file mode 100644
index 0000000..734211b
--- /dev/null
+++ b/test/mocks.c
@@ -0,0 +1,671 @@
+#include <assert.h>
+#include <string.h>
+
+#include <oslib/font.h>
+#include <oslib/hourglass.h>
+#include <oslib/os.h>
+#include <oslib/osfscontrol.h>
+#include <oslib/taskwindow.h>
+#include <oslib/wimp.h>
+#include <oslib/wimpreadsysinfo.h>
+
+#include "harness-priv.h"
+
+static os_error font_no_font = { error_FONT_NO_FONT, "Undefined font handle" };
+static os_error font_bad_font_number = {
+ error_FONT_BAD_FONT_NUMBER,
+ "Font handle out of range"
+};
+static os_error font_not_found = { error_FONT_NOT_FOUND, "Font not found" };
+static os_error font_encoding_not_found = {
+ error_FONT_ENCODING_NOT_FOUND,
+ "Encoding not found"
+};
+static os_error font_no_handles = {
+ error_FONT_NO_HANDLES,
+ "No more font handles"
+};
+static os_error font_reserved = {
+ error_FONT_RESERVED,
+ "Reserved fields must be zero"
+};
+static os_error buff_overflow = { error_BUFF_OVERFLOW, "Buffer overflow" };
+static os_error bad_parameters = { error_BAD_PARAMETERS, "Bad parameters" };
+static os_error no_such_swi = { error_NO_SUCH_SWI, "SWI not known" };
+static os_error unimplemented = { error_UNIMPLEMENTED, "Not implemented" };
+
+/****************************************************************************/
+
+os_error *xfont_cache_addr (int *version, int *cache_size, int *cache_used)
+{
+ if (version != NULL)
+ *version = h->fm_version;
+ if (cache_size != NULL)
+ *cache_size = 512 * 1024;
+ if (cache_used != NULL)
+ *cache_used = 0;
+
+ return NULL;
+}
+
+os_error *xfont_find_font (char const *font_name, int xsize, int ysize,
+ int xres, int yres, font_f *font, int *xres_out, int *yres_out)
+{
+ char name[80], encoding[80];
+ const char *slash;
+ size_t ni, ei;
+ int fh;
+
+ /* Default xres and yres */
+ if (xres <= 0)
+ xres = 90;
+ if (yres <= 0)
+ yres = 90;
+
+ /* Parse font name */
+ slash = strchr(font_name, '\\');
+ if (slash == NULL) {
+ /* Bare name: symbol font */
+ strncpy(name, font_name, sizeof(name));
+ name[sizeof(name)-1] = '\0';
+ strcpy(encoding, "Symbol");
+ } else {
+ /* Identifier: extract encoding */
+ strncpy(name, font_name, slash - font_name);
+ name[slash-font_name] = '\0';
+ assert(slash[1] == 'E');
+ strncpy(encoding, slash + 2, sizeof(encoding));
+ encoding[sizeof(encoding)-1] = '\0';
+ }
+
+ /* Determine if we know about this font name */
+ for (ni = 0; ni < h->n_font_names; ni++) {
+ if (strcmp(h->font_names[ni], name) == 0)
+ break;
+ }
+ if (ni == h->n_font_names) {
+ return &font_not_found;
+ }
+
+ /* Determine if we know about this encoding */
+ if (strcmp("Symbol", encoding) == 0) {
+ ei = FONT_ENCODING_SYMBOL;
+ } else {
+ for (ei = 0; ei < h->n_encodings; ei++) {
+ if (strcmp(h->encodings[ei], encoding) == 0)
+ break;
+ }
+ if (ei == h->n_encodings) {
+ return &font_encoding_not_found;
+ }
+ }
+
+ /* Find existing font handle (0 is forbidden) */
+ for (fh = 1; fh < 256; fh++) {
+ if (h->fonts[fh].refcnt > 0 && h->fonts[fh].name == ni &&
+ h->fonts[fh].encoding == ei &&
+ h->fonts[fh].xsize == xsize &&
+ h->fonts[fh].ysize == ysize &&
+ h->fonts[fh].xres == xres &&
+ h->fonts[fh].yres == yres)
+ break;
+ }
+ if (fh == 256) {
+ /* No existing font found: allocate new one */
+ for (fh = 1; fh < 256; fh++) {
+ if (h->fonts[fh].refcnt == 0)
+ break;
+ }
+ if (fh == 256) {
+ return &font_no_handles;
+ }
+
+ h->fonts[fh].name = ni;
+ h->fonts[fh].encoding = ei;
+ h->fonts[fh].xsize = xsize;
+ h->fonts[fh].ysize = ysize;
+ h->fonts[fh].xres = xres;
+ h->fonts[fh].yres = yres;
+ }
+
+ /* Bump refcnt */
+ h->fonts[fh].refcnt++;
+
+ /* Set current font */
+ h->current_font = fh;
+
+ if (font != NULL)
+ *font = (font_f) fh;
+ if (xres_out != NULL)
+ *xres_out = xres;
+ if (yres_out != NULL)
+ *yres_out = yres;
+
+ return NULL;
+}
+
+os_error *xfont_lose_font (font_f font)
+{
+ if (font != 0 && h->fonts[font].refcnt > 0)
+ h->fonts[font].refcnt--;
+
+ return NULL;
+}
+
+os_error *xfont_read_info (font_f font, int *x0, int *y0, int *x1, int *y1)
+{
+ if (font == 0)
+ return &font_bad_font_number;
+ if (h->fonts[font].refcnt == 0)
+ return &font_no_font;
+
+ /* Cheat: just scale point size to OS units */
+ if (x0 != NULL)
+ *x0 = 0;
+ if (y0 != NULL)
+ *y0 = 0;
+ if (x1 != NULL)
+ *x1 = (h->fonts[font].xsize * 180) / (72 * 16);
+ if (y1 != NULL)
+ *y1 = (h->fonts[font].ysize * 180) / (72 * 16);
+
+ return NULL;
+}
+
+os_error *xfont_read_font_metrics (font_f font, font_bbox_info *bbox_info,
+ font_width_info *xwidth_info, font_width_info *ywidth_info,
+ font_metrics_misc_info *misc_info, font_kern_info *kern_info,
+ font_metric_flags *flags, int *bbox_info_size,
+ int *xwidth_info_size, int *ywidth_info_size,
+ int *misc_info_size, int *kern_info_size)
+{
+ if (font == 0)
+ return &font_bad_font_number;
+ if (h->fonts[font].refcnt == 0)
+ return &font_no_font;
+ if (bbox_info != NULL || xwidth_info != NULL || ywidth_info != NULL ||
+ kern_info != NULL || flags != NULL)
+ return &unimplemented;
+
+ if (misc_info != NULL) {
+ os_error *err = xfont_read_info(font,
+ &misc_info->x0, &misc_info->y0,
+ &misc_info->x1, &misc_info->y1);
+ if (err != NULL)
+ return err;
+ misc_info->xkern = misc_info->ykern = 0;
+ misc_info->italic_correction = 0;
+ misc_info->underline_position = 0;
+ misc_info->underline_thickness = 0;
+ misc_info->cap_height = misc_info->y1 - misc_info->y0;
+ misc_info->xheight = misc_info->cap_height >> 1;
+ misc_info->descender = misc_info->ascender = 0;
+ }
+
+ if (bbox_info_size != NULL)
+ *bbox_info_size = 0;
+ if (xwidth_info_size != NULL)
+ *xwidth_info_size = 0;
+ if (ywidth_info_size != NULL)
+ *ywidth_info_size = 0;
+ if (misc_info_size != NULL)
+ *misc_info_size = sizeof(font_metrics_misc_info);
+ if (kern_info_size != NULL)
+ *kern_info_size = 0;
+
+ return NULL;
+}
+
+os_error *xfont_read_encoding_filename (font_f font, char *buffer, int size,
+ char **end)
+{
+ const char *filename = NULL;
+ size_t ei;
+
+ if (font == 0)
+ return &font_bad_font_number;
+ if (h->fonts[font].refcnt == 0)
+ return &font_no_font;
+ if (h->encoding_filenames == NULL)
+ return &font_encoding_not_found;
+ if (h->fonts[font].encoding != FONT_ENCODING_SYMBOL) {
+ ei = h->fonts[font].encoding;
+ } else {
+ ei = h->n_encodings;
+ }
+ filename = h->encoding_filenames[
+ (h->fonts[font].name * (h->n_encodings + 1)) + ei];
+ if (filename == NULL)
+ return &font_encoding_not_found;
+ if (buffer == NULL || (size_t) size < strlen(filename) + 1)
+ return &bad_parameters;
+
+ strcpy(buffer, filename);
+
+ if (end != NULL)
+ *end = buffer + strlen(filename) + 1;
+
+ return NULL;
+}
+
+os_error *xfont_list_fonts (byte *buffer1, font_list_context context,
+ int size1, byte *buffer2, int size2, char const *tick_font,
+ font_list_context *context_out, int *used1, int *used2)
+{
+ const char **values;
+ size_t n_values;
+ size_t index = (context & 0xffff);
+
+ if ((context & font_RETURN_FONT_MENU) &&
+ (context & ~(font_USE_LINEFEED |
+ font_RETURN_FONT_MENU |
+ font_ALLOW_SYSTEM_FONT |
+ font_GIVEN_TICK | 0x400000)) >> 16)
+ return &bad_parameters;
+ if (!(context & font_RETURN_FONT_MENU) &&
+ (context & ~(font_RETURN_FONT_NAME |
+ font_RETURN_LOCAL_FONT_NAME |
+ font_USE_LINEFEED | 0x400000)) >> 16)
+ return &bad_parameters;
+ if (context & font_RETURN_FONT_MENU)
+ return &unimplemented;
+
+ if (context & 0x400000) {
+ values = h->encodings;
+ n_values = h->n_encodings;
+ } else {
+ values = h->font_names;
+ n_values = h->n_font_names;
+ }
+
+ if (index < n_values) {
+ int len = (int) strlen(values[index]) + 1;
+ if (context & font_RETURN_FONT_NAME) {
+ if (buffer1 != NULL && size1 < len)
+ return &buff_overflow;
+ if (buffer1 != NULL)
+ strcpy((char *) buffer1, values[index]);
+ if (used1 != NULL)
+ *used1 = len;
+ }
+ if (context & font_RETURN_LOCAL_FONT_NAME) {
+ if (buffer2 != NULL && size2 < len)
+ return &buff_overflow;
+ if (buffer2 != NULL)
+ strcpy((char *) buffer2, values[index]);
+ if (used2 != NULL)
+ *used2 = len;
+ }
+ index++;
+ } else {
+ index = -1;
+ }
+
+ if (context_out != NULL)
+ *context_out = (font_list_context) index;
+
+ (void) tick_font;
+
+ return NULL;
+}
+
+os_error *xfont_set_font (font_f font)
+{
+ if (font == 0)
+ return &font_bad_font_number;
+ if (h->fonts[font].refcnt == 0)
+ return &font_no_font;
+
+ h->current_font = font;
+
+ return NULL;
+}
+
+os_error *xfont_paint (font_f font, char const *string,
+ font_string_flags flags, int xpos, int ypos,
+ font_paint_block const *block, os_trfm const *trfm, int length)
+{
+ if (!(flags & font_GIVEN_FONT) || font == 0)
+ font = h->current_font;
+ if (font == 0 || h->fonts[font].refcnt == 0)
+ return &font_no_font;
+
+ if (flags & font_GIVEN_FONT)
+ h->current_font = font;
+
+ //XXX:
+ //XXX: also, pay attention to redirection to buffer
+ (void) string;
+ (void) xpos;
+ (void) ypos;
+ (void) block;
+ (void) trfm;
+ (void) length;
+
+ return NULL;
+}
+
+os_error *xfont_scan_string (font_f font, char const *s,
+ font_string_flags flags, int x, int y, font_scan_block *block,
+ os_trfm const *trfm, int length, char **split_point,
+ int *x_out, int *y_out, int *num_split_chars)
+{
+ size_t advance = 1;
+ int width = 0;
+
+ if (!(flags & font_GIVEN_FONT) || font == 0)
+ font = h->current_font;
+ if (font == 0 || h->fonts[font].refcnt == 0)
+ return &font_no_font;
+
+ if ((flags & font_GIVEN_BLOCK) && block == NULL)
+ return &bad_parameters;
+ if ((flags & font_RETURN_BBOX) && !(flags & font_GIVEN_BLOCK))
+ return &bad_parameters;
+ if ((flags & font_GIVEN_BLOCK) && (block->space.x != 0 ||
+ block->space.y != 0 ||
+ block->letter.x != 0 ||
+ block->letter.y != 0 ||
+ block->split_char != -1))
+ return &unimplemented;
+
+ if ((flags & font_GIVEN32_BIT) && (flags & font_GIVEN16_BIT))
+ return &bad_parameters;
+
+ if (!(flags & font_GIVEN_LENGTH))
+ length = 0x7ffffffc;
+
+ if (flags & font_GIVEN32_BIT)
+ advance = 4;
+ else if (flags & font_GIVEN16_BIT)
+ advance = 2;
+
+ /* Consume up to length bytes of input */
+ while (length > 0) {
+ uint32_t c = 0, i;
+ int cwidth;
+ for (i = 0; i < advance; i++) {
+ c |= s[i] << (advance - i - 1);
+ }
+ s += advance;
+ length -= advance;
+
+ /* Regardless of length, stop on terminator */
+ if (c == 0 || c == 10 || c == 13)
+ break;
+
+ /* Just scale font size to millipoints and add on the width */
+ cwidth = ((h->fonts[font].xsize * 1000) >> 4);
+ if ((flags & font_RETURN_CARET_POS) && x > 0 &&
+ (width + cwidth/2) > x) {
+ /* Split point is less than half way through
+ * this character: exclude it */
+ s -= advance;
+ length += advance;
+ break;
+ }
+ width += cwidth;
+ //XXX: how is negative x meant to work?
+ if (x > 0 && width > x)
+ break;
+ }
+
+ if (flags & font_RETURN_BBOX) {
+ block->bbox.x0 = 0;
+ block->bbox.y0 = 0;
+ block->bbox.x1 = width;
+ block->bbox.y1 = (h->fonts[font].ysize * 1000) >> 4;
+ }
+
+ if (x_out != NULL)
+ *x_out = width;
+ if (y_out != NULL)
+ *y_out = (h->fonts[font].ysize * 1000) >> 4;
+ if (split_point != NULL)
+ *split_point = (char *) s;
+
+ (void) y;
+ (void) trfm;
+ (void) num_split_chars;
+
+ return NULL;
+}
+
+os_error *xfont_switch_output_to_buffer (font_output_flags flags,
+ byte *buffer, char **end)
+{
+ if ((intptr_t) buffer <= 0 && flags != 0)
+ return &font_reserved;
+ if (flags & ~(font_NO_OUTPUT | font_ADD_HINTS | font_ERROR_IF_BITMAP))
+ return &font_reserved;
+
+ if (end)
+ *end = h->buffer;
+
+ if ((intptr_t) buffer != -1) {
+ h->buffer = (char *) buffer;
+ h->buffer_flags = flags;
+ }
+
+ return NULL;
+}
+
+os_error *xfont_enumerate_characters (font_f font, int character,
+ int *next_character, int *internal_character_code)
+{
+ static int extchars[] = { 0x20, 0x21, 0x30, 0x31, 0x32, 0xa0, 0x10ac0, 0x20021, 0x30000, -1 };
+ static int intchars[] = { 1, 2, 3, 4, -1, 5, 6, 7, 8 };
+ size_t index = 0;
+ int next = -1, internal = -1;
+
+ if (!h->fm_ucs)
+ return &no_such_swi;
+
+ if (font == 0)
+ font = h->current_font;
+ if (font == 0)
+ return &font_bad_font_number;
+ if (h->fonts[font].refcnt == 0)
+ return &font_no_font;
+
+ /* Broken FEC: skip first chunk unless code is valid.
+ * (only 0x20 and 0x21 are valid in the first chunk here,
+ * so we simply need to skip over these if the code is
+ * less than 0x20 -- any other codes in the first chunk
+ * will just fall out of the usual "next code" logic)
+ */
+ if (h->fm_broken_fec && character < extchars[0])
+ index = 2;
+
+ for (; index < (sizeof(intchars)/sizeof(intchars[0])); index++) {
+ if (extchars[index] == character) {
+ /* Found: return it and compute next */
+ next = extchars[index+1];
+ internal = intchars[index];
+ break;
+ } else if (extchars[index] > character) {
+ /* Not found and won't be: compute next */
+ next = extchars[index];
+ internal = -1;
+ break;
+ }
+ }
+
+ if (next_character != NULL)
+ *next_character = next;
+ if (internal_character_code != NULL)
+ *internal_character_code = internal;
+
+ return NULL;
+}
+
+/****************************************************************************/
+
+os_error *xhourglass_on (void)
+{
+ return &unimplemented;
+}
+
+os_error *xhourglass_off (void)
+{
+ return &unimplemented;
+}
+
+os_error *xhourglass_percentage (int percent)
+{
+ (void) percent;
+
+ return &unimplemented;
+}
+
+os_error *xhourglass_leds (bits eor_mask, bits and_mask, bits *old_leds)
+{
+ (void) eor_mask;
+ (void) and_mask;
+ (void) old_leds;
+
+ return &unimplemented;
+}
+
+os_error *xhourglass_colours (os_colour sand, os_colour glass,
+ os_colour *old_sand, os_colour *old_glass)
+{
+ (void) sand;
+ (void) glass;
+ (void) old_sand;
+ (void) old_glass;
+
+ return &unimplemented;
+}
+
+/****************************************************************************/
+
+os_error *xos_read_monotonic_time (os_t *t)
+{
+ (void) t;
+
+ return &unimplemented;
+}
+
+os_error *xos_read_mode_variable (os_mode mode, os_mode_var var, int *var_val,
+ bits *psr)
+{
+ (void) mode;
+ (void) var;
+ (void) var_val;
+ (void) psr;
+
+ return &unimplemented;
+}
+
+/****************************************************************************/
+
+os_error *xosfscontrol_canonicalise_path (char const *path_name, char *buffer,
+ char const *var, char const *path, int size, int *spare)
+{
+ const char *prefix = "Resources:$.Fonts.";
+ size_t len = strlen(path_name) + strlen(prefix) + 1;
+
+ if (strcmp(var, "Font$Path") != 0 || path != NULL)
+ return &unimplemented;
+
+ if (buffer == NULL && size != 0)
+ return &bad_parameters;
+
+ if (buffer != NULL && size < (int) len)
+ return &buff_overflow;
+
+ if (buffer != NULL) {
+ strcpy(buffer, prefix);
+ strcpy(buffer + strlen(prefix), path_name);
+ }
+
+ if (spare != NULL)
+ *spare = size - len;
+
+ return NULL;
+}
+
+/****************************************************************************/
+
+os_error *xtaskwindowtaskinfo_window_task (osbool *window_task)
+{
+ (void) window_task;
+
+ return &unimplemented;
+}
+
+/****************************************************************************/
+
+os_error *xwimp_create_window (wimp_window const *window, wimp_w *w)
+{
+ (void) window;
+ (void) w;
+
+ return &unimplemented;
+}
+
+os_error *xwimp_delete_window (wimp_w w)
+{
+ (void) w;
+
+ return &unimplemented;
+}
+
+os_error *xwimp_get_window_state (wimp_window_state *state)
+{
+ (void) state;
+
+ return &unimplemented;
+}
+
+os_error *xwimp_open_window (wimp_open *open)
+{
+ (void) open;
+
+ return &unimplemented;
+}
+
+os_error *xwimp_set_icon_state (wimp_w w, wimp_i i, wimp_icon_flags eor_bits,
+ wimp_icon_flags clear_bits)
+{
+ (void) w;
+ (void) i;
+ (void) eor_bits;
+ (void) clear_bits;
+
+ return &unimplemented;
+}
+
+os_error *xwimp_resize_icon (wimp_w w, wimp_i i, int x0, int y0, int x1, int y1)
+{
+ (void) w;
+ (void) i;
+ (void) x0;
+ (void) y0;
+ (void) x1;
+ (void) y1;
+
+ return &unimplemented;
+}
+
+os_error *xwimp_poll (wimp_poll_flags mask, wimp_block *block, int *pollword,
+ wimp_event_no *event)
+{
+ (void) mask;
+ (void) block;
+ (void) pollword;
+ (void) event;
+
+ return &unimplemented;
+}
+
+/****************************************************************************/
+
+os_error *xwimpreadsysinfo_task (wimp_t *task, wimp_version_no *version)
+{
+ (void) task;
+ (void) version;
+
+ return &unimplemented;
+}
diff --git a/test/nofonts.c b/test/nofonts.c
new file mode 100644
index 0000000..557d764
--- /dev/null
+++ b/test/nofonts.c
@@ -0,0 +1,20 @@
+#include <stdio.h>
+
+#include "rufl.h"
+
+#include "harness.h"
+#include "testutils.h"
+
+int main(int argc, const char **argv)
+{
+ UNUSED(argc);
+ UNUSED(argv);
+
+ rufl_test_harness_init(380, true, false);
+
+ assert(rufl_FONT_MANAGER_ERROR == rufl_init());
+
+ printf("PASS\n");
+
+ return 0;
+}
diff --git a/test/oldfminit.c b/test/oldfminit.c
new file mode 100644
index 0000000..c554499
--- /dev/null
+++ b/test/oldfminit.c
@@ -0,0 +1,353 @@
+#include <ftw.h>
+#include <libgen.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "rufl.h"
+
+/* dirty! */
+#include "../src/rufl_internal.h"
+
+#include "harness.h"
+#include "testutils.h"
+
+struct expumap {
+ char *fontname;
+ size_t num_umaps;
+};
+
+struct cfg {
+ const char *datadir;
+
+ struct expumap *expumaps;
+ size_t n_expumaps;
+};
+
+static char template[] = "/tmp/oldfminitXXXXXX";
+static const char *ptmp = NULL;
+static struct cfg cfg;
+
+static int ftw_cb(const char *path, const struct stat *sb,
+ int typeflag, struct FTW *ftwbuf)
+{
+ (void) sb;
+ (void) typeflag;
+ (void) ftwbuf;
+
+ remove(path);
+
+ return 0;
+}
+
+static void cleanup(void)
+{
+ if (cfg.expumaps != NULL) {
+ size_t i;
+
+ for (i = 0; i < cfg.n_expumaps; i++) {
+ free(cfg.expumaps[i].fontname);
+ }
+ free(cfg.expumaps);
+ }
+
+ if (ptmp == NULL)
+ return;
+
+ nftw(ptmp, ftw_cb, FOPEN_MAX, FTW_DEPTH | FTW_MOUNT | FTW_PHYS);
+}
+
+static void parse_cfg(const char *path, struct cfg *cfg,
+ void (*cb)(struct cfg *cfg, const char *line, size_t len))
+{
+ FILE *fp;
+ char wbuf[4096];
+ size_t nleft = 0;
+
+ fp = fopen(path, "r");
+ assert(fp != NULL);
+
+ while (!feof(fp)) {
+ char buf[2048];
+ size_t nread;
+ const char *p, *s;
+
+ nread = fread(buf, 1, sizeof(buf), fp);
+ if (nread != sizeof(buf)) {
+ assert(ferror(fp) == 0);
+ }
+
+ memcpy(wbuf + nleft, buf, nread);
+ nleft += nread;
+
+ for (p = s = wbuf; p < wbuf + nleft; p++) {
+ if (*p == '\n') {
+ cb(cfg, s, p - s);
+ s = p+1;
+ }
+ }
+ if (s != wbuf + nleft) {
+ memmove(wbuf, s, p - s);
+ nleft = p - s;
+ } else {
+ nleft = 0;
+ }
+ assert(nleft < sizeof(buf));
+ }
+ assert(nleft == 0);
+
+ fclose(fp);
+}
+
+static void parse_expumaps(struct cfg *cfg, char *data, size_t len)
+{
+ char *p, *s;
+ const char *font = NULL, *count = NULL;
+ struct expumap *e;
+ size_t num_umaps;
+
+ for (p = s = data; p < data+len; p++) {
+ if (*p == ' ' || *p == '\t') {
+ *p = '\0';
+ if (s != p) {
+ if (font == NULL)
+ font = s;
+ else if (count == NULL)
+ count = s;
+ }
+ s = p+1;
+ }
+ }
+ if (count == NULL)
+ count = s;
+
+ num_umaps = strtoul(count, &p, 10);
+ assert((size_t)(p-count) == strlen(count));
+
+ e = realloc(cfg->expumaps, (cfg->n_expumaps + 1) * sizeof(*e));
+ assert(e != NULL);
+
+ cfg->expumaps = e;
+ cfg->expumaps[cfg->n_expumaps].fontname = strdup(font);
+ assert(cfg->expumaps[cfg->n_expumaps].fontname != NULL);
+ cfg->expumaps[cfg->n_expumaps].num_umaps = num_umaps;
+ cfg->n_expumaps++;
+}
+
+static void parse_directive(struct cfg *cfg, char *linecpy, size_t len)
+{
+ char *p, *s;
+ const char *directive = NULL;
+
+ for (p = s = linecpy; p < linecpy+len; p++) {
+ if (*p == ' ' || *p == '\t') {
+ *p = '\0';
+ if (s != p && directive == NULL) {
+ directive = s;
+ s = p+1;
+ break;
+ }
+ s = p+1;
+ }
+ }
+ if (directive == NULL)
+ directive = s;
+
+ if (strcmp("\%expumaps", directive) == 0) {
+ parse_expumaps(cfg, s, len - (s - linecpy));
+ }
+}
+
+static void parse_encoding(struct cfg *cfg, char *linecpy, size_t len)
+{
+ char *p, *s;
+ const char *font = NULL, *encoding = NULL, *file = NULL;
+ char *path;
+
+ for (p = s = linecpy; p < linecpy+len; p++) {
+ if (*p == ' ' || *p == '\t') {
+ *p = '\0';
+ if (s != p) {
+ if (font == NULL)
+ font = s;
+ else if (encoding == NULL)
+ encoding = s;
+ else if (file == NULL)
+ file = s;
+ }
+ s = p+1;
+ }
+ }
+ if (file == NULL)
+ file = s;
+
+ assert(font != NULL);
+ assert(encoding != NULL);
+ assert(file != NULL);
+
+ path = malloc(strlen(cfg->datadir) + strlen(file) + 2);
+ assert(path != NULL);
+ strcpy(path, cfg->datadir);
+ path[strlen(cfg->datadir)] = '/'; //XXX: platform-agnostic dirsep?
+ strcpy(path+strlen(cfg->datadir)+1, file);
+
+ rufl_test_harness_set_font_encoding(font, encoding, path);
+
+ free(path);
+}
+
+static void line_cb(struct cfg *cfg, const char *line, size_t len)
+{
+ char *linecpy;
+
+ if (len == 0 || line[0] == '#')
+ return;
+
+ linecpy = malloc(len + 1);
+ assert(linecpy != NULL);
+ memcpy(linecpy, line, len);
+ linecpy[len] = '\0';
+
+ if (line[0] == '%')
+ parse_directive(cfg, linecpy, len);
+ else
+ parse_encoding(cfg, linecpy, len);
+
+ free(linecpy);
+}
+
+static void read_config(const char *path, struct cfg *cfg)
+{
+ char *pathcpy;
+
+ pathcpy = strdup(path);
+ assert(pathcpy != NULL);
+
+ cfg->datadir = dirname(pathcpy);
+
+ parse_cfg(path, cfg, line_cb);
+
+ free(pathcpy);
+ cfg->datadir = NULL;
+}
+
+int main(int argc, const char **argv)
+{
+ int width, x;
+ size_t offset;
+ int32_t xkern, ykern, italic, ascent, descent, xheight, cap_height;
+ int32_t x_bearing, y_bearing, mwidth, mheight, x_advance, y_advance;
+ int8_t uline_position;
+ uint8_t uline_thickness;
+ os_box bbox;
+
+ assert(2 == argc);
+
+ ptmp = mkdtemp(template);
+ assert(NULL != ptmp);
+ atexit(cleanup);
+ chdir(ptmp);
+
+ rufl_test_harness_init(339, false, true);
+
+ read_config(argv[1], &cfg);
+
+ assert(rufl_OK == rufl_init());
+ assert(NULL == rufl_fm_error);
+ assert(3 == rufl_family_list_entries);
+ assert(NULL != rufl_family_menu);
+
+ if (cfg.expumaps != NULL) {
+ size_t i, j;
+ for (i = 0; i != cfg.n_expumaps; i++) {
+ for (j = 0; j != rufl_font_list_entries; j++) {
+ if (strcmp(cfg.expumaps[i].fontname, rufl_font_list[j].identifier) == 0) {
+ assert(cfg.expumaps[i].num_umaps == rufl_font_list[j].num_umaps);
+ }
+ }
+ }
+ }
+
+ assert(rufl_OK == rufl_font_metrics("Corpus", rufl_WEIGHT_500,
+ &bbox, &xkern, &ykern, &italic,
+ &ascent, &descent, &xheight, &cap_height,
+ &uline_position, &uline_thickness));
+ assert(0 == bbox.x0);
+ assert(2 == bbox.x1);
+ assert(0 == bbox.y0);
+ assert(2 == bbox.y1);
+ assert(0 == xkern);
+ assert(0 == ykern);
+ assert(0 == italic);
+ assert(0 == ascent);
+ assert(0 == descent);
+ assert((bbox.y1 - bbox.y0) == cap_height);
+ assert((cap_height / 2) == xheight);
+ assert(0 == uline_position);
+ assert(0 == uline_thickness);
+
+ assert(rufl_OK == rufl_width("Corpus", rufl_WEIGHT_500, 160,
+ "!\xc2\xa0", 3, &width));
+ assert(50 == width);
+
+ /* Place caret after first character */
+ assert(rufl_OK == rufl_x_to_offset("Homerton", rufl_WEIGHT_500, 160,
+ "!\xc2\xa0", 3, 25, &offset, &x));
+ assert(1 == offset);
+ assert(25 == x);
+
+ /* Attempt to split after first character. As the split point is
+ * coincident with the start of the second character, however,
+ * the split point is placed after it. */
+ assert(rufl_OK == rufl_split("Trinity", rufl_WEIGHT_500, 160,
+ "!\xc2\xa0", 3, 25, &offset, &x));
+ assert(3 == offset);
+ assert(50 == x);
+
+ /* Compute width of replacement character */
+ assert(rufl_OK == rufl_width("Corpus", rufl_WEIGHT_500, 160,
+ "\xef\xbf\xbd", 3, &width));
+ assert(17 == width);
+ assert(rufl_OK == rufl_width("Corpus", rufl_WEIGHT_500, 160,
+ "\xf0\xa0\x80\xa5", 4, &width));
+ assert(26 == width);
+
+ /* Measure font bounding box */
+ assert(rufl_OK == rufl_font_bbox("Corpus", rufl_WEIGHT_500, 160,
+ &bbox));
+ assert(0 == bbox.x0);
+ assert(25 == bbox.x1);
+ assert(0 == bbox.y0);
+ assert(25 == bbox.y1);
+
+ /* Trivial render */
+ assert(rufl_OK == rufl_paint("Trinity", rufl_WEIGHT_500, 160,
+ "!\xc2\xa0", 3, 0, 0, 0));
+
+ rufl_dump_state(true);
+
+ /* Obtain metrics for a glyph */
+ assert(rufl_OK == rufl_glyph_metrics("Homerton", rufl_WEIGHT_500, 160,
+ "!", 1, &x_bearing, &y_bearing,
+ &mwidth, &mheight, &x_advance, &y_advance));
+ assert(0 == x_bearing);
+ assert(10000 == y_bearing);
+ assert(10000 == mwidth);
+ assert(10000 == mheight);
+ assert(10000 == x_advance);
+ assert(10000 == y_advance);
+
+ rufl_quit();
+
+ /* Reinit -- should load cache */
+ assert(rufl_OK == rufl_init());
+ assert(NULL == rufl_fm_error);
+ assert(3 == rufl_family_list_entries);
+ assert(NULL != rufl_family_menu);
+ /* Done for real this time */
+ rufl_quit();
+
+ printf("PASS\n");
+
+ return 0;
+}
diff --git a/test/olducsinit.c b/test/olducsinit.c
new file mode 100644
index 0000000..ed3d846
--- /dev/null
+++ b/test/olducsinit.c
@@ -0,0 +1,140 @@
+#include <ftw.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include "rufl.h"
+
+#include "harness.h"
+#include "testutils.h"
+
+static char template[] = "/tmp/olducsinitXXXXXX";
+static const char *ptmp = NULL;
+
+static int ftw_cb(const char *path, const struct stat *sb,
+ int typeflag, struct FTW *ftwbuf)
+{
+ (void) sb;
+ (void) typeflag;
+ (void) ftwbuf;
+
+ remove(path);
+
+ return 0;
+}
+
+static void cleanup(void)
+{
+ if (ptmp == NULL)
+ return;
+
+ nftw(ptmp, ftw_cb, FOPEN_MAX, FTW_DEPTH | FTW_MOUNT | FTW_PHYS);
+}
+
+int main(int argc, const char **argv)
+{
+ int width, x;
+ size_t offset;
+ int32_t xkern, ykern, italic, ascent, descent, xheight, cap_height;
+ int32_t x_bearing, y_bearing, mwidth, mheight, x_advance, y_advance;
+ int8_t uline_position;
+ uint8_t uline_thickness;
+ os_box bbox;
+
+ UNUSED(argc);
+ UNUSED(argv);
+
+ ptmp = mkdtemp(template);
+ assert(NULL != ptmp);
+ atexit(cleanup);
+ chdir(ptmp);
+
+ rufl_test_harness_init(361, true, true);
+
+ assert(rufl_OK == rufl_init());
+ assert(NULL == rufl_fm_error);
+ assert(3 == rufl_family_list_entries);
+ assert(NULL != rufl_family_menu);
+
+ assert(rufl_OK == rufl_font_metrics("Corpus", rufl_WEIGHT_500,
+ &bbox, &xkern, &ykern, &italic,
+ &ascent, &descent, &xheight, &cap_height,
+ &uline_position, &uline_thickness));
+ assert(0 == bbox.x0);
+ assert(2 == bbox.x1);
+ assert(0 == bbox.y0);
+ assert(2 == bbox.y1);
+ assert(0 == xkern);
+ assert(0 == ykern);
+ assert(0 == italic);
+ assert(0 == ascent);
+ assert(0 == descent);
+ assert((bbox.y1 - bbox.y0) == cap_height);
+ assert((cap_height / 2) == xheight);
+ assert(0 == uline_position);
+ assert(0 == uline_thickness);
+
+ assert(rufl_OK == rufl_width("Corpus", rufl_WEIGHT_500, 160,
+ "!\xc2\xa0", 3, &width));
+ assert(50 == width);
+
+ /* Place caret after first character */
+ assert(rufl_OK == rufl_x_to_offset("Homerton", rufl_WEIGHT_500, 160,
+ "!\xc2\xa0", 3, 25, &offset, &x));
+ assert(1 == offset);
+ assert(25 == x);
+
+ /* Attempt to split after first character. As the split point is
+ * coincident with the start of the second character, however,
+ * the split point is placed after it. */
+ assert(rufl_OK == rufl_split("Trinity", rufl_WEIGHT_500, 160,
+ "!\xc2\xa0", 3, 25, &offset, &x));
+ assert(3 == offset);
+ assert(50 == x);
+
+ /* Compute width of replacement character */
+ assert(rufl_OK == rufl_width("Corpus", rufl_WEIGHT_500, 160,
+ "\xef\xbf\xbd", 3, &width));
+ assert(17 == width);
+ assert(rufl_OK == rufl_width("Corpus", rufl_WEIGHT_500, 160,
+ "\xf0\xa0\x80\xa5", 4, &width));
+ assert(26 == width);
+
+ /* Measure font bounding box */
+ assert(rufl_OK == rufl_font_bbox("Corpus", rufl_WEIGHT_500, 160,
+ &bbox));
+ assert(0 == bbox.x0);
+ assert(25 == bbox.x1);
+ assert(0 == bbox.y0);
+ assert(25 == bbox.y1);
+
+ /* Trivial render */
+ assert(rufl_OK == rufl_paint("Trinity", rufl_WEIGHT_500, 160,
+ "!\xc2\xa0", 3, 0, 0, 0));
+
+ /* Obtain metrics for a glyph */
+ assert(rufl_OK == rufl_glyph_metrics("Homerton", rufl_WEIGHT_500, 160,
+ "!", 1, &x_bearing, &y_bearing,
+ &mwidth, &mheight, &x_advance, &y_advance));
+ assert(0 == x_bearing);
+ assert(10000 == y_bearing);
+ assert(10000 == mwidth);
+ assert(10000 == mheight);
+ assert(10000 == x_advance);
+ assert(10000 == y_advance);
+
+ rufl_dump_state(true);
+
+ rufl_quit();
+
+ /* Reinit -- should load cache */
+ assert(rufl_OK == rufl_init());
+ assert(NULL == rufl_fm_error);
+ assert(3 == rufl_family_list_entries);
+ assert(NULL != rufl_family_menu);
+ /* Done for real this time */
+ rufl_quit();
+
+ printf("PASS\n");
+
+ return 0;
+}
diff --git a/test/rufl_chars.c b/test/rufl_chars.c
index 14a0fb6..64831ad 100644
--- a/test/rufl_chars.c
+++ b/test/rufl_chars.c
@@ -18,13 +18,13 @@
unsigned int font = 0;
unsigned int weight = rufl_WEIGHT_400;
bool italic = false;
+unsigned int plane = 0;
static rufl_code redraw(int x, int y, int y0, int y1);
static void try(rufl_code code, const char *context);
static void die(const char *error);
-
int main(void)
{
unsigned int i;
@@ -73,7 +73,7 @@ int main(void)
try(rufl_init(), "rufl_init");
- menu = malloc(wimp_SIZEOF_MENU(10 + rufl_family_list_entries));
+ menu = malloc(wimp_SIZEOF_MENU(27 + rufl_family_list_entries));
if (!menu)
die("Out of memory");
strcpy(menu->title_data.text, "Fonts");
@@ -99,23 +99,37 @@ int main(void)
(wimp_COLOUR_BLACK << wimp_ICON_FG_COLOUR_SHIFT) |
(wimp_COLOUR_WHITE << wimp_ICON_BG_COLOUR_SHIFT);
strcpy(menu->entries[9].data.text, "Italic");
- for (i = 0; i != rufl_family_list_entries; i++) {
- menu->entries[10 + i].menu_flags = 0;
+ for (i = 0; i != 17; i++) {
+ menu->entries[10 + i].menu_flags =
+ (i == 16 ? wimp_MENU_SEPARATE :0);
menu->entries[10 + i].sub_menu = wimp_NO_SUB_MENU;
menu->entries[10 + i].icon_flags = wimp_ICON_TEXT |
+ (wimp_COLOUR_BLACK << wimp_ICON_FG_COLOUR_SHIFT) |
+ (wimp_COLOUR_WHITE << wimp_ICON_BG_COLOUR_SHIFT);
+ strcpy(menu->entries[10 + i].data.text, "Plane 1");
+ menu->entries[10 + i].data.text[6] = '0' + (i+1)/10;
+ if (menu->entries[10 + i].data.text[6] == '0')
+ menu->entries[10 + i].data.text[6] = ' ';
+ menu->entries[10 + i].data.text[7] = '0' + (i+1)%10;
+ }
+ for (i = 0; i != rufl_family_list_entries; i++) {
+ menu->entries[27 + i].menu_flags = 0;
+ menu->entries[27 + i].sub_menu = wimp_NO_SUB_MENU;
+ menu->entries[27 + i].icon_flags = wimp_ICON_TEXT |
wimp_ICON_INDIRECTED |
(wimp_COLOUR_BLACK << wimp_ICON_FG_COLOUR_SHIFT) |
(wimp_COLOUR_WHITE << wimp_ICON_BG_COLOUR_SHIFT);
- menu->entries[10 + i].data.indirected_text.text =
+ menu->entries[27 + i].data.indirected_text.text =
(char *) rufl_family_list[i];
- menu->entries[10 + i].data.indirected_text.validation =
+ menu->entries[27 + i].data.indirected_text.validation =
(char *) -1;
- menu->entries[10 + i].data.indirected_text.size =
+ menu->entries[27 + i].data.indirected_text.size =
strlen(rufl_family_list[i]);
}
menu->entries[3].menu_flags |= wimp_MENU_TICKED;
menu->entries[10].menu_flags |= wimp_MENU_TICKED;
- menu->entries[i + 9].menu_flags |= wimp_MENU_LAST;
+ menu->entries[27].menu_flags |= wimp_MENU_TICKED;
+ menu->entries[i + 26].menu_flags |= wimp_MENU_LAST;
error = xwimp_create_window((wimp_window *) &window, &w);
if (error)
@@ -194,11 +208,17 @@ int main(void)
} else if (block.selection.items[0] == 9) {
italic = !italic;
menu->entries[9].menu_flags ^= wimp_MENU_TICKED;
+ } else if (block.selection.items[0] <= 26) {
+ menu->entries[10 + plane].menu_flags ^=
+ wimp_MENU_TICKED;
+ plane = block.selection.items[0] - 10;
+ menu->entries[10 + plane].menu_flags ^=
+ wimp_MENU_TICKED;
} else {
- menu->entries[10 + font].menu_flags ^=
+ menu->entries[27 + font].menu_flags ^=
wimp_MENU_TICKED;
- font = block.selection.items[0] - 10;
- menu->entries[10 + font].menu_flags ^=
+ font = block.selection.items[0] - 27;
+ menu->entries[27 + font].menu_flags ^=
wimp_MENU_TICKED;
}
error = xwimp_force_redraw(w,
@@ -249,15 +269,21 @@ rufl_code redraw(int x, int y, int y0, int y1)
rufl_style style = weight | (italic ? rufl_SLANTED : 0);
for (u = y0 / 40 * 32; (int) u != (y1 / 40 + 1) * 32; u++) {
- if (u <= 0x7f)
- s[0] = u, l = 1;
- else if (u <= 0x7ff)
- s[0] = 0xc0 | (u >> 6),
- s[1] = 0x80 | (u & 0x3f), l = 2;
- else if (u <= 0xffff)
- s[0] = 0xe0 | (u >> 12),
- s[1] = 0x80 | ((u >> 6) & 0x3f),
- s[2] = 0x80 | (u & 0x3f), l = 3;
+ unsigned int c = (plane << 16) | u;
+ if (c <= 0x7f)
+ s[0] = c, l = 1;
+ else if (c <= 0x7ff)
+ s[0] = 0xc0 | (c >> 6),
+ s[1] = 0x80 | (c & 0x3f), l = 2;
+ else if (c <= 0xffff)
+ s[0] = 0xe0 | (c >> 12),
+ s[1] = 0x80 | ((c >> 6) & 0x3f),
+ s[2] = 0x80 | (c & 0x3f), l = 3;
+ else if (c <= 0x10ffff)
+ s[0] = 0xf0 | (c >> 18),
+ s[1] = 0x80 | ((c >> 12) & 0x3f),
+ s[2] = 0x80 | ((c >> 6) & 0x3f),
+ s[3] = 0x80 | (c & 0x3f), l = 4;
else
break;
s[l] = 0;
@@ -276,7 +302,7 @@ rufl_code redraw(int x, int y, int y0, int y1)
void try(rufl_code code, const char *context)
{
- char s[200];
+ char s[400];
if (code == rufl_OK)
return;
else if (code == rufl_OUT_OF_MEMORY)
diff --git a/test/rufl_test.c b/test/rufl_test.c
index 51a29d6..bc644f5 100644
--- a/test/rufl_test.c
+++ b/test/rufl_test.c
@@ -18,23 +18,24 @@ static int cubic_to(os_coord *control1, os_coord *control2, os_coord *to,
void *user);
static void callback(void *context,
const char *font_name, unsigned int font_size,
- const char *s8, unsigned short *s16, unsigned int n,
+ const uint8_t *s8, const uint32_t *s32, unsigned int n,
int x, int y);
int main(void)
{
- char utf8_test[] = "Hello, world! ὕαλον "
- "Uherské Hradiště. 𐀀";
+ const char utf8_test[] = "Hello, world! ὕαλον "
+ "Uherské Hradiště. 𐀀"
+ "\xf0\xa0\x80\xa1";
int width;
size_t char_offset;
int x;
int actual_x;
struct rufl_decomp_funcs funcs = { move_to, line_to, cubic_to };
- int bbox[4];
+ os_box bbox;
try(rufl_init(), "rufl_init");
- rufl_dump_state();
+ rufl_dump_state(false);
try(rufl_paint("NewHall", rufl_WEIGHT_400, 240,
utf8_test, sizeof utf8_test - 1,
1200, 1000, 0), "rufl_paint");
@@ -62,9 +63,9 @@ int main(void)
try(rufl_paint_callback("NewHall", rufl_WEIGHT_400, 240,
utf8_test, sizeof utf8_test - 1,
1200, 1000, callback, 0), "rufl_paint_callback");
- try(rufl_font_bbox("NewHall", rufl_WEIGHT_400, 240, bbox),
+ try(rufl_font_bbox("NewHall", rufl_WEIGHT_400, 240, &bbox),
"rufl_font_bbox");
- printf("bbox: %i %i %i %i\n", bbox[0], bbox[1], bbox[2], bbox[3]);
+ printf("bbox: %i %i %i %i\n", bbox.x0, bbox.y0, bbox.x1, bbox.y1);
rufl_quit();
return 0;
@@ -131,7 +132,7 @@ int cubic_to(os_coord *control1, os_coord *control2, os_coord *to,
void callback(void *context,
const char *font_name, unsigned int font_size,
- const char *s8, unsigned short *s16, unsigned int n,
+ const uint8_t *s8, const uint32_t *s32, unsigned int n,
int x, int y)
{
(void) context;
@@ -140,9 +141,9 @@ void callback(void *context,
if (s8)
printf("s8 \"%.*s\" ", n, s8);
else {
- printf("s16 \"");
+ printf("s32 \"");
for (unsigned int i = 0; i != n; i++)
- printf("%x ", (unsigned int) s16[i]);
+ printf("%x ", (unsigned int) s32[i]);
printf("\" ");
}
printf("%i %i\n", x, y);
diff --git a/test/testutils.h b/test/testutils.h
new file mode 100644
index 0000000..7fe6333
--- /dev/null
+++ b/test/testutils.h
@@ -0,0 +1,123 @@
+#ifndef test_testutils_h_
+#define test_testutils_h_
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifndef UNUSED
+#define UNUSED(x) ((void) (x))
+#endif
+
+/* Redefine assert, so we can simply use the standard assert mechanism
+ * within testcases and exit with the right output for the testrunner
+ * to do the right thing. */
+void __assert2(const char *expr, const char *function,
+ const char *file, int line);
+
+void __assert2(const char *expr, const char *function,
+ const char *file, int line)
+{
+ UNUSED(function);
+ UNUSED(file);
+
+ printf("FAIL - %s at line %d\n", expr, line);
+
+ exit(EXIT_FAILURE);
+}
+
+#define assert(expr) \
+ ((void) ((expr) || (__assert2 (#expr, __func__, __FILE__, __LINE__), 0)))
+
+
+typedef bool (*line_func)(const char *data, size_t datalen, void *pw);
+
+static size_t parse_strlen(const char *str, size_t limit);
+bool parse_testfile(const char *filename, line_func callback, void *pw);
+size_t parse_filesize(const char *filename);
+
+/**
+ * Testcase datafile parser driver
+ *
+ * \param filename Name of file to parse
+ * \param callback Pointer to function to handle each line of input data
+ * \param pw Pointer to client-specific private data
+ * \return true on success, false otherwise.
+ */
+bool parse_testfile(const char *filename, line_func callback, void *pw)
+{
+ FILE *fp;
+ char buf[300];
+
+ fp = fopen(filename, "rb");
+ if (fp == NULL) {
+ printf("Failed opening %s\n", filename);
+ return false;
+ }
+
+ while (fgets(buf, sizeof buf, fp)) {
+ if (buf[0] == '\n')
+ continue;
+
+ if (!callback(buf, parse_strlen(buf, sizeof buf - 1), pw)) {
+ fclose(fp);
+ return false;
+ }
+ }
+
+ fclose(fp);
+
+ return true;
+}
+
+/**
+ * Utility string length measurer; assumes strings are '\n' terminated
+ *
+ * \param str String to measure length of
+ * \param limit Upper bound on string length
+ * \return String length
+ */
+size_t parse_strlen(const char *str, size_t limit)
+{
+ size_t len = 0;
+
+ if (str == NULL)
+ return 0;
+
+ while (len < limit - 1 && *str != '\n') {
+ len++;
+ str++;
+ }
+
+ len++;
+
+ return len;
+}
+
+/**
+ * Read the size of a file
+ *
+ * \param filename Name of file to read size of
+ * \return File size (in bytes), or 0 on error
+ */
+size_t parse_filesize(const char *filename)
+{
+ FILE *fp;
+ size_t len = 0;
+
+ fp = fopen(filename, "rb");
+ if (fp == NULL) {
+ printf("Failed opening %s\n", filename);
+ return 0;
+ }
+
+ fseek(fp, 0, SEEK_END);
+ len = ftell(fp);
+
+ fclose(fp);
+
+ return len;
+}
+
+
+#endif
diff --git a/test/ucsinit.c b/test/ucsinit.c
new file mode 100644
index 0000000..25aea60
--- /dev/null
+++ b/test/ucsinit.c
@@ -0,0 +1,140 @@
+#include <ftw.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include "rufl.h"
+
+#include "harness.h"
+#include "testutils.h"
+
+static char template[] = "/tmp/ucsinitXXXXXX";
+static const char *ptmp = NULL;
+
+static int ftw_cb(const char *path, const struct stat *sb,
+ int typeflag, struct FTW *ftwbuf)
+{
+ (void) sb;
+ (void) typeflag;
+ (void) ftwbuf;
+
+ remove(path);
+
+ return 0;
+}
+
+static void cleanup(void)
+{
+ if (ptmp == NULL)
+ return;
+
+ nftw(ptmp, ftw_cb, FOPEN_MAX, FTW_DEPTH | FTW_MOUNT | FTW_PHYS);
+}
+
+int main(int argc, const char **argv)
+{
+ int width, x;
+ size_t offset;
+ int32_t xkern, ykern, italic, ascent, descent, xheight, cap_height;
+ int32_t x_bearing, y_bearing, mwidth, mheight, x_advance, y_advance;
+ int8_t uline_position;
+ uint8_t uline_thickness;
+ os_box bbox;
+
+ UNUSED(argc);
+ UNUSED(argv);
+
+ ptmp = mkdtemp(template);
+ assert(NULL != ptmp);
+ atexit(cleanup);
+ chdir(ptmp);
+
+ rufl_test_harness_init(380, true, true);
+
+ assert(rufl_OK == rufl_init());
+ assert(NULL == rufl_fm_error);
+ assert(3 == rufl_family_list_entries);
+ assert(NULL != rufl_family_menu);
+
+ assert(rufl_OK == rufl_font_metrics("Corpus", rufl_WEIGHT_500,
+ &bbox, &xkern, &ykern, &italic,
+ &ascent, &descent, &xheight, &cap_height,
+ &uline_position, &uline_thickness));
+ assert(0 == bbox.x0);
+ assert(2 == bbox.x1);
+ assert(0 == bbox.y0);
+ assert(2 == bbox.y1);
+ assert(0 == xkern);
+ assert(0 == ykern);
+ assert(0 == italic);
+ assert(0 == ascent);
+ assert(0 == descent);
+ assert((bbox.y1 - bbox.y0) == cap_height);
+ assert((cap_height / 2) == xheight);
+ assert(0 == uline_position);
+ assert(0 == uline_thickness);
+
+ assert(rufl_OK == rufl_width("Corpus", rufl_WEIGHT_500, 160,
+ "!\xc2\xa0", 3, &width));
+ assert(50 == width);
+
+ /* Place caret after first character */
+ assert(rufl_OK == rufl_x_to_offset("Homerton", rufl_WEIGHT_500, 160,
+ "!\xc2\xa0", 3, 25, &offset, &x));
+ assert(1 == offset);
+ assert(25 == x);
+
+ /* Attempt to split after first character. As the split point is
+ * coincident with the start of the second character, however,
+ * the split point is placed after it. */
+ assert(rufl_OK == rufl_split("Trinity", rufl_WEIGHT_500, 160,
+ "!\xc2\xa0", 3, 25, &offset, &x));
+ assert(3 == offset);
+ assert(50 == x);
+
+ /* Compute width of replacement character */
+ assert(rufl_OK == rufl_width("Corpus", rufl_WEIGHT_500, 160,
+ "\xef\xbf\xbd", 3, &width));
+ assert(17 == width);
+ assert(rufl_OK == rufl_width("Corpus", rufl_WEIGHT_500, 160,
+ "\xf0\xa0\x80\xa5", 4, &width));
+ assert(26 == width);
+
+ /* Measure font bounding box */
+ assert(rufl_OK == rufl_font_bbox("Corpus", rufl_WEIGHT_500, 160,
+ &bbox));
+ assert(0 == bbox.x0);
+ assert(25 == bbox.x1);
+ assert(0 == bbox.y0);
+ assert(25 == bbox.y1);
+
+ /* Trivial render */
+ assert(rufl_OK == rufl_paint("Trinity", rufl_WEIGHT_500, 160,
+ "!\xc2\xa0", 3, 0, 0, 0));
+
+ /* Obtain metrics for a glyph */
+ assert(rufl_OK == rufl_glyph_metrics("Homerton", rufl_WEIGHT_500, 160,
+ "!", 1, &x_bearing, &y_bearing,
+ &mwidth, &mheight, &x_advance, &y_advance));
+ assert(0 == x_bearing);
+ assert(10000 == y_bearing);
+ assert(10000 == mwidth);
+ assert(10000 == mheight);
+ assert(10000 == x_advance);
+ assert(10000 == y_advance);
+
+ rufl_dump_state(true);
+
+ rufl_quit();
+
+ /* Reinit -- should load cache */
+ assert(rufl_OK == rufl_init());
+ assert(NULL == rufl_fm_error);
+ assert(3 == rufl_family_list_entries);
+ assert(NULL != rufl_family_menu);
+ /* Done for real this time */
+ rufl_quit();
+
+ printf("PASS\n");
+
+ return 0;
+}