summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJames Bursa <james@netsurf-browser.org>2005-01-08 21:02:32 +0000
committerJames Bursa <james@netsurf-browser.org>2005-01-08 21:02:32 +0000
commitc4580a33f78825e385626610fbbff9a675b248a4 (patch)
treed4ab197d183e4a917202415cb7bd44b75d5c7866
downloadlibrufl-c4580a33f78825e385626610fbbff9a675b248a4.tar.gz
librufl-c4580a33f78825e385626610fbbff9a675b248a4.tar.bz2
[project @ 2005-01-08 21:02:32 by bursa]
Initial version of RISC OS Unicode font library. Require Unicode (RO5) Font Manager. svn path=/import/rufl/; revision=2443
-rwxr-xr-xmake3
-rw-r--r--rufl.h77
-rw-r--r--rufl_character_set_test.c36
-rw-r--r--rufl_chars.c297
-rw-r--r--rufl_dump_state.c98
-rw-r--r--rufl_init.c540
-rw-r--r--rufl_internal.h134
-rw-r--r--rufl_paint.c255
-rw-r--r--rufl_quit.c43
-rw-r--r--rufl_substitution_lookup.c32
-rw-r--r--rufl_test.c54
11 files changed, 1569 insertions, 0 deletions
diff --git a/make b/make
new file mode 100755
index 0000000..2e3942b
--- /dev/null
+++ b/make
@@ -0,0 +1,3 @@
+/home/riscos/cross/bin/gcc -std=c99 -W -Wall -Wundef -Wpointer-arith -Wcast-qual -Wcast-align -Wwrite-strings -Wstrict-prototypes -Wmissing-prototypes -Wmissing-declarations -Wredundant-decls -Wnested-externs -Winline -Wno-unused-parameter -mpoke-function-name -I/home/riscos/env/include -L/home/riscos/env/lib -loslib -o rufl_test,ff8 rufl_test.c rufl_init.c rufl_quit.c rufl_dump_state.c rufl_character_set_test.c rufl_substitution_lookup.c rufl_paint.c
+
+/home/riscos/cross/bin/gcc -std=c99 -O3 -W -Wall -Wundef -Wpointer-arith -Wcast-qual -Wcast-align -Wwrite-strings -Wstrict-prototypes -Wmissing-prototypes -Wmissing-declarations -Wredundant-decls -Wnested-externs -Winline -Wno-unused-parameter -mpoke-function-name -I/home/riscos/env/include -L/home/riscos/env/lib -loslib -o rufl_chars,ff8 rufl_chars.c rufl_init.c rufl_quit.c rufl_dump_state.c rufl_character_set_test.c rufl_substitution_lookup.c rufl_paint.c
diff --git a/rufl.h b/rufl.h
new file mode 100644
index 0000000..1691da4
--- /dev/null
+++ b/rufl.h
@@ -0,0 +1,77 @@
+/*
+ * This file is part of RUfl
+ * Licensed under the MIT License,
+ * http://www.opensource.org/licenses/mit-license
+ * Copyright 2005 James Bursa <james@semichrome.net>
+ */
+
+#include <stdbool.h>
+#include <stdlib.h>
+#include "oslib/os.h"
+
+
+/** Return code for RUfl functions. */
+typedef enum {
+ /** Success. */
+ rufl_OK,
+ /** Failure: memory was exhausted. */
+ rufl_OUT_OF_MEMORY,
+ /** Failure: Font Manager error; details in rufl_fm_error. */
+ rufl_FONT_MANAGER_ERROR,
+ /** Failure: no font with this name exists. */
+ rufl_FONT_NOT_FOUND,
+ /** Failure: file input / output error: details in errno. */
+ rufl_IO_ERROR,
+ /** Failure: file input unexpected eof. */
+ rufl_IO_EOF,
+} rufl_code;
+
+
+typedef enum {
+ rufl_REGULAR,
+ rufl_SLANTED,
+ rufl_BOLD,
+ rufl_BOLD_SLANTED,
+} rufl_style;
+
+
+/** Last Font Manager error. */
+extern os_error *rufl_fm_error;
+
+/** List of available font families. */
+extern char **rufl_family_list;
+/** Number of entries in rufl_family_list. */
+extern unsigned int rufl_family_list_entries;
+
+
+/**
+ * Initialise RUfl.
+ *
+ * All available fonts are scanned. May take some time.
+ */
+
+rufl_code rufl_init(void);
+
+
+/**
+ * Render Unicode text.
+ */
+
+rufl_code rufl_paint(const char *font_family, rufl_style font_style,
+ unsigned int font_size,
+ const char *string, size_t length,
+ int x, int y);
+
+
+/**
+ * Dump the internal library state to stdout.
+ */
+
+void rufl_dump_state(void);
+
+
+/**
+ * Free all resources used by the library.
+ */
+
+void rufl_quit(void);
diff --git a/rufl_character_set_test.c b/rufl_character_set_test.c
new file mode 100644
index 0000000..7134772
--- /dev/null
+++ b/rufl_character_set_test.c
@@ -0,0 +1,36 @@
+/*
+ * This file is part of RUfl
+ * Licensed under the MIT License,
+ * http://www.opensource.org/licenses/mit-license
+ * Copyright 2005 James Bursa <james@semichrome.net>
+ */
+
+#include "rufl_internal.h"
+
+
+/**
+ * Test if a character set contains a character.
+ *
+ * \param c character code
+ * \return true if present, false if absent
+ */
+
+bool rufl_character_set_test(struct rufl_character_set *charset,
+ unsigned int c)
+{
+ unsigned int block = c >> 8;
+ unsigned int byte = (c >> 3) & 31;
+ unsigned int bit = c & 7;
+
+ if (256 < block)
+ return false;
+
+ if (charset->index[block] == BLOCK_EMPTY)
+ return false;
+ else if (charset->index[block] == BLOCK_FULL)
+ return true;
+ else {
+ unsigned char z = charset->block[charset->index[block]][byte];
+ return z & (1 << bit);
+ }
+}
diff --git a/rufl_chars.c b/rufl_chars.c
new file mode 100644
index 0000000..a3af85b
--- /dev/null
+++ b/rufl_chars.c
@@ -0,0 +1,297 @@
+/*
+ * This file is part of RUfl
+ * Licensed under the MIT License,
+ * http://www.opensource.org/licenses/mit-license
+ * Copyright 2005 James Bursa <james@semichrome.net>
+ */
+
+#include <errno.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+#include "oslib/colourtrans.h"
+#include "oslib/wimp.h"
+#include "rufl.h"
+
+
+unsigned int font = 0;
+bool bold = false;
+bool italic = false;
+
+
+static void 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;
+ bool quit = false;
+ wimp_MESSAGE_LIST(2) messages = { { message_MODE_CHANGE,
+ message_QUIT } };
+ wimp_t task;
+ wimp_menu *menu;
+ wimp_WINDOW(0) window = {
+ { 400, 400, 1700, 1200 },
+ 0, 0,
+ wimp_TOP,
+ wimp_WINDOW_MOVEABLE | wimp_WINDOW_BACK_ICON |
+ wimp_WINDOW_CLOSE_ICON | wimp_WINDOW_TITLE_ICON |
+ wimp_WINDOW_TOGGLE_ICON | wimp_WINDOW_VSCROLL |
+ wimp_WINDOW_SIZE_ICON | wimp_WINDOW_NEW_FORMAT,
+ wimp_COLOUR_BLACK, wimp_COLOUR_LIGHT_GREY,
+ wimp_COLOUR_BLACK, wimp_COLOUR_WHITE,
+ wimp_COLOUR_DARK_GREY, wimp_COLOUR_MID_LIGHT_GREY,
+ wimp_COLOUR_CREAM,
+ 0,
+ { 0, -81928, 1300, 0 },
+ wimp_ICON_TEXT | wimp_ICON_HCENTRED,
+ 0,
+ 0,
+ 2, 1,
+ { "RUfl Chars" },
+ 0 };
+ wimp_w w;
+ wimp_window_state state;
+ wimp_block block;
+ wimp_event_no event;
+ wimp_pointer pointer;
+ osbool more;
+ os_error *error;
+
+ error = xwimp_initialise(wimp_VERSION_RO3, "RUfl Chars",
+ (wimp_message_list *) &messages,
+ 0, &task);
+ if (error) {
+ printf("error: xwimp_initialise: 0x%x: %s\n",
+ error->errnum, error->errmess);
+ exit(1);
+ }
+
+ try(rufl_init(), "rufl_init");
+
+ menu = malloc(wimp_SIZEOF_MENU(2 + rufl_family_list_entries));
+ if (!menu)
+ die("Out of memory");
+ strcpy(menu->title_data.text, "Fonts");
+ menu->title_fg = wimp_COLOUR_BLACK;
+ menu->title_bg = wimp_COLOUR_LIGHT_GREY;
+ menu->work_fg = wimp_COLOUR_RED;
+ menu->work_bg = wimp_COLOUR_WHITE;
+ menu->width = 200;
+ menu->height = wimp_MENU_ITEM_HEIGHT;
+ menu->gap = wimp_MENU_ITEM_GAP;
+ menu->entries[0].menu_flags = 0;
+ menu->entries[0].sub_menu = wimp_NO_SUB_MENU;
+ menu->entries[0].icon_flags = wimp_ICON_TEXT |
+ (wimp_COLOUR_BLACK << wimp_ICON_FG_COLOUR_SHIFT) |
+ (wimp_COLOUR_WHITE << wimp_ICON_BG_COLOUR_SHIFT);
+ strcpy(menu->entries[0].data.text, "Bold");
+ menu->entries[1].menu_flags = wimp_MENU_SEPARATE;
+ menu->entries[1].sub_menu = wimp_NO_SUB_MENU;
+ menu->entries[1].icon_flags = wimp_ICON_TEXT |
+ (wimp_COLOUR_BLACK << wimp_ICON_FG_COLOUR_SHIFT) |
+ (wimp_COLOUR_WHITE << wimp_ICON_BG_COLOUR_SHIFT);
+ strcpy(menu->entries[1].data.text, "Italic");
+ for (i = 0; i != rufl_family_list_entries; i++) {
+ menu->entries[2 + i].menu_flags = 0;
+ menu->entries[2 + i].sub_menu = wimp_NO_SUB_MENU;
+ menu->entries[2 + 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[2 + i].data.indirected_text.text =
+ rufl_family_list[i];
+ menu->entries[2 + i].data.indirected_text.validation =
+ (char *) -1;
+ menu->entries[2 + i].data.indirected_text.size =
+ strlen(rufl_family_list[i]);
+ }
+ menu->entries[2].menu_flags |= wimp_MENU_TICKED;
+ menu->entries[i + 1].menu_flags |= wimp_MENU_LAST;
+
+ error = xwimp_create_window((wimp_window *) &window, &w);
+ if (error)
+ die(error->errmess);
+
+ state.w = w;
+ error = xwimp_get_window_state(&state);
+ if (error)
+ die(error->errmess);
+
+ error = xwimp_open_window((wimp_open *) &state);
+ if (error)
+ die(error->errmess);
+
+ while (!quit) {
+ error = xwimp_poll(wimp_MASK_NULL, &block, 0, &event);
+ if (error)
+ die(error->errmess);
+
+ switch (event) {
+ case wimp_REDRAW_WINDOW_REQUEST:
+ error = xwimp_redraw_window(&block.redraw, &more);
+ if (error)
+ die(error->errmess);
+ xcolourtrans_set_font_colours(0, os_COLOUR_WHITE,
+ os_COLOUR_BLACK, 14, 0, 0, 0);
+ while (more) {
+ redraw(block.redraw.box.x0 -
+ block.redraw.xscroll,
+ block.redraw.box.y1 -
+ block.redraw.yscroll,
+ block.redraw.box.y1 -
+ block.redraw.yscroll -
+ block.redraw.clip.y1,
+ block.redraw.box.y1 -
+ block.redraw.yscroll -
+ block.redraw.clip.y0);
+ error = xwimp_get_rectangle(&block.redraw,
+ &more);
+ if (error)
+ die(error->errmess);
+ }
+ break;
+
+ case wimp_OPEN_WINDOW_REQUEST:
+ error = xwimp_open_window(&block.open);
+ if (error)
+ die(error->errmess);
+ break;
+
+ case wimp_CLOSE_WINDOW_REQUEST:
+ quit = true;
+ break;
+
+ case wimp_MOUSE_CLICK:
+ if (block.pointer.buttons == wimp_CLICK_MENU) {
+ error = xwimp_create_menu(menu,
+ block.pointer.pos.x - 64,
+ block.pointer.pos.y);
+ if (error)
+ die(error->errmess);
+ }
+ break;
+
+ case wimp_MENU_SELECTION:
+ error = xwimp_get_pointer_info(&pointer);
+ if (error)
+ die(error->errmess);
+ if (block.selection.items[0] == 0) {
+ bold = !bold;
+ menu->entries[0].menu_flags ^= wimp_MENU_TICKED;
+ } else if (block.selection.items[0] == 1) {
+ italic = !italic;
+ menu->entries[1].menu_flags ^= wimp_MENU_TICKED;
+ } else {
+ menu->entries[2 + font].menu_flags ^=
+ wimp_MENU_TICKED;
+ font = block.selection.items[0] - 2;
+ menu->entries[2 + font].menu_flags ^=
+ wimp_MENU_TICKED;
+ }
+ error = xwimp_force_redraw(w,
+ window.extent.x0, window.extent.y0,
+ window.extent.x1, window.extent.y1);
+ if (error)
+ die(error->errmess);
+ if (pointer.buttons == wimp_CLICK_ADJUST) {
+ error = xwimp_create_menu(menu,
+ pointer.pos.x - 64,
+ pointer.pos.y);
+ if (error)
+ die(error->errmess);
+ }
+ break;
+
+ case wimp_USER_MESSAGE:
+ case wimp_USER_MESSAGE_RECORDED:
+ if (block.message.action == message_QUIT)
+ quit = true;
+ break;
+ }
+ }
+
+/* try(rufl_paint("NewHall.Medium", 240, utf8_test, sizeof utf8_test - 1, */
+/* 1200, 1200), "rufl_paint"); */
+
+ xwimp_close_down(task);
+
+ rufl_quit();
+
+ return 0;
+}
+
+
+void redraw(int x, int y, int y0, int y1)
+{
+ char s[10];
+ unsigned int l;
+ unsigned int u;
+ rufl_style style = bold && italic ? rufl_BOLD_SLANTED :
+ bold ? rufl_BOLD :
+ italic ? rufl_SLANTED :
+ rufl_REGULAR;
+
+ 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;
+ else
+ break;
+ s[l] = 0;
+
+ rufl_paint(rufl_family_list[font], style, 240, s, l,
+ x + 10 + 40 * (u % 32),
+ y - 40 - 40 * (u / 32));
+ }
+}
+
+
+void try(rufl_code code, const char *context)
+{
+ char s[200];
+ if (code == rufl_OK)
+ return;
+ else if (code == rufl_OUT_OF_MEMORY)
+ snprintf(s, sizeof s, "error: %s: out of memory\n", context);
+ else if (code == rufl_FONT_MANAGER_ERROR)
+ snprintf(s, sizeof s, "error: %s: Font Manager error %x %s\n",
+ context, rufl_fm_error->errnum,
+ rufl_fm_error->errmess);
+ else if (code == rufl_FONT_NOT_FOUND)
+ snprintf(s, sizeof s, "error: %s: font not found\n", context);
+ else if (code == rufl_IO_ERROR)
+ snprintf(s, sizeof s, "error: %s: io error: %i %s\n", context,
+ errno, strerror(errno));
+ else if (code == rufl_IO_EOF)
+ snprintf(s, sizeof s, "error: %s: eof\n", context);
+ else
+ snprintf(s, sizeof s, "error: %s: unknown error\n", context);
+
+ die(s);
+}
+
+
+void die(const char *error)
+{
+ os_error warn_error;
+
+ warn_error.errnum = 1;
+ strncpy(warn_error.errmess, error,
+ sizeof warn_error.errmess - 1);
+ warn_error.errmess[sizeof warn_error.errmess - 1] = '\0';
+ xwimp_report_error(&warn_error,
+ wimp_ERROR_BOX_OK_ICON,
+ "RUfl Chars", 0);
+ rufl_quit();
+ exit(EXIT_FAILURE);
+}
diff --git a/rufl_dump_state.c b/rufl_dump_state.c
new file mode 100644
index 0000000..3a1fe99
--- /dev/null
+++ b/rufl_dump_state.c
@@ -0,0 +1,98 @@
+/*
+ * This file is part of RUfl
+ * Licensed under the MIT License,
+ * http://www.opensource.org/licenses/mit-license
+ * Copyright 2005 James Bursa <james@semichrome.net>
+ */
+
+#include <stdio.h>
+#include "rufl_internal.h"
+
+
+static void rufl_dump_character_set(struct rufl_character_set *charset);
+static void rufl_dump_substitution_table(void);
+
+
+/**
+ * Dump the internal library state to stdout.
+ */
+
+void rufl_dump_state(void)
+{
+ unsigned int i, j;
+
+ printf("rufl_font_list:\n");
+ for (i = 0; i != rufl_font_list_entries; i++) {
+ 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);
+ printf("\n");
+ } else {
+ printf(" (no charset table)\n");
+ }
+ }
+
+ printf("rufl_family_list:\n");
+ for (i = 0; i != rufl_family_list_entries; i++) {
+ printf(" %u \"%s\"\n", i, rufl_family_list[i]);
+ for (j = 0; j != rufl_STYLES; j++)
+ printf(" %u \"%s\"\n", j, rufl_font_list
+ [rufl_family_map[rufl_STYLES * i + j]]
+ .identifier);
+ }
+
+ printf("rufl_substitution_table:\n");
+ rufl_dump_substitution_table();
+}
+
+
+/**
+ * Dump a representation of a character set to stdout.
+ *
+ * \param charset character set to print
+ */
+
+void rufl_dump_character_set(struct rufl_character_set *charset)
+{
+ unsigned int u, t;
+
+ u = 0;
+ while (u != 0x10000) {
+ while (u != 0x10000 && !rufl_character_set_test(charset, u))
+ u++;
+ if (u != 0x10000) {
+ if (!rufl_character_set_test(charset, u + 1)) {
+ printf("%x ", u);
+ u++;
+ } else {
+ t = u;
+ while (rufl_character_set_test(charset, u))
+ u++;
+ printf("%x-%x ", t, u - 1);
+ }
+ }
+ }
+}
+
+
+/**
+ * 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_lookup(t);
+ while (u != 0x10000 && font == rufl_substitution_lookup(u))
+ u++;
+ if (font != NOT_AVAILABLE)
+ printf(" %x-%x => %u \"%s\"\n", t, u - 1,
+ font, rufl_font_list[font].identifier);
+ }
+}
diff --git a/rufl_init.c b/rufl_init.c
new file mode 100644
index 0000000..0f5424f
--- /dev/null
+++ b/rufl_init.c
@@ -0,0 +1,540 @@
+/*
+ * This file is part of RUfl
+ * Licensed under the MIT License,
+ * http://www.opensource.org/licenses/mit-license
+ * Copyright 2005 James Bursa <james@semichrome.net>
+ */
+
+#include <errno.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include "oslib/font.h"
+#include "oslib/hourglass.h"
+#include "rufl_internal.h"
+
+
+struct rufl_font_list_entry *rufl_font_list = 0;
+unsigned int rufl_font_list_entries = 0;
+char **rufl_family_list = 0;
+unsigned int rufl_family_list_entries = 0;
+unsigned int *rufl_family_map = 0;
+os_error *rufl_fm_error = 0;
+struct rufl_substitution_table *rufl_substitution_table = 0;
+struct rufl_cache_entry rufl_cache[rufl_CACHE_SIZE];
+int rufl_cache_time = 0;
+
+
+struct rufl_style_table_entry {
+ const char *name;
+ unsigned int style;
+};
+struct rufl_style_table_entry rufl_style_table[] = {
+ { "Bold", rufl_BOLD },
+ { "Bold.Italic", rufl_BOLD_SLANTED },
+ { "Bold.Oblique", rufl_BOLD_SLANTED },
+ { "Italic", rufl_SLANTED },
+ { "Medium", rufl_REGULAR },
+ { "Medium.Italic", rufl_SLANTED },
+ { "Medium.Oblique", rufl_SLANTED },
+ { "Oblique", rufl_SLANTED },
+ { "Regular", rufl_REGULAR },
+ { "Regular.Italic", rufl_SLANTED },
+ { "Regular.Oblique", rufl_SLANTED },
+};
+
+
+static rufl_code rufl_init_font_list(void);
+static int rufl_style_table_cmp(const void *keyval, const void *datum);
+static rufl_code rufl_init_scan_font(unsigned int font);
+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);
+
+
+/**
+ * Initialise RUfl.
+ *
+ * All available fonts are scanned. May take some time.
+ */
+
+rufl_code rufl_init(void)
+{
+ bool changes = false;
+ unsigned int i;
+ rufl_code code;
+
+ if (rufl_font_list_entries)
+ /* already initialized */
+ return rufl_OK;
+
+ xhourglass_on();
+
+ code = rufl_init_font_list();
+ if (code != rufl_OK) {
+ rufl_quit();
+ xhourglass_off();
+ return code;
+ }
+
+ code = rufl_load_cache();
+ if (code != rufl_OK) {
+ rufl_quit();
+ xhourglass_off();
+ return code;
+ }
+
+ xhourglass_leds(1, 0, 0);
+ for (i = 0; i != rufl_font_list_entries; i++) {
+ if (rufl_font_list[i].charset)
+ /* character set loaded from cache */
+ continue;
+ xhourglass_percentage(100 * i / rufl_font_list_entries);
+ code = rufl_init_scan_font(i);
+ if (code != rufl_OK) {
+ rufl_quit();
+ xhourglass_off();
+ return code;
+ }
+ changes = true;
+ }
+
+ xhourglass_leds(2, 0, 0);
+ code = rufl_init_substitution_table();
+ if (code != rufl_OK) {
+ rufl_quit();
+ xhourglass_off();
+ return code;
+ }
+
+ if (changes) {
+ xhourglass_leds(3, 0, 0);
+ code = rufl_save_cache();
+ if (code != rufl_OK) {
+ rufl_quit();
+ xhourglass_off();
+ return code;
+ }
+ }
+
+ for (i = 0; i != rufl_CACHE_SIZE; i++)
+ rufl_cache[i].font = rufl_CACHE_NONE;
+
+ xhourglass_off();
+
+ return rufl_OK;
+}
+
+
+/**
+ * Build list of font in rufl_font_list and list of font families
+ * in rufl_family_list.
+ */
+
+rufl_code rufl_init_font_list(void)
+{
+ int size;
+ struct rufl_font_list_entry *font_list;
+ char *identifier;
+ char *dot;
+ char **family_list;
+ char *family;
+ unsigned int *family_map;
+ unsigned int i;
+ font_list_context context = 0;
+ font_list_context context2;
+ struct rufl_style_table_entry *entry;
+
+ while (context != -1) {
+ /* find length of next identifier */
+ rufl_fm_error = xfont_list_fonts(0,
+ font_RETURN_FONT_NAME | context,
+ 0, 0, 0, 0,
+ &context2, &size, 0);
+ if (rufl_fm_error)
+ return rufl_FONT_MANAGER_ERROR;
+ if (context2 == -1)
+ break;
+
+ /* (re)allocate buffers */
+ font_list = realloc(rufl_font_list, sizeof rufl_font_list[0] *
+ (rufl_font_list_entries + 1));
+ if (!font_list)
+ return rufl_OUT_OF_MEMORY;
+ rufl_font_list = font_list;
+
+ identifier = malloc(size);
+ if (!identifier)
+ return rufl_OUT_OF_MEMORY;
+
+ rufl_font_list[rufl_font_list_entries].identifier = identifier;
+ rufl_font_list[rufl_font_list_entries].charset = 0;
+ rufl_font_list_entries++;
+
+ /* read identifier */
+ rufl_fm_error = xfont_list_fonts(identifier,
+ font_RETURN_FONT_NAME | context,
+ size, 0, 0, 0,
+ &context, 0, 0);
+ if (rufl_fm_error)
+ return rufl_FONT_MANAGER_ERROR;
+
+ /* add family to list, if it is new */
+ dot = strchr(identifier, '.');
+ if (2 <= rufl_font_list_entries && dot &&
+ strncmp(identifier, rufl_font_list
+ [rufl_font_list_entries - 2].identifier,
+ dot - identifier) == 0) {
+ /* same family as last font */
+ entry = bsearch(dot + 1, rufl_style_table,
+ sizeof rufl_style_table /
+ sizeof rufl_style_table[0],
+ sizeof rufl_style_table[0],
+ rufl_style_table_cmp);
+ if (entry)
+ rufl_family_map[rufl_STYLES *
+ (rufl_family_list_entries - 1) +
+ entry->style] =
+ rufl_font_list_entries - 1;
+ continue;
+ }
+
+ /* new family */
+ family_list = realloc(rufl_family_list,
+ sizeof rufl_family_list[0] *
+ (rufl_family_list_entries + 1));
+ if (!family_list)
+ return rufl_OUT_OF_MEMORY;
+ rufl_family_list = family_list;
+
+ family_map = realloc(rufl_family_map,
+ rufl_STYLES * sizeof rufl_family_map[0] *
+ (rufl_family_list_entries + 1));
+ if (!family_map)
+ return rufl_OUT_OF_MEMORY;
+ rufl_family_map = family_map;
+
+ if (dot)
+ family = strndup(identifier, dot - identifier);
+ else
+ family = strdup(identifier);
+ if (!family)
+ return rufl_OUT_OF_MEMORY;
+
+ rufl_family_list[rufl_family_list_entries] = family;
+ for (i = 0; i != rufl_STYLES; i++)
+ rufl_family_map[rufl_STYLES * rufl_family_list_entries +
+ i] = rufl_font_list_entries - 1;
+ rufl_family_list_entries++;
+ }
+
+ return rufl_OK;
+}
+
+
+int rufl_style_table_cmp(const void *keyval, const void *datum)
+{
+ const char *key = keyval;
+ const struct rufl_style_table_entry *entry = datum;
+ return strcmp(key, entry->name);
+}
+
+
+/**
+ * Scan a font for available characters.
+ */
+
+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 *charset;
+ struct rufl_character_set *charset2;
+ font_f font;
+ font_scan_block block = { { 0, 0 }, { 0, 0 }, -1, { 0, 0, 0, 0 } };
+
+ charset = calloc(1, sizeof *charset);
+ if (!charset)
+ return rufl_OUT_OF_MEMORY;
+
+ snprintf(font_name, sizeof font_name, "%s\\EUTF8",
+ rufl_font_list[font_index].identifier);
+
+ rufl_fm_error = xfont_find_font(font_name, 160, 160, 0, 0, &font, 0, 0);
+ if (rufl_fm_error) {
+ free(charset);
+ return rufl_FONT_MANAGER_ERROR;
+ }
+
+ /* scan through all characters */
+ for (u = 32; u != 0x10000; u++) {
+ 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 ||
+ (x_out == 0 && y_out == 0 &&
+ block.bbox.x0 == 0 && block.bbox.y0 == 0 &&
+ block.bbox.x1 == 0 && block.bbox.y1 == 0)) {
+ /* absent */
+ } else {
+ /* present */
+ byte = (u >> 3) & 31;
+ bit = u & 7;
+ charset->block[last_used][byte] |= 1 << bit;
+
+ block_count++;
+ }
+
+ 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;
+ }
+ }
+
+ xfont_lose_font(font);
+
+ if (rufl_fm_error) {
+ free(charset);
+ return rufl_FONT_MANAGER_ERROR;
+ }
+
+ /* 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;
+ }
+
+ rufl_font_list[font_index].charset = charset;
+
+ return rufl_OK;
+}
+
+
+/**
+ * Construct the font substitution table.
+ */
+
+rufl_code rufl_init_substitution_table(void)
+{
+ unsigned int block_count = 0;
+ unsigned int i;
+ unsigned int last_used = 0;
+ unsigned int u;
+ struct rufl_substitution_table *substitution_table2;
+
+ rufl_substitution_table = malloc(sizeof *rufl_substitution_table);
+ if (!rufl_substitution_table)
+ return rufl_OUT_OF_MEMORY;
+
+ /* scan through all characters */
+ for (u = 0; u != 0x10000; u++) {
+ rufl_substitution_table->block[last_used][u & 255] =
+ NOT_AVAILABLE;
+ for (i = 0; i != rufl_font_list_entries; i++) {
+ if (rufl_character_set_test(rufl_font_list[i].charset,
+ u)) {
+ rufl_substitution_table->block[last_used]
+ [u & 255] = i;
+ block_count++;
+ break;
+ }
+ }
+
+ if ((u + 1) % 256 == 0) {
+ /* end of block */
+ if (block_count == 0) {
+ rufl_substitution_table->index[u >> 8] =
+ BLOCK_NONE_AVAILABLE;
+ } else {
+ rufl_substitution_table->index[u >> 8] =
+ last_used;
+ last_used++;
+ if (last_used == 255)
+ /* too many characters */
+ break;
+ }
+ block_count = 0;
+ }
+ }
+
+ /* shrink-wrap */
+ substitution_table2 = realloc(rufl_substitution_table,
+ offsetof(struct rufl_substitution_table, block) +
+ sizeof (short) * 256 * last_used);
+ if (!substitution_table2)
+ return rufl_OUT_OF_MEMORY;
+ rufl_substitution_table = substitution_table2;
+
+ return rufl_OK;
+}
+
+
+/**
+ * Save character sets to cache.
+ */
+
+rufl_code rufl_save_cache(void)
+{
+ unsigned int i;
+ size_t len;
+ FILE *fp;
+
+ fp = fopen(rufl_CACHE, "wb");
+ if (!fp)
+ return rufl_IO_ERROR;
+
+ for (i = 0; i != rufl_font_list_entries; i++) {
+ /* length of font identifier */
+ len = strlen(rufl_font_list[i].identifier);
+ if (fwrite(&len, sizeof len, 1, fp) != 1) {
+ fclose(fp);
+ return rufl_IO_ERROR;
+ }
+
+ /* font identifier */
+ if (fwrite(rufl_font_list[i].identifier, len, 1, fp) != 1) {
+ fclose(fp);
+ return rufl_IO_ERROR;
+ }
+
+ /* character set */
+ if (fwrite(rufl_font_list[i].charset,
+ rufl_font_list[i].charset->size, 1, fp) != 1) {
+ fclose(fp);
+ return rufl_IO_ERROR;
+ }
+ }
+
+ if (fclose(fp) == EOF)
+ return rufl_IO_ERROR;
+
+ return rufl_OK;
+}
+
+
+/**
+ * Load character sets from cache.
+ */
+
+rufl_code rufl_load_cache(void)
+{
+ bool eof;
+ char *identifier;
+ size_t len, size;
+ FILE *fp;
+ struct rufl_font_list_entry *entry;
+ struct rufl_character_set *charset;
+
+ fp = fopen(rufl_CACHE, "rb");
+ if (!fp) {
+ if (errno == ENOENT)
+ return rufl_OK;
+ else
+ return rufl_IO_ERROR;
+ }
+
+ while (!feof(fp)) {
+ /* length of font identifier */
+ if (fread(&len, sizeof len, 1, fp) != 1) {
+ if (feof(fp))
+ break;
+ fclose(fp);
+ return rufl_IO_ERROR;
+ }
+
+ identifier = malloc(len + 1);
+ if (!identifier) {
+ fclose(fp);
+ return rufl_OUT_OF_MEMORY;
+ }
+
+ /* font identifier */
+ if (fread(identifier, len, 1, fp) != 1) {
+ eof = feof(fp);
+ free(identifier);
+ fclose(fp);
+ return eof ? rufl_IO_EOF : rufl_IO_ERROR;
+ }
+ identifier[len] = 0;
+
+ /* character set */
+ if (fread(&size, sizeof size, 1, fp) != 1) {
+ eof = feof(fp);
+ free(identifier);
+ fclose(fp);
+ return eof ? rufl_IO_EOF : rufl_IO_ERROR;
+ }
+
+ charset = malloc(size);
+ if (!charset) {
+ free(identifier);
+ fclose(fp);
+ return rufl_OUT_OF_MEMORY;
+ }
+
+ charset->size = size;
+ if (fread(charset->index, size - sizeof size, 1, fp) != 1) {
+ eof = feof(fp);
+ free(charset);
+ free(identifier);
+ fclose(fp);
+ return eof ? rufl_IO_EOF : rufl_IO_ERROR;
+ }
+
+ /* put in rufl_font_list */
+ entry = bsearch(identifier, rufl_font_list,
+ rufl_font_list_entries,
+ sizeof rufl_font_list[0], rufl_font_list_cmp);
+ if (entry)
+ entry->charset = charset;
+ else
+ free(charset);
+
+ free(identifier);
+ }
+
+ if (fclose(fp) == EOF)
+ return rufl_IO_ERROR;
+
+ return rufl_OK;
+}
+
+
+int rufl_font_list_cmp(const void *keyval, const void *datum)
+{
+ const char *key = keyval;
+ const struct rufl_font_list_entry *entry = datum;
+ return strcmp(key, entry->identifier);
+}
diff --git a/rufl_internal.h b/rufl_internal.h
new file mode 100644
index 0000000..0f9a75f
--- /dev/null
+++ b/rufl_internal.h
@@ -0,0 +1,134 @@
+/*
+ * This file is part of RUfl
+ * Licensed under the MIT License,
+ * http://www.opensource.org/licenses/mit-license
+ * Copyright 2005 James Bursa <james@semichrome.net>
+ */
+
+#include <limits.h>
+#include "oslib/font.h"
+#include "rufl.h"
+
+
+/** 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. */
+struct rufl_character_set {
+ /** Size of structure / bytes. */
+ size_t size;
+
+ /** 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];
+ /** The block has no characters present. */
+# define BLOCK_EMPTY 254
+ /** All characters in the block are present. */
+# define BLOCK_FULL 255
+
+ /** Block table. Each entry is a 256-bit bitmap indicating which
+ * characters in the block are present and absent. */
+ unsigned char block[254][32];
+};
+
+
+/** An entry in rufl_font_list. */
+struct rufl_font_list_entry {
+ /** Font identifier (name). */
+ char *identifier;
+ /** Character set of font. */
+ struct rufl_character_set *charset;
+};
+/** List of all available fonts. */
+extern struct rufl_font_list_entry *rufl_font_list;
+/** Number of entries in rufl_font_list. */
+extern unsigned int rufl_font_list_entries;
+
+
+#define rufl_STYLES 4
+
+/** Map from font family to fonts. rufl_STYLES entries per family. */
+extern unsigned int *rufl_family_map;
+
+
+/** Map from characters to a font which includes them. A typical machine might
+ * have characters from 30 blocks, giving 15616 bytes. */
+struct rufl_substitution_table {
+ /** 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_NONE_AVAILABLE or an offset into the block table. */
+ unsigned char index[256];
+ /** None of the characters in the block are available in any font. */
+# define BLOCK_NONE_AVAILABLE 255
+
+ /** Block table. Each entry is a map from the characters in the block
+ * to a font number which includes it, or NOT_AVAILABLE. */
+ unsigned short block[255][256];
+ /** No font contains this character. */
+# define NOT_AVAILABLE 65535
+};
+
+
+/** Font substitution table. */
+extern struct rufl_substitution_table *rufl_substitution_table;
+
+
+/** 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. */
+#define rufl_CACHE_SIZE 10
+
+/** An entry in rufl_cache. */
+struct rufl_cache_entry {
+ /** Font number (index in rufl_font_list), or rufl_CACHE_*. */
+ unsigned int 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;
+ /** Value of rufl_cache_time when last used. */
+ unsigned int 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;
+
+
+bool rufl_character_set_test(struct rufl_character_set *charset,
+ unsigned int c);
+unsigned int rufl_substitution_lookup(unsigned int c);
+
+
+#define rufl_utf8_read(s, l, u) \
+ if (4 <= l && ((s[0] & 0xf8) == 0xf0) && ((s[1] & 0xc0) == 0x80) && \
+ ((s[2] & 0xc0) == 0x80) && ((s[3] & 0xc0) == 0x80)) { \
+ u = ((s[0] & 0x7) << 18) | ((s[1] & 0x3f) << 12) | \
+ ((s[2] & 0x3f) << 6) | (s[3] & 0x3f); \
+ s += 4; l -= 4; \
+ } 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; \
+ } else if (2 <= l && ((s[0] & 0xe0) == 0xc0) && \
+ ((s[1] & 0xc0) == 0x80)) { \
+ u = ((s[0] & 0x3f) << 6) | (s[1] & 0x3f); \
+ s += 2; l -= 2; \
+ } else if ((s[0] & 0x80) == 0) { \
+ u = s[0]; \
+ s++; l--; \
+ } else { \
+ u = 0xfffd; \
+ s++; l--; \
+ }
+
+#define rufl_CACHE "<Wimp$ScrapDir>.RUfl_cache"
diff --git a/rufl_paint.c b/rufl_paint.c
new file mode 100644
index 0000000..9014d96
--- /dev/null
+++ b/rufl_paint.c
@@ -0,0 +1,255 @@
+/*
+ * This file is part of RUfl
+ * Licensed under the MIT License,
+ * http://www.opensource.org/licenses/mit-license
+ * Copyright 2005 James Bursa <james@semichrome.net>
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "oslib/font.h"
+#include "rufl_internal.h"
+
+
+static int rufl_family_list_cmp(const void *keyval, const void *datum);
+static rufl_code rufl_paint_span(unsigned short *s, unsigned int n,
+ unsigned int font, unsigned int font_size, int *x, int y);
+static rufl_code rufl_paint_not_available(unsigned short *s, unsigned int n,
+ unsigned int font_size, int *x, int y);
+static rufl_code rufl_place_in_cache(unsigned int font, unsigned int font_size,
+ font_f f);
+
+
+/**
+ * Render Unicode text.
+ */
+
+rufl_code rufl_paint(const char *font_family, rufl_style font_style,
+ unsigned int font_size,
+ const char *string, size_t length,
+ int x, int y)
+{
+ unsigned short s[80];
+ unsigned int font;
+ unsigned int font0, font1;
+ unsigned int n;
+ unsigned int u;
+ char **family;
+ struct rufl_character_set *charset;
+ rufl_code code;
+
+ if (length == 0)
+ return rufl_OK;
+
+ family = bsearch(font_family, rufl_family_list,
+ rufl_family_list_entries,
+ sizeof rufl_family_list[0], rufl_family_list_cmp);
+ if (!family)
+ return rufl_FONT_NOT_FOUND;
+ font = rufl_family_map[rufl_STYLES * (family - rufl_family_list) +
+ font_style];
+ charset = rufl_font_list[font].charset;
+
+ rufl_utf8_read(string, length, u);
+ if (rufl_character_set_test(charset, u))
+ font1 = font;
+ else
+ font1 = rufl_substitution_lookup(u);
+ do {
+ s[0] = u;
+ n = 1;
+ font0 = font1;
+ /* invariant: s[0..n) is in font font0 */
+ while (0 < length && n < 70 && font1 == font0) {
+ rufl_utf8_read(string, length, u);
+ s[n] = u;
+ if (rufl_character_set_test(charset, u))
+ font1 = font;
+ else
+ font1 = rufl_substitution_lookup(u);
+ if (font1 == font0)
+ n++;
+ }
+ s[n] = 0;
+
+ if (font0 == NOT_AVAILABLE)
+ code = rufl_paint_not_available(s, n, font_size, &x, y);
+ else
+ code = rufl_paint_span(s, n, font0, font_size, &x, y);
+
+ if (code != rufl_OK)
+ return code;
+
+ } while (!(length == 0 && font1 == font0));
+
+ return rufl_OK;
+}
+
+
+int rufl_family_list_cmp(const void *keyval, const void *datum)
+{
+ const char *key = keyval;
+ const char * const *entry = datum;
+ return strcmp(key, *entry);
+}
+
+
+/**
+ * Render a string of characters from a single RISC OS font.
+ */
+
+rufl_code rufl_paint_span(unsigned short *s, unsigned int n,
+ unsigned int font, unsigned int font_size, int *x, int y)
+{
+ char font_name[80];
+ int x_out, y_out;
+ unsigned int i;
+ font_f f;
+ rufl_code code;
+
+ /* search cache */
+ for (i = 0; i != rufl_CACHE_SIZE; i++) {
+ if (rufl_cache[i].font == font &&
+ rufl_cache[i].size == font_size)
+ break;
+ }
+ if (i != rufl_CACHE_SIZE) {
+ /* found in cache */
+ f = rufl_cache[i].f;
+ rufl_cache[i].last_used = rufl_cache_time++;
+ } else {
+ /* not found */
+ snprintf(font_name, sizeof font_name, "%s\\EUTF8",
+ rufl_font_list[font].identifier);
+ rufl_fm_error = xfont_find_font(font_name,
+ font_size, font_size, 0, 0, &f, 0, 0);
+ if (rufl_fm_error)
+ return rufl_FONT_MANAGER_ERROR;
+ /* place in cache */
+ code = rufl_place_in_cache(font, font_size, f);
+ if (code != rufl_OK)
+ return code;
+ }
+
+ /* paint span */
+ rufl_fm_error = xfont_paint(f, (const char *) s, font_OS_UNITS |
+ font_GIVEN_LENGTH | font_GIVEN_FONT | font_KERN |
+ font_GIVEN16_BIT,
+ *x, y, 0, 0, n * 2);
+ if (rufl_fm_error) {
+ xfont_lose_font(f);
+ return rufl_FONT_MANAGER_ERROR;
+ }
+
+ /* increment x by width of span */
+ 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,
+ 0, &x_out, &y_out, 0);
+ if (rufl_fm_error) {
+ xfont_lose_font(f);
+ return rufl_FONT_MANAGER_ERROR;
+ }
+ *x += x_out / 400;
+
+ return rufl_OK;
+}
+
+
+/**
+ * Render a string of characters not available in any font as their hex code.
+ */
+
+rufl_code rufl_paint_not_available(unsigned short *s, unsigned int n,
+ unsigned int font_size, int *x, int y)
+{
+ char missing[] = "0000";
+ unsigned int i;
+ font_f f;
+ rufl_code code;
+
+ /* search cache */
+ for (i = 0; i != rufl_CACHE_SIZE; i++) {
+ if (rufl_cache[i].font == rufl_CACHE_CORPUS &&
+ rufl_cache[i].size == font_size)
+ break;
+ }
+ if (i != rufl_CACHE_SIZE) {
+ /* found in cache */
+ f = rufl_cache[i].f;
+ rufl_cache[i].last_used = rufl_cache_time++;
+ } else {
+ /* not found */
+ rufl_fm_error = xfont_find_font("Corpus.Medium\\EUTF8",
+ font_size / 2, font_size / 2, 0, 0,
+ &f, 0, 0);
+ if (rufl_fm_error)
+ return rufl_FONT_MANAGER_ERROR;
+ /* place in cache */
+ code = rufl_place_in_cache(rufl_CACHE_CORPUS, font_size, f);
+ if (code != rufl_OK)
+ 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];
+
+ /* first two characters in top row */
+ rufl_fm_error = xfont_paint(f, missing, font_OS_UNITS |
+ font_GIVEN_LENGTH | font_GIVEN_FONT | font_KERN,
+ *x, y + 5 * font_size / 64,
+ 0, 0, 2);
+ if (rufl_fm_error)
+ return rufl_FONT_MANAGER_ERROR;
+
+ /* last two characters underneath */
+ rufl_fm_error = xfont_paint(f, missing + 2, font_OS_UNITS |
+ font_GIVEN_LENGTH | font_GIVEN_FONT | font_KERN,
+ *x, y, 0, 0, 2);
+ if (rufl_fm_error)
+ return rufl_FONT_MANAGER_ERROR;
+
+ *x += 7 * font_size / 64;
+ }
+
+ return rufl_OK;
+}
+
+
+/**
+ * Place a font into the recent-use cache, making space if necessary.
+ */
+
+rufl_code rufl_place_in_cache(unsigned int font, unsigned int font_size,
+ font_f f)
+{
+ unsigned int i;
+ unsigned int max_age = 0;
+ unsigned int evict = 0;
+
+ for (i = 0; i != rufl_CACHE_SIZE; i++) {
+ if (rufl_cache[i].font == rufl_CACHE_NONE) {
+ evict = i;
+ break;
+ } else if (max_age < rufl_cache_time -
+ rufl_cache[i].last_used) {
+ max_age = rufl_cache_time -
+ rufl_cache[i].last_used;
+ evict = i;
+ }
+ }
+ rufl_fm_error = xfont_lose_font(rufl_cache[evict].f);
+ if (rufl_fm_error)
+ return rufl_FONT_MANAGER_ERROR;
+ rufl_cache[evict].font = font;
+ rufl_cache[evict].size = font_size;
+ rufl_cache[evict].f = f;
+ rufl_cache[evict].last_used = rufl_cache_time++;
+
+ return rufl_OK;
+}
diff --git a/rufl_quit.c b/rufl_quit.c
new file mode 100644
index 0000000..e15a80b
--- /dev/null
+++ b/rufl_quit.c
@@ -0,0 +1,43 @@
+/*
+ * This file is part of RUfl
+ * Licensed under the MIT License,
+ * http://www.opensource.org/licenses/mit-license
+ * Copyright 2005 James Bursa <james@semichrome.net>
+ */
+
+#include <stdlib.h>
+#include "oslib/font.h"
+#include "rufl_internal.h"
+
+
+/**
+ * Free all resources used by the library.
+ */
+
+void rufl_quit(void)
+{
+ unsigned int i;
+
+ if (!rufl_font_list)
+ return;
+
+ for (i = 0; i != rufl_font_list_entries; i++) {
+ free(rufl_font_list[i].identifier);
+ free(rufl_font_list[i].charset);
+ }
+ free(rufl_font_list);
+ rufl_font_list = 0;
+
+ for (i = 0; i != rufl_family_list_entries; i++)
+ free(rufl_family_list[i]);
+ free(rufl_family_list);
+ free(rufl_family_map);
+ rufl_family_list = 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;
+ }
+ }
+}
diff --git a/rufl_substitution_lookup.c b/rufl_substitution_lookup.c
new file mode 100644
index 0000000..fba76aa
--- /dev/null
+++ b/rufl_substitution_lookup.c
@@ -0,0 +1,32 @@
+/*
+ * This file is part of RUfl
+ * Licensed under the MIT License,
+ * http://www.opensource.org/licenses/mit-license
+ * Copyright 2005 James Bursa <james@semichrome.net>
+ */
+
+#include "rufl_internal.h"
+
+
+/**
+ * Look up a character in the substitution table.
+ *
+ * \param c character to look up
+ * \return font number containing the character, or NOT_AVAILABLE
+ */
+
+unsigned int rufl_substitution_lookup(unsigned int c)
+{
+ unsigned int block = c >> 8;
+
+ if (256 < block)
+ return NOT_AVAILABLE;
+
+ if (rufl_substitution_table->index[block] == BLOCK_NONE_AVAILABLE)
+ return NOT_AVAILABLE;
+ else {
+ return rufl_substitution_table->block
+ [rufl_substitution_table->index[block]]
+ [c & 255];
+ }
+}
diff --git a/rufl_test.c b/rufl_test.c
new file mode 100644
index 0000000..b225595
--- /dev/null
+++ b/rufl_test.c
@@ -0,0 +1,54 @@
+/*
+ * This file is part of RUfl
+ * Licensed under the MIT License,
+ * http://www.opensource.org/licenses/mit-license
+ * Copyright 2005 James Bursa <james@semichrome.net>
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include "rufl.h"
+
+
+static void try(rufl_code code, const char *context);
+
+
+int main(void)
+{
+ char utf8_test[] = "Hello, world! ὕαλον "
+ "Uherské Hradiště.";
+
+ try(rufl_init(), "rufl_init");
+ rufl_dump_state();
+ try(rufl_paint("NewHall", rufl_REGULAR, 240,
+ utf8_test, sizeof utf8_test - 1,
+ 1200, 1200), "rufl_paint");
+ rufl_quit();
+
+ return 0;
+}
+
+
+void try(rufl_code code, const char *context)
+{
+ if (code == rufl_OK)
+ return;
+ else if (code == rufl_OUT_OF_MEMORY)
+ printf("error: %s: out of memory\n", context);
+ else if (code == rufl_FONT_MANAGER_ERROR)
+ printf("error: %s: Font Manager error %x %s\n", context,
+ rufl_fm_error->errnum,
+ rufl_fm_error->errmess);
+ else if (code == rufl_FONT_NOT_FOUND)
+ printf("error: %s: font not found\n", context);
+ else if (code == rufl_IO_ERROR)
+ printf("error: %s: io error: %i %s\n", context, errno,
+ strerror(errno));
+ else if (code == rufl_IO_EOF)
+ printf("error: %s: eof\n", context);
+ else
+ printf("error: %s: unknown error\n", context);
+ rufl_quit();
+ exit(1);
+}