diff options
author | James Bursa <james@netsurf-browser.org> | 2005-01-08 21:02:32 +0000 |
---|---|---|
committer | James Bursa <james@netsurf-browser.org> | 2005-01-08 21:02:32 +0000 |
commit | c4580a33f78825e385626610fbbff9a675b248a4 (patch) | |
tree | d4ab197d183e4a917202415cb7bd44b75d5c7866 | |
download | librufl-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-x | make | 3 | ||||
-rw-r--r-- | rufl.h | 77 | ||||
-rw-r--r-- | rufl_character_set_test.c | 36 | ||||
-rw-r--r-- | rufl_chars.c | 297 | ||||
-rw-r--r-- | rufl_dump_state.c | 98 | ||||
-rw-r--r-- | rufl_init.c | 540 | ||||
-rw-r--r-- | rufl_internal.h | 134 | ||||
-rw-r--r-- | rufl_paint.c | 255 | ||||
-rw-r--r-- | rufl_quit.c | 43 | ||||
-rw-r--r-- | rufl_substitution_lookup.c | 32 | ||||
-rw-r--r-- | rufl_test.c | 54 |
11 files changed, 1569 insertions, 0 deletions
@@ -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 @@ -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); +} |