librufl: branch master updated. release/0.0.5-64-g769b9ee
by NetSurf Browser Project
Gitweb links:
...log http://git.netsurf-browser.org/librufl.git/shortlog/769b9ee5c1a1a70a0a75a...
...commit http://git.netsurf-browser.org/librufl.git/commit/769b9ee5c1a1a70a0a75a78...
...tree http://git.netsurf-browser.org/librufl.git/tree/769b9ee5c1a1a70a0a75a7826...
The branch, master has been updated
via 769b9ee5c1a1a70a0a75a78269ccabb2b7591832 (commit)
via 7f5504490fb8fb7ba3916bef7882bd8860a5c0a5 (commit)
via 7324abb6b05b6f6703667b39f736d6d958cb9460 (commit)
via fe56281fd5dc6e1ec85812cdb55de2df223b8cd1 (commit)
via 6e5b46e3f363bd70923419a9a0f1764a09fdd98f (commit)
via 1fd4db565d2b8b981ed00e3ee127ea4f04207b92 (commit)
via 107427fc7f55778f7434334cb84bc34f84a00115 (commit)
via 12748b1ee8019b24761d36fa7da555c500e83c33 (commit)
via 193969427f9b515fef02db45b9382cb576afdbe5 (commit)
via 479cc6662a3a26a237a993290216f5257d031318 (commit)
via e25cd523d0336286dcb43b17de3864399a55fd42 (commit)
via 88b143b1c47cc32b25a9b97389553963e4c8a66e (commit)
via 7b1ee98c53be125756b16d35cbd2b8c9f4ffbe15 (commit)
via e58ebde8498df388975cf050382e4e9aadf2f050 (commit)
via 7f8d135f7c3e2ba286cea01b4458c6e817f67794 (commit)
via 826d34fdc8660e76759423e3fa108c16bd2b3c96 (commit)
via 0a9599a0f146023336ab3cb77885834eb1f63070 (commit)
via 8fab85a9c1e1b820d4e32b6e318dcf33e3a08884 (commit)
via 20e35970679f5aab5a4dd8e60747af24ddac998f (commit)
via 8cd266be4e1e610573fd81a875a7479cebb2edfe (commit)
via b07f310d495a680de336122d7be6bb9059981a3d (commit)
via 49e4f00d4bc9c765342dc91ecfa93db1629919a7 (commit)
via cea91f55027126013633c7ff932e6eef8ca181a5 (commit)
via 37885aaa2300acb12f57e1dfa7824de32ccd6a17 (commit)
via 02a17503dbe2ff2f90d2f2f5e60025c2f6ec716e (commit)
via 081ae3ed868feb0a6501aa48946d19bc86af430b (commit)
via 445d1a2e9113899ad5ed993626d0960ecc12006a (commit)
via 1e9de16e39c38a86d1e61d63482c88ad96fae8d9 (commit)
via a1735a1171f484e9828029576c99cff12448060f (commit)
via 06fae32a7ae53ad113376befbc78414374f9326b (commit)
via deb692c00afecca0d73394feef6e99e51606b9b4 (commit)
via ce3664d296b9b2459c4251321cb3f90adc32972d (commit)
via 0358488cc592f217158a8c4ea9adacf652ee85ce (commit)
via d4468833b85b8c8f340bc9c3bb6572a245fd5982 (commit)
via 569a7dd761255ec0a04f9bc4e020440638395bb8 (commit)
via a4c41198bdcd21336f0921291c0e9a7b7da36fc3 (commit)
via 421bacf56744d00db7ccef93daa119ab8ea4ac55 (commit)
via d59f17a6fb3d4f20a931ab45bf60ff910685b241 (commit)
via 01da1538227f4d139c0665b730211c665c821625 (commit)
via b547911ad6cba76439eeb9cd03a6af3fe0dc6be3 (commit)
via 5fd0f4c9161c5ab6163386598d04681f12858509 (commit)
via 7455528fedc1d6a03ba693307c441ffddcc3c164 (commit)
via c69d7fee4ef1820296cb1c0db072e01cf6970ce1 (commit)
via 158483bc75ac79cb2699d92e30f30aeefefe56ca (commit)
via 5741043b16e81f9673fdef791aeb225e522c9a41 (commit)
via a53a1e176218aba04ec13d91bbfbd103608c4eb2 (commit)
via c945837b458fc9ff4d37568be74ddb1a685e6431 (commit)
via 6dce21aa10a2d5f5a745a2f168d801d4baa7d4b2 (commit)
via a1389acbc6234340c14f9236bfc9b6fd1776e8d8 (commit)
via a520ed23f23e77b8c91b31814d9bbc2246df18a5 (commit)
via bb8eab8b112ef6eae8af7d0869f0e1d2b416f61c (commit)
via 3c116337d2cad81fa27df86b1d9f995d8906fcd8 (commit)
via 5be9bd8e833c84dbd55511046657e2fcf678d03c (commit)
via 6f212ee62e6cc19cb686208dd5b510d01217d0af (commit)
via a0bad0bd03988b9b77969f8b00cc9a512856f7f0 (commit)
via fbec3fe20c30e4597110967f05f8416ba7769db4 (commit)
via c4db3cb6963278e377083e556b4a74341c446a27 (commit)
via 247a8f1c34b8ffcf3d6072dbb621ea2a19052874 (commit)
via 9bd774ec26a7a97019210b6779105f58cb29788b (commit)
via 282b342fe51a2e9856b5448c30679ea8b49f18c5 (commit)
via b7d315249f56dffa626354a4feaea5135f930e8a (commit)
via d8a8a3cc929986ed793b24f7cac1727f1f356bc9 (commit)
via 7b1430ad00add849dcbaa97472b5f71a9b9d5699 (commit)
from 6edf2b6406c830fcccf5061c2953eeba95a40741 (commit)
Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.
- Log -----------------------------------------------------------------
-----------------------------------------------------------------------
Summary of changes:
Makefile | 17 +-
include/rufl.h | 24 +-
src/Makefile | 3 +-
src/rufl_character_set_test.c | 26 +-
src/rufl_dump_state.c | 65 +-
src/rufl_init.c | 1084 +++++++++++++++++----------
src/rufl_internal.h | 139 +++-
src/rufl_metrics.c | 34 +-
src/rufl_paint.c | 151 ++--
src/rufl_quit.c | 22 +-
src/rufl_substitution_table.c | 1143 +++++++++++++++++++++++++++++
test/INDEX | 7 +
test/Makefile | 13 +-
test/data/{Encoding => oldfminit/Allerta} | 0
test/data/oldfminit/INDEX | 10 +
test/data/oldfminit/Latin1 | 270 +++++++
test/data/oldfminit/brokenencoding.cfg | 28 +
test/data/oldfminit/latin1.cfg | 28 +
test/data/oldfminit/mergeumap.cfg | 40 +
test/data/oldfminit/nomapping.cfg | 19 +
test/data/oldfminit/symbol.cfg | 28 +
test/harness-priv.h | 45 ++
test/harness.c | 139 ++++
test/harness.h | 10 +
test/mocks.c | 671 +++++++++++++++++
test/nofonts.c | 20 +
test/oldfminit.c | 353 +++++++++
test/olducsinit.c | 140 ++++
test/rufl_chars.c | 68 +-
test/rufl_test.c | 21 +-
test/testutils.h | 123 ++++
test/ucsinit.c | 140 ++++
32 files changed, 4274 insertions(+), 607 deletions(-)
create mode 100644 src/rufl_substitution_table.c
create mode 100644 test/INDEX
rename test/data/{Encoding => oldfminit/Allerta} (100%)
create mode 100644 test/data/oldfminit/INDEX
create mode 100644 test/data/oldfminit/Latin1
create mode 100644 test/data/oldfminit/brokenencoding.cfg
create mode 100644 test/data/oldfminit/latin1.cfg
create mode 100644 test/data/oldfminit/mergeumap.cfg
create mode 100644 test/data/oldfminit/nomapping.cfg
create mode 100644 test/data/oldfminit/symbol.cfg
create mode 100644 test/harness-priv.h
create mode 100644 test/harness.c
create mode 100644 test/harness.h
create mode 100644 test/mocks.c
create mode 100644 test/nofonts.c
create mode 100644 test/oldfminit.c
create mode 100644 test/olducsinit.c
create mode 100644 test/testutils.h
create mode 100644 test/ucsinit.c
diff --git a/Makefile b/Makefile
index 1e44ea3..7bc8c9d 100644
--- a/Makefile
+++ b/Makefile
@@ -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 ($(HOST),arm-unknown-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 ($(HOST),arm-unknown-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_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..6c06a8f 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,18 +25,24 @@
#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];
@@ -58,11 +66,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 +83,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 +113,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 +186,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 +209,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 +222,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 +273,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 +362,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 +448,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 +613,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 +749,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 +817,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 +882,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 +905,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 +922,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 +949,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 +972,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 +987,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 +1001,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 +1035,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 +1130,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 +1319,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 +1359,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 +1381,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 +1416,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 +1473,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 +1493,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 +1514,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 +1609,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 +1683,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 +1860,10 @@ rufl_code rufl_load_cache(void)
free(charset);
}
- free(identifier);
+ charset = NULL;
+ size = 0;
+
+ free(identifier);
}
fclose(fp);
@@ -1598,10 +1890,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 +1914,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 0868571..8438bd8 100644
--- a/src/rufl_internal.h
+++ b/src/rufl_internal.h
@@ -5,6 +5,7 @@
* Copyright 2006 James Bursa <james(a)semichrome.net>
*/
+#include <inttypes.h>
#include <limits.h>
#include <oslib/font.h>
#include "rufl.h"
@@ -13,21 +14,79 @@
#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[];
diff --git a/src/rufl_metrics.c b/src/rufl_metrics.c
index 468f671..bbfd6bb 100644
--- a/src/rufl_metrics.c
+++ b/src/rufl_metrics.c
@@ -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 b49a158..9dc0e3f 100644
--- a/src/rufl_paint.c
+++ b/src/rufl_paint.c
@@ -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 cacc9c5..039229c 100644
--- a/src/rufl_quit.c
+++ b/src/rufl_quit.c
@@ -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..a599930
--- /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(a)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. */
+ subst_table->table = realloc(table, range * sizeof(*table));
+ if (!subst_table->table) {
+ free(subst_table);
+ return rufl_OUT_OF_MEMORY;
+ }
+ t64 = (uint64_t *) subst_table->table;
+ /* 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);
+
+ /* 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. */
+ table = realloc(subst_table->table,
+ range * sizeof(*subst_table->table));
+ if (table)
+ subst_table->table = (uint32_t *) table;
+
+ *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);
+ 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..a1b324c
--- /dev/null
+++ b/test/INDEX
@@ -0,0 +1,7 @@
+# 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
diff --git a/test/Makefile b/test/Makefile
index 2bfa58b..ec15598 100644
--- a/test/Makefile
+++ b/test/Makefile
@@ -1,4 +1,15 @@
# Tests
-DIR_TEST_ITEMS := rufl_test:rufl_test.c rufl_chars:rufl_chars.c
+ifeq ($(HOST),arm-unknown-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
+endif
include $(NSBUILD)/Makefile.subdir
diff --git a/test/data/Encoding b/test/data/oldfminit/Allerta
similarity index 100%
rename from test/data/Encoding
rename to 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/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;
+}
--
RISC OS Unicode Font Library