diff options
Diffstat (limited to 'riscos/ufont.c')
-rw-r--r-- | riscos/ufont.c | 1281 |
1 files changed, 1281 insertions, 0 deletions
diff --git a/riscos/ufont.c b/riscos/ufont.c new file mode 100644 index 000000000..37767ccfe --- /dev/null +++ b/riscos/ufont.c @@ -0,0 +1,1281 @@ +/* ufont.c + * Licensed under the GNU General Public License, + * http://www.opensource.org/licenses/gpl-license + * Copyright 2000 James Bursa <bursa@users.sourceforge.net> + * Copyright 2004 John Tytgat <John.Tytgat@aaug.net> + */ + +/** \file + * UFont - Unicode wrapper for non-Unicode aware FontManager + * + * This code allows non-Unicode aware FontManager to be used for + * displaying Unicode encoded text lines. It needs the !UFont + * resource (accessed via UFont$Path). + */ + +#include <assert.h> +#include <limits.h> +#include <wchar.h> + +#include "oslib/osfile.h" + +#include "ufont.h" +#include "utils/utils.h" /* \todo: has to go ! */ + +// #define DEBUG_UFONT +// #define DEBUG_ACTIVATE_SANITY_CHECK + +#ifdef DEBUG_UFONT +# define dbg_fprintf fprintf +#else +# define dbg_fprintf (1)?0:fprintf +#endif +#ifdef DEBUG_ACTIVATE_SANITY_CHECK +# define do_sanity_check sanity_check +#else +# define do_sanity_check (1)?0:sanity_check +#endif +#define MALLOC_CHUNK 256 + +typedef struct usage_chain_s usage_chain_t; +typedef struct virtual_fh_s virtual_fh_t; +/* Virtual font handle : + */ +struct virtual_fh_s + { + const char *fontNameP; /* => malloc'ed block holding RISC OS font name */ + int xsize, ysize; /* font size */ + int xres, yres; /* requested or actual resolution */ + unsigned int usage; /* the higher, the more this font handle is used for setting its glyphs */ + unsigned int refCount; /* number of times this struct is refered from ufont_f element */ + usage_chain_t *usageP; /* Ptr to element usage chain; if non NULL, we have a RISC OS font handle allocated. When refCount is 0, this is not necessary NULL. */ + }; +#define kInitialFHArraySize 20 +static virtual_fh_t *oVirtualFHArrayP; +static size_t oCurVirtualFHArrayElems; +static size_t oMaxVirtualFHArrayElems; + +/* Usage chain (one element per open RISC OS font handle) : + */ +struct usage_chain_s + { + usage_chain_t *nextP; + usage_chain_t *prevP; + + size_t chainTimer; /* When equal to oChainTimer, you can not throw this element out the chain. */ + font_f ro_fhandle; /* RISC OS font handle (garanteed non zero) */ + virtual_fh_t *virFHP; + }; + +typedef struct ufont_map_s ufont_map_t; + +struct ufont_map_s + { + byte fontnr[65536]; /* Must be 1st (comes straight from 'Data' file). Each entry is an index in the virtual_font_index array. */ + byte character[65536]; /* Must be 2nd (comes straight from 'Data' file) */ + + const char *uFontNameP; /* => malloc'ed block holding UFont name */ + unsigned int refCount; + ufont_map_t *nextP; + }; +static const ufont_map_t *oMapCollectionP; + +struct ufont_font + { + const ufont_map_t *mapP; + int virtual_handles_used; /* Number of filled virtual_font_index[] elements */ + size_t virtual_font_index[256]; /* Index in the oVirtualFHArrayP */ + }; + +/* Walking the chain starting with oUsageChain->nextP and continuing via + * ->nextP until reaching oUsageChain again, results in equal or + * decreasing ->virFHP->usage values. + * Also walking the chain starting with oUsageChain->prevP and continuing + * via ->prevP until reaching oUsageChain again, results in equal or + * increasing ->virFHP->usage values. + */ +static usage_chain_t oUsageChain; +static size_t oCurUsageChainElems; +/* Maximum number of RISC OS handles open by UFont : + */ +#define kMaxUsageChainElems 80 +static size_t oChainTimer; + +static os_error *create_map(const char *fontNameP, const ufont_map_t **mapPP); +static os_error *delete_map(ufont_map_t *mapP); +static int eat_utf8(wchar_t *pwc, const byte *s, int n); +static os_error *addref_virtual_fonthandle(const char *fontNameP, int xsize, int ysize, int xres, int yres, int *xresOutP, int *yresOutP, size_t *offsetP); +static os_error *deref_virtual_fonthandle(size_t offset); +static os_error *activate_virtual_fh(virtual_fh_t *virFHP); +static os_error *remove_usage_chain_elem(usage_chain_t *usageElemP); +static void repos_usage_chain_elem(usage_chain_t *usageElemP); +static const char *get_rofontname(font_f rofhandle); +static void dump_internals(void); +static int sanity_check(const char *testMsgP); + +/* UFont error messages : + */ +static os_error error_badparams = { error_BAD_PARAMETERS, "Bad parameters" }; +static os_error error_exists = { error_FONT_NOT_FOUND, "UFont Fonts/Data file not found" }; +static os_error error_memory = { error_FONT_NO_ROOM, "Insufficient memory for font" }; +static os_error error_size = { error_FONT_BAD_FONT_FILE, "Wrong size of font file" }; +static os_error error_fnt_corrupt = { 1 /** \todo */, "UFont is corrupt" }; +static os_error error_toomany_handles = { 2 /** \todo */, "Too many UFont handles are needed to fulfill current request" }; +static os_error error_noufont = { 3 /** \todo */, "Unable to find UFont font" }; +static os_error error_badrohandle = { 4 /** \todo */, "Invalid internal RISC OS font handle" }; + +/* + * UFont_FindFont + * + * => as Font_FindFont, but + * font_name does not support '\' qualifiers + * + * <= as Font_FindFont, but + * handle is 32-bit + * Results from xres_out and yres_out are questionable because we + * delay-loading the real font data. + */ +os_error * +xufont_find_font(char const *fontNameP, + int xsize, + int ysize, + int xres, + int yres, + ufont_f *font, + int *xresOutP, + int *yresOutP) +{ + ufont_f fontP; + const char *old_font; + char *fonts_file; + char file_name[256]; // \todo: fixed size, i.e. not safe. + fileswitch_object_type objType; + int size; + os_error *errorP; + +if (font == NULL) + return &error_badparams; +/* Be sure never to return garbage as result. */ +*font = NULL; + +/* Allocate memory for UFont font set */ +if ((fontP = (ufont_f)calloc(1, sizeof(struct ufont_font))) == NULL) + return &error_memory; + +if ((errorP = create_map(fontNameP, &fontP->mapP)) != NULL) + { + (void)xufont_lose_font(fontP); + return errorP; + } +if (fontP->mapP == NULL) + { + (void)xufont_lose_font(fontP); + return &error_noufont; + } + +/* Find size of Fonts file : + */ +strcpy(file_name, fontNameP); +strcat(file_name, ".Fonts"); +if ((errorP = xosfile_read_stamped_path(file_name, "UFont:", &objType, NULL, NULL, &size, NULL, NULL)) != NULL) + { + (void)xufont_lose_font(fontP); + return errorP; + } +if (objType != fileswitch_IS_FILE) + { + (void)xufont_lose_font(fontP); + return &error_exists; + } + +if ((fonts_file = (char *)malloc(size)) == NULL) + { + (void)xufont_lose_font(fontP); + return &error_memory; + } + +/* Load Fonts : + */ +if ((errorP = xosfile_load_stamped_path(file_name, fonts_file, "UFont:", NULL, NULL, NULL, NULL, NULL)) != NULL) + { + (void)xufont_lose_font(fontP); + free((void *)fonts_file); + return errorP; + } + +/* Open all fonts listed in Fonts : + */ +for (old_font = fonts_file, fontP->virtual_handles_used = 0; + old_font - fonts_file < size; + old_font += strlen(old_font) + 1, ++fontP->virtual_handles_used) + { + /* UFont can maximum have 256 real RISC OS fonts : + */ + if (fontP->virtual_handles_used < 256) + { + dbg_fprintf(stderr, "%i %s: ", fontP->virtual_handles_used, old_font); + errorP = addref_virtual_fonthandle(old_font, xsize, ysize, xres, yres, xresOutP, yresOutP, &fontP->virtual_font_index[fontP->virtual_handles_used]); + } + else + errorP = &error_fnt_corrupt; + + if (errorP != NULL) + { + (void)xufont_lose_font(fontP); + free((void *)fonts_file); + return errorP; + } + + dbg_fprintf(stderr, "%i\n", fontP->virtual_font_index[fontP->virtual_handles_used]); + } + +/* free Fonts */ +free((void *)fonts_file); fonts_file = NULL; + +*font = fontP; +if (xresOutP != NULL) + *xresOutP = 96; +if (yresOutP != NULL) + *yresOutP = 96; +return NULL; +} + + +/* + * UFont_LoseFont + * + * => font handle as returned by UFont_FindFont + * Even if there was an error returned, we tried to delete as much + * as possible. The ufont_f is definately not reusable afterwards. + */ +os_error * +xufont_lose_font(ufont_f fontP) +{ + size_t index; + os_error *theErrorP; + +theErrorP = (fontP->mapP != NULL) ? delete_map(fontP->mapP) : NULL; + +/* Close all fonts used : + */ +for (index = 0; index < fontP->virtual_handles_used; ++index) + { + os_error *errorP; + dbg_fprintf(stderr, "About to deref virtual font handle %d\n", fontP->virtual_font_index[index]); + if ((errorP = deref_virtual_fonthandle(fontP->virtual_font_index[index])) != NULL) + theErrorP = errorP; + } + +/* Free ufont structure : + */ +free((void *)fontP); + +return theErrorP; +} + + +/* + * UFont_Paint + * + * => font handle as returned by UFont_FindFont + * string is Unicode UTF-8 encoded + * other parameters as Font_Paint + */ +os_error * +xufont_paint(ufont_f fontP, + unsigned char const *string, + font_string_flags flags, + int xpos, + int ypos, + font_paint_block const *block, + os_trfm const *trfm, + int length) +{ + char *result; + os_error *error; + + if ((flags & font_GIVEN_LENGTH) == 0) + length = INT_MAX; + + dbg_fprintf(stderr, "xufont_paint() : size %d, consider len %d\n", strlen(string), length); + if ((error = xufont_convert(fontP, string, length, &result, NULL)) != NULL) + return error; + if (result[0] == '\0') + return NULL; + + assert(result[0] == font_COMMAND_FONT); + error = xfont_paint(result[1], &result[2], + (flags & (~font_GIVEN_LENGTH)) | font_GIVEN_FONT, + xpos, ypos, block, trfm, 0); + return error; +} + + +/* + * UFont_ScanString + * + * => font handle as returned by UFont_FindFont + * string is Unicode UTF-8 encoded + * split length is index in string, not pointer + * other parameters as Font_ScanString + * + * <= as Font_ScanString + */ +os_error * +xufont_scan_string(ufont_f fontP, + unsigned char const *string, + font_string_flags flags, + int x, + int y, + font_scan_block const *block, + os_trfm const *trfm, + int length, + unsigned char const **split_point, + int *x_out, + int *y_out, + int *length_out) +{ + char *result; + char *split_point_i; + unsigned int *table; + os_error *error; + + if ((flags & font_GIVEN_LENGTH) == 0) + length = INT_MAX; + + dbg_fprintf(stderr, "xufont_scan_string() : size %d, consider len %d\n", strlen(string), length); + if ((error = xufont_convert(fontP, string, length, &result, &table)) != NULL) + return error; + if (result[0] == '\0') + { + if (split_point != NULL) + *split_point = string; + if (x_out != NULL) + *x_out = 0; + if (y_out != NULL) + *y_out = 0; + if (length_out != NULL) + *length_out = 0; + return NULL; + } + + assert(result[0] == font_COMMAND_FONT); + error = xfont_scan_string(result[1], &result[2], + (flags & (~font_GIVEN_LENGTH)) | font_GIVEN_FONT, + x, y, block, trfm, 0, + (split_point) ? &split_point_i : NULL, + x_out, y_out, length_out); + if (error != NULL) + return error; + + if (split_point != NULL) + { + dbg_fprintf(stderr, "RISC OS scan string split at offset %d (char %d)\n", split_point_i - result, *split_point_i); + *split_point = &string[table[split_point_i - result]]; + dbg_fprintf(stderr, "UTF-8 Split offset at %d (char %d)\n", *split_point - string, **split_point); + } + + return NULL; +} + + +/** + * Given a text line, return the number of bytes which can be set using + * one RISC OS font and the bounding box fitting that part of the text + * only. + * + * \param font a ufont font handle, as returned by xufont_find_font(). + * \param string string text. Does not have to be NUL terminated. + * \param flags FontManger flags to be used internally + * \param length length in bytes of the text to consider. + * \param width returned width of the text which can be set with one RISC OS font. If 0, then error happened or initial text length was 0. + * \param rofontname returned name of the RISC OS font which can be used to set the text. If NULL, then error happened or initial text length was 0. + * \param rotext returned string containing the characters in returned RISC OS font. Not necessary NUL terminated. free() after use. If NULL, then error happened or initial text length was 0. + * \param rolength length of return rotext string. If 0, then error happened or initial text length was 0. + * \param consumed number of bytes of the given text which can be set with one RISC OS font. If 0, then error happened or initial text length was 0. + */ +os_error *xufont_txtenum(ufont_f fontP, + unsigned char const *string, + font_string_flags flags, + size_t length, + int *widthP, + unsigned char const **rofontnameP, + unsigned char const **rotextP, + size_t *rolengthP, + size_t *consumedP) +{ + char *result, *end_result; + unsigned int *table; + os_error *errorP; + + int width; + const char *rofontname; + char *rotext; + size_t rolength; + + *rotextP = *rofontnameP = NULL; + *consumedP = *rolengthP = *widthP = 0; + + if ((flags & font_GIVEN_LENGTH) == 0) + length = INT_MAX; + + if (length == 0) + return NULL; + + if ((errorP = xufont_convert(fontP, string, length, &result, &table)) != NULL) + return errorP; + if (result[0] == '\0') + return NULL; + assert(result[0] == font_COMMAND_FONT); + + /* Find how many characters starting at <result + 2> onwards + are set using the RISC OS font with handle <result + 1> */ + for (end_result = result + 2; *end_result != '\0' && *end_result != font_COMMAND_FONT; ++end_result) + ; + + rolength = end_result - result - 2; + if ((errorP = xfont_scan_string(result[1], &result[2], + flags | font_GIVEN_LENGTH | font_GIVEN_FONT, + 0x7fffffff, 0x7fffffff, + NULL, NULL, + rolength, + NULL, + &width, NULL, + NULL)) != NULL) + return errorP; + if ((rofontname = get_rofontname(result[1])) == NULL) + return &error_badrohandle; + if ((rotext = malloc(rolength)) == NULL) + return &error_memory; + memcpy(rotext, result + 2, rolength); + + *widthP = width; + *rofontnameP = rofontname; + *rotextP = rotext; + *rolengthP = rolength; + *consumedP = table[end_result - result]; + + return NULL; +} + + +/* + * UFont_Convert + * + * => initial font + * UTF-8 string to convert to RISC OS font numbers and codes. + * max length to convert (characters) or NUL char terminated. + * + * <= string converted to Font_Paint format + * table of offsets in UTF-8 string + */ +os_error * +xufont_convert(ufont_f fontP, + unsigned char const *string, + size_t length, + char **presult, /* may not be NULL ! */ + size_t **ptable /* may be NULL */) +{ + static char *resultP; + static size_t *tableP; + static size_t currentSize; + + size_t max_length; + size_t string_index, new_string_index, result_index; + virtual_fh_t *curVirFH = NULL; + +assert(presult != NULL); + +do_sanity_check("xufont_convert() : begin"); + +/* Find upfront if we're NUL char terminated or length terminated. */ +for (max_length = 0; max_length < length && string[max_length] != '\0'; ++max_length) + /* no body */; + +/* Increase timer so we can enforce a set of usage elements to remain active. + */ +++oChainTimer; + +/* Ensure memory block. + */ +if (resultP == NULL) + { + if ((resultP = (char *)malloc(MALLOC_CHUNK)) == NULL) + return &error_memory; + currentSize = MALLOC_CHUNK; + } +if (tableP == NULL && (tableP = (size_t *)malloc(MALLOC_CHUNK * sizeof(size_t))) == NULL) + return &error_memory; + +dbg_fprintf(stderr, "xufont_convert() : "); +for (string_index = 0, result_index = 0; + string_index < max_length; + string_index = new_string_index) + { + wchar_t wchar; + int result = eat_utf8(&wchar, &string[string_index], max_length - string_index); + + if (result == 0) + { + /* Too few input bytes : abort conversion */ + fprintf(stderr, "eat_utf8() : too few input bytes\n"); + break; + } + else if (result < 0) + { + /* Corrupt UTF-8 stream : skip <-result> input characters. */ + fprintf(stderr, "eat_utf8() : error %d\n", result); +fprintf(stderr, "String <%.*s> error pos %d\n", length, string, string_index); + wchar = '?'; + new_string_index = string_index - result; + } + else + { + /* Normal case : one wchar_t produced, <result> bytes consumed. */ + if (wchar >= 0x10000) + wchar = '?'; + new_string_index = string_index + result; + } + + dbg_fprintf(stderr, "src offset 0x%x : 0x%x ", string_index, wchar); + + /* Reserve room for at least 32 more entries. + */ + if (result_index + 32 > currentSize) + { + if ((resultP = realloc(resultP, currentSize*2)) == NULL + || (tableP = realloc(tableP, currentSize*2 * sizeof(size_t))) == NULL) + return &error_memory; + + currentSize *= 2; + } + + { + const byte fontnr = fontP->mapP->fontnr[wchar]; + virtual_fh_t *virFHP; + usage_chain_t *usageP; + + assert(fontnr < fontP->virtual_handles_used); + virFHP = &oVirtualFHArrayP[fontP->virtual_font_index[fontnr]]; + + /* Check if current font is ok : + */ + if (virFHP != curVirFH) + { + os_error *errorP; + + curVirFH = virFHP; + + /* Make sure we have a RISC OS font handle associated : + */ + if ((errorP = activate_virtual_fh(virFHP)) != NULL) + return errorP; + usageP = virFHP->usageP; + assert(usageP != NULL); + assert(usageP->ro_fhandle != 0); + tableP[result_index] = tableP[result_index + 1] = string_index; + resultP[result_index++] = font_COMMAND_FONT; + resultP[result_index++] = usageP->ro_fhandle; + + dbg_fprintf(stderr, "{%i} ", resultP[result_index - 1]); + } + else + { + usageP = virFHP->usageP; + assert(usageP != NULL); + } + + ++virFHP->usage; + /* By increasing the usage counter, it might that the oUsageChain needs + * reordering. + */ + if (usageP != oUsageChain.nextP && virFHP->usage > usageP->prevP->virFHP->usage) + repos_usage_chain_elem(usageP); + + tableP[result_index] = string_index; + resultP[result_index++] = fontP->mapP->character[wchar]; + dbg_fprintf(stderr, "[0x%x] ", resultP[result_index - 1]); + } + } +resultP[result_index] = 0; +*presult = resultP; + +tableP[result_index] = string_index; +if (ptable != NULL) + *ptable = tableP; + +#ifdef DEBUG_UFONT +fprintf(stderr, "\nRISC OS font string result:\n"); + +for (result_index = 0; resultP[result_index] != 0; ++result_index) + fprintf(stderr, " Dst offset %d : 0x%x (src offset %d)\n", result_index, resultP[result_index], tableP[result_index]); +#endif + +do_sanity_check("xufont_convert() : end"); + +dbg_fprintf(stderr, "--- After convert()\n"); +//dump_internals(); + +return NULL; +} + + +/* Creates or reuses an existing ufont_map_t + */ +static os_error *create_map(const char *uFontNameP, const ufont_map_t **mapPP) +{ + ufont_map_t *curMapP; + size_t uFontNameLen = strlen(uFontNameP); + char *fileNameP; + os_error *errorP; + +/* Make sure we never return garbage results. + */ +*mapPP = NULL; + +if ((fileNameP = (char *)alloca(uFontNameLen + sizeof(".Data"))) == NULL) + return &error_memory; +memcpy(fileNameP, uFontNameP, uFontNameLen); +do { + fileswitch_object_type objType; + int size; + + memcpy(&fileNameP[uFontNameLen], ".Data", sizeof(".Data")); + if ((errorP = xosfile_read_stamped_path(fileNameP, "UFont:", &objType, NULL, NULL, &size, NULL, NULL)) != NULL) + return errorP; + + if (objType == fileswitch_NOT_FOUND) + { + /* Look for the Data file one directory level up. */ + while (uFontNameLen != 0 && fileNameP[--uFontNameLen] != '.') + /* no body */; + if (uFontNameLen == 0) + return &error_exists; + } + else if (objType == fileswitch_IS_FILE) + { + if (size != 2*65536) + return &error_size; + break; + } + else + return &error_exists; + } while (1); + +/* Try to reuse an existing map : + */ +for (curMapP = oMapCollectionP; curMapP != NULL; curMapP = curMapP->nextP) + { + size_t curUFontNameLen = strlen(curMapP->uFontNameP); + + if (uFontNameLen != curUFontNameLen) + continue; + + if (memcmp(fileNameP, curMapP->uFontNameP, curUFontNameLen) != 0) + break; + } +if (curMapP != NULL) + { + ++curMapP->refCount; + *mapPP = curMapP; + return NULL; + } + +/* We need to create & load new map into memory : + */ +if ((curMapP = (ufont_map_t *)malloc(sizeof(ufont_map_t))) == NULL) + return &error_memory; + +/* Load Data file : + */ +if ((errorP = xosfile_load_stamped_path(fileNameP, (byte *)&curMapP->fontnr[0], "UFont:", NULL, NULL, NULL, NULL, NULL)) != NULL) + { + free((void *)curMapP); + return errorP; + } +fileNameP[uFontNameLen] = '\0'; +if ((curMapP->uFontNameP = strdup(fileNameP)) == NULL) + { + free((void *)curMapP); + return &error_memory; + } +curMapP->refCount = 1; +curMapP->nextP = oMapCollectionP; +oMapCollectionP = curMapP; + +*mapPP = curMapP; +return NULL; +} + + +static os_error *delete_map(ufont_map_t *mapP) +{ +assert(mapP->refCount > 0); +--mapP->refCount; +/* \todo: we don't remove the map from the oMapCollection list. Should we ? + */ + +return NULL; +} + + +/* Returns: + * x > 0: number of bytes consumed at s, valid wchar_t returned at pwc[0] + * x = 0: too few input bytes, pwc[0] is undefined + * x < 0: illegal UTF-8 stream, skip -x characters at s, pwc[0] is undefined + */ +static int eat_utf8(wchar_t *pwc, const byte *s, int n) +{ + byte c; + int i; + +#if 0 +fputs("<", stderr); +for (i = 0; i < n; ++i) + fputc(s[i], stderr); +fputs(">\n", stderr); +#endif + if (n < 1) + return 0; /* not enough input bytes */ + else if ((c = s[0]) < 0x80) { + *pwc = c; + return 1; + } else if (c < 0xc2) + goto do_sync; + else if (c < 0xe0) { + if (n < 2) + return 0; /* not enough input bytes */ + if (!((s[1] ^ 0x80) < 0x40)) + goto do_sync; + *pwc = ((wchar_t) (c & 0x1f) << 6) | (wchar_t) (s[1] ^ 0x80); + return 2; + } else if (c < 0xf0) { + if (n < 3) + return 0; /* not enough input bytes */ + if (!((s[1] ^ 0x80) < 0x40 && (s[2] ^ 0x80) < 0x40 && (c >= 0xe1 || s[1] >= 0xa0))) + goto do_sync; + *pwc = ((wchar_t) (c & 0x0f) << 12) | ((wchar_t) (s[1] ^ 0x80) << 6) | (wchar_t) (s[2] ^ 0x80); + return 3; + } else if (c < 0xf8) { + if (n < 4) + return 0; /* not enough input bytes */ + if (!((s[1] ^ 0x80) < 0x40 && (s[2] ^ 0x80) < 0x40 && (s[3] ^ 0x80) < 0x40 && (c >= 0xf1 || s[1] >= 0x90))) + goto do_sync; + *pwc = ((wchar_t) (c & 0x07) << 18) | ((wchar_t) (s[1] ^ 0x80) << 12) | ((wchar_t) (s[2] ^ 0x80) << 6) | (wchar_t) (s[3] ^ 0x80); + return 4; + } else if (c < 0xfc) { + if (n < 5) + return 0; /* not enough input bytes */ + if (!((s[1] ^ 0x80) < 0x40 && (s[2] ^ 0x80) < 0x40 && (s[3] ^ 0x80) < 0x40 && (s[4] ^ 0x80) < 0x40 && (c >= 0xf9 || s[1] >= 0x88))) + goto do_sync; + *pwc = ((wchar_t) (c & 0x03) << 24) | ((wchar_t) (s[1] ^ 0x80) << 18) | ((wchar_t) (s[2] ^ 0x80) << 12) | ((wchar_t) (s[3] ^ 0x80) << 6) | (wchar_t) (s[4] ^ 0x80); + return 5; + } else if (c < 0xfe) { + if (n < 6) + return 0; /* not enough input bytes */ + if (!((s[1] ^ 0x80) < 0x40 && (s[2] ^ 0x80) < 0x40 && (s[3] ^ 0x80) < 0x40 && (s[4] ^ 0x80) < 0x40 && (s[5] ^ 0x80) < 0x40 && (c >= 0xfd || s[1] >= 0x84))) + goto do_sync; + *pwc = ((wchar_t) (c & 0x01) << 30) | ((wchar_t) (s[1] ^ 0x80) << 24) | ((wchar_t) (s[2] ^ 0x80) << 18) | ((wchar_t) (s[3] ^ 0x80) << 12) | ((wchar_t) (s[4] ^ 0x80) << 6) | (wchar_t) (s[5] ^ 0x80); + return 6; + } + +do_sync: + /* The UTF-8 sequence at s is illegal, skipping from s onwards + * until first non top character is found or %11xxxxxx is found + * (both valid UTF-8 *starts* - not necessary valid sequences). + */ + for (i = 1; i < n && !((s[i] & 0x80) == 0x00 || (s[i] & 0xC0) == 0xC0); ++i) + /* no body */; + return -i; +} + + +/* Adds the RISC OS font <fontNameP> to the oVirtualFHArrayP list and + * returns the index in that array. + * oVirtualFHArrayP can be reallocated (and all virFHP ptrs in oUsageChain). + * Results in xresOutP and yresOutP are not always that meaningful because + * of the delayed-loading of the real RISC OS font data. + * xresOutP and/or yresOutP may be NULL. + */ +static os_error *addref_virtual_fonthandle(const char *fontNameP, int xsize, int ysize, int xres, int yres, int *xresOutP, int *yresOutP, size_t *offsetP) +{ + size_t curIndex; + virtual_fh_t *unusedSlotP; + +assert(offsetP != NULL); + +do_sanity_check("addref_virtual_fonthandle() : begin"); + +if (oVirtualFHArrayP == NULL) + { + if ((oVirtualFHArrayP = (virtual_fh_t *)calloc(kInitialFHArraySize, sizeof(virtual_fh_t))) == NULL) + return &error_memory; + /* oCurVirtualFHArrayElems = 0; Isn't really necessary because of static */ + oMaxVirtualFHArrayElems = kInitialFHArraySize; + } + +/* Check for duplicate (and find first unused slot if any) : + */ +for (unusedSlotP = NULL, curIndex = 0; + curIndex < oCurVirtualFHArrayElems; + ++curIndex) + { + virtual_fh_t *virFHP = &oVirtualFHArrayP[curIndex]; + + if (virFHP->fontNameP != NULL /* case strdup(fontNameP) failed */ + && stricmp(virFHP->fontNameP, fontNameP) == 0 + && virFHP->xsize == xsize && virFHP->ysize == ysize) + { + if (xresOutP != NULL) + *xresOutP = virFHP->xres; + if (yresOutP != NULL) + *yresOutP = virFHP->yres; + ++virFHP->refCount; + *offsetP = curIndex; + + do_sanity_check("addref_virtual_fonthandle() : case 1"); + return NULL; + } + + if (virFHP->refCount == 0 && unusedSlotP == NULL) + unusedSlotP = virFHP; + } + +/* Can we reuse a slot ? + * I.e. a virtual FH which refCount is zero. + */ +if (unusedSlotP != NULL) + { + if (unusedSlotP->usageP != NULL) + { + os_error *errorP; + + /* This slot is refered in the usage chain, we have to unlink it. + */ + if ((errorP = remove_usage_chain_elem(unusedSlotP->usageP)) != NULL) + return errorP; + } + + unusedSlotP->usage = 0; + if (unusedSlotP->fontNameP != NULL) + free((void *)unusedSlotP->fontNameP); + if ((unusedSlotP->fontNameP = strdup(fontNameP)) == NULL) + return &error_memory; + + unusedSlotP->xsize = xsize; + unusedSlotP->ysize = ysize; + unusedSlotP->xres = (xres > 1) ? xres : 96; + if (xresOutP != NULL) + *xresOutP = unusedSlotP->xres; + unusedSlotP->yres = (yres > 1) ? yres : 96; + if (yresOutP != NULL) + *yresOutP = unusedSlotP->yres; + unusedSlotP->refCount = 1; + *offsetP = unusedSlotP - oVirtualFHArrayP; + do_sanity_check("addref_virtual_fonthandle() : case 2"); + return NULL; + } + +/* Add new entry : + */ +if (oCurVirtualFHArrayElems == oMaxVirtualFHArrayElems) + { + virtual_fh_t *newVirtualFHArrayP; + size_t extraOffset; + usage_chain_t *usageP; + + /* Don't use realloc() as when that fails, we don't even have the original + * memory block anymore. + */ + if ((newVirtualFHArrayP = (virtual_fh_t *)calloc(2*oMaxVirtualFHArrayElems, sizeof(virtual_fh_t))) == NULL) + return &error_memory; + memcpy(newVirtualFHArrayP, oVirtualFHArrayP, oMaxVirtualFHArrayElems * sizeof(virtual_fh_t)); + free((void *)oVirtualFHArrayP); + extraOffset = (const char *)newVirtualFHArrayP - (const char *)oVirtualFHArrayP; + oVirtualFHArrayP = newVirtualFHArrayP; + oMaxVirtualFHArrayElems *= 2; + + /* Update the virFHP pointers in the usage chain : + */ + if (oUsageChain.nextP != NULL) + { + for (usageP = oUsageChain.nextP; usageP != &oUsageChain; usageP = usageP->nextP) + usageP->virFHP = (virtual_fh_t *)&((char *)usageP->virFHP)[extraOffset]; + } + } + +unusedSlotP = &oVirtualFHArrayP[oCurVirtualFHArrayElems]; +if ((unusedSlotP->fontNameP = (const char *)strdup(fontNameP)) == NULL) + return &error_memory; +unusedSlotP->xsize = xsize; +unusedSlotP->ysize = ysize; +unusedSlotP->xres = (xres > 1) ? xres : 96; +if (xresOutP != NULL) + *xresOutP = unusedSlotP->xres; +unusedSlotP->yres = (yres > 1) ? yres : 96; +if (yresOutP != NULL) + *yresOutP = unusedSlotP->yres; +unusedSlotP->refCount = 1; +*offsetP = oCurVirtualFHArrayElems++; + +do_sanity_check("addref_virtual_fonthandle() : case 3"); +return NULL; +} + + +/* Deref virtual_fh_t element in oVirtualFHArrayP array. + */ +static os_error *deref_virtual_fonthandle(size_t offset) +{ +assert(offset >= 0 && offset < oCurVirtualFHArrayElems); +assert(oVirtualFHArrayP[offset].refCount > 0); + +/* When the refCount reaches 0, it will be reused by preference when a + * new usageChain element is needed in addref_virtual_fonthandle(). + */ +--oVirtualFHArrayP[offset].refCount; + +do_sanity_check("deref_virtual_fonthandle()"); +return NULL; +} + + +/* Virtual font handle <virFHP> needs to have a RISC OS font handle + * associated via virFHP->usageP. For this we can throw out all other + * usage chain elements which have a chainTimer different from oChainTimer. + * However, we may not have more than kMaxUsageChainElems chain elements + * active. + * Afterwards: either an error, either virFHP->usageP points to an usage chain + * element in the oUsageChain linked list and that usage chain element has + * a valid RISC OS font handle associated. + */ +static os_error *activate_virtual_fh(virtual_fh_t *virFHP) +{ + usage_chain_t *usageP; + +dbg_fprintf(stderr, "+++ activate_virtual_fh(virFHP %p, usageP ? %p)\n", virFHP, virFHP->usageP); +do_sanity_check("activate_virtual_fh() : begin"); + +/* The easiest case : we already have a RISC OS font handle : + */ +if ((usageP = virFHP->usageP) != NULL) + { + usageP->chainTimer = oChainTimer; + assert(usageP->ro_fhandle != 0); + assert(usageP->virFHP == virFHP); + do_sanity_check("activate_virtual_fh() : case 1"); + dbg_fprintf(stderr, "--- done, activate_virtual_fh(), case 1\n"); + return NULL; + } + +/* The second easiest case : we're still allowed to create an extra + * chain element : + */ +if (oCurUsageChainElems < kMaxUsageChainElems) + { + os_error *errorP; + + if ((usageP = (usage_chain_t *)malloc(sizeof(usage_chain_t))) == NULL) + return &error_memory; + usageP->chainTimer = oChainTimer; + + if ((errorP = xfont_find_font(virFHP->fontNameP, + virFHP->xsize, virFHP->ysize, + virFHP->xres, virFHP->yres, + &usageP->ro_fhandle, + &virFHP->xres, &virFHP->yres)) != NULL) + { + free((void *)usageP); + return errorP; + } + usageP->virFHP = virFHP; + virFHP->usageP = usageP; + ++oCurUsageChainElems; + + /* Make sure oUsageChain nextP and prevP point to at least something (is + * only executed once and should probably better be done in a global + * init routine). + */ + if (oUsageChain.nextP == NULL) + oUsageChain.nextP = oUsageChain.prevP = &oUsageChain; + } +else + { + os_error *errorP; + + /* The more difficult one : we need to reuse a usage chain element. + * Take the last one because that is least used but skip the onces + * with chainTimer equal to the current oChainTimer because those + * RISC OS font handles are still used in the font string we're + * currently processing. + */ + for (usageP = oUsageChain.prevP; + usageP != &oUsageChain && usageP->chainTimer == oChainTimer; + usageP = usageP->prevP) + /* no body */; + if (usageP == &oUsageChain) + { + /* Painful : all usage chain elements are in use and we already have the + * maximum of chain elements reached. + */ + return &error_toomany_handles; + } + + usageP->chainTimer = oChainTimer; + /* The virtual font handle currently in usageP->virFHP no longer has a real + * RISC OS font handle anymore. + */ + usageP->virFHP->usageP = NULL; + if ((errorP = xfont_lose_font(usageP->ro_fhandle)) != NULL) + return errorP; + if ((errorP = xfont_find_font(virFHP->fontNameP, + virFHP->xsize, virFHP->ysize, + virFHP->xres, virFHP->yres, + &usageP->ro_fhandle, + &virFHP->xres, &virFHP->yres)) != NULL) + return errorP; + usageP->virFHP = virFHP; + virFHP->usageP = usageP; + + /* Delink : + */ + usageP->prevP->nextP = usageP->nextP; + usageP->nextP->prevP = usageP->prevP; + } + +/* Link usageP in the oUsageChain based on its current virFHP->usage value : + */ + { + const unsigned int usage = virFHP->usage; + usage_chain_t *runUsageP; + + for (runUsageP = &oUsageChain; + runUsageP != oUsageChain.nextP && runUsageP->prevP->virFHP->usage <= usage; + runUsageP = runUsageP->prevP) + /* no body */; + + /* We have to link usageP between runUsageP and runUsageP->prevP + * because runUsageP->prevP has higher usage than the one we need to + * link in -or- we're at the end/start. + */ + usageP->nextP = runUsageP; + usageP->prevP = runUsageP->prevP; + runUsageP->prevP->nextP = usageP; + runUsageP->prevP = usageP; + } +do_sanity_check("activate_virtual_fh() : case 2"); +dbg_fprintf(stderr, "--- done, activate_virtual_fh(), case 2\n"); + +return NULL; +} + + +/* Remove this element from the usage chain : + */ +static os_error *remove_usage_chain_elem(usage_chain_t *usageP) +{ + os_error *errorP; + +assert(usageP != NULL); +assert(oCurUsageChainElems > 0); +assert(usageP->ro_fhandle != 0); +assert(usageP->virFHP != NULL); + +if ((errorP = xfont_lose_font(usageP->ro_fhandle)) != NULL) + return errorP; + +usageP->virFHP->usageP = NULL; + +/* Delink it : + */ +usageP->prevP->nextP = usageP->nextP; +usageP->nextP->prevP = usageP->prevP; + +--oCurUsageChainElems; +free((void *)usageP); + +do_sanity_check("remove_usage_chain_elem() : end"); +return NULL; +} + + +/* usageP is no longer in the right place in the chain because its + * ->virFHP->usage value increased. Reposition it towards prev + * direction. + */ +static void repos_usage_chain_elem(usage_chain_t *usageP) +{ + usage_chain_t *prev1P, *prev2P; + const unsigned int curUsage = usageP->virFHP->usage; + +dbg_fprintf(stderr, "+++ repos_usage_chain_elem(%p)\n", usageP); + +/* If this assert goes off, then it means that this routine shouldn't + * have been called. + */ +assert(curUsage > usageP->prevP->virFHP->usage); + +/* Delink : + */ +usageP->prevP->nextP = usageP->nextP; +usageP->nextP->prevP = usageP->prevP; + +/* Place usageElemP between prev1P and prev2P. + */ +for (prev1P = usageP->prevP, prev2P = prev1P->prevP; + prev2P != &oUsageChain && curUsage > prev2P->virFHP->usage; + prev1P = prev2P, prev2P = prev2P->prevP) + { + dbg_fprintf(stderr, "> prev1P %p (%d), usageElemP %p (%d), prev2P %p (%d), dummy %p\n", prev1P, prev1P->virFHP->usage, usageP, usageP->virFHP->usage, prev2P, prev2P->virFHP->usage, &oUsageChain); + assert(prev1P->virFHP->usage <= prev2P->virFHP->usage); + } + +dbg_fprintf(stderr, "prev1P %p (%d), usageElemP %p (%d), prev2P %p (%d), dummy %p\n", prev1P, prev1P->virFHP->usage, usageP, usageP->virFHP->usage, prev2P, prev2P->virFHP->usage, &oUsageChain); + +/* Relink between prev1P and prev2P : + */ +prev1P->prevP = usageP; +usageP->prevP = prev2P; +prev2P->nextP = usageP; +usageP->nextP = prev1P; + +do_sanity_check("repos_usage_chain_elem() : end"); +dbg_fprintf(stderr, "--- done, repos_usage_chain_elem()\n"); +} + + +/** + * Retrieves the RISC OS font name of given RISC OS fonthandle. + * + * \param rofhandle RISC OS font handle + */ +static const char *get_rofontname(font_f rofhandle) +{ + usage_chain_t *usageP; + + if (oUsageChain.nextP == NULL) + return NULL; + + for (usageP = oUsageChain.nextP; usageP != &oUsageChain; usageP = usageP->nextP) + if (usageP->ro_fhandle == rofhandle) + return usageP->virFHP->fontNameP; + + return NULL; +} + + +static void dump_internals(void) +{ +fprintf(stderr, "Dump UFont internals:\n - Virtual font handle array at %p (length %d, max length %d)\n - Usage chain elements %d\n - Chain timer is %d\n Dump usage chain (first dummy at %p):\n", oVirtualFHArrayP, oCurVirtualFHArrayElems, oMaxVirtualFHArrayElems, oCurUsageChainElems, oChainTimer, &oUsageChain); +if (oUsageChain.prevP == NULL || oUsageChain.nextP == NULL) + { + fprintf(stderr, " Empty usage chain\n"); + if (oUsageChain.prevP != oUsageChain.nextP) + fprintf(stderr, " *** Corrupted empty usage chain: next %p, prev %p\n", oUsageChain.nextP, oUsageChain.prevP); + if (oCurUsageChainElems != 0) + fprintf(stderr, " *** Current usage chain length is wrong\n"); + } +else + { + size_t usageCount; + const usage_chain_t *usageP; + + for (usageCount = 0, usageP = oUsageChain.nextP; usageP != &oUsageChain; ++usageCount, usageP = usageP->nextP) + { + fprintf(stderr, " -%d- : cur %p, next %p, prev %p, timer %d, RISC OS font handle %d, virtual font %p (%d, %s), usage %d\n", usageCount, usageP, usageP->nextP, usageP->prevP, usageP->chainTimer, usageP->ro_fhandle, usageP->virFHP, usageP->virFHP - oVirtualFHArrayP, usageP->virFHP->fontNameP, usageP->virFHP->usage); + if (usageP->nextP->prevP != usageP) + fprintf(stderr, " *** Bad usageP->nextP->prevP != usageP\n"); + if (usageP->prevP->nextP != usageP) + fprintf(stderr, " *** Bad usageP->prevP->nextP != usageP\n"); + if (usageP->virFHP < oVirtualFHArrayP || usageP->virFHP >= &oVirtualFHArrayP[oCurVirtualFHArrayElems]) + fprintf(stderr, " *** Bad virtual font handle\n"); + } + if (usageCount != oCurUsageChainElems) + fprintf(stderr, " *** Current usage chain length is wrong\n"); + if (usageCount > kMaxUsageChainElems) + fprintf(stderr, " *** Current usage chain is too long\n"); + } + +if (oVirtualFHArrayP != NULL) + { + size_t fhIndex; + + fprintf(stderr, " Dump virtual font handles:\n"); + for (fhIndex = 0; fhIndex < oCurVirtualFHArrayElems; ++fhIndex) + { + const virtual_fh_t *virFHP = &oVirtualFHArrayP[fhIndex]; + + fprintf(stderr, " -%d (%p)- : <%s>, size %d,%d, res %d,%d, usage %d, ref count %d, usage chain ptr %p\n", fhIndex, virFHP, virFHP->fontNameP, virFHP->xsize, virFHP->ysize, virFHP->xres, virFHP->yres, virFHP->usage, virFHP->refCount, virFHP->usageP); + if (virFHP->usageP != NULL) + { + const usage_chain_t *usageP; + + for (usageP = oUsageChain.nextP; + usageP != virFHP->usageP && usageP != &oUsageChain; + usageP = usageP->nextP) + /* no body */; + if (usageP != virFHP->usageP) + fprintf(stderr, " *** Usage chain ptr could not be found in usage chain\n"); + } + } + } +} + + +static int sanity_check(const char *testMsgP) +{ +dbg_fprintf(stderr, "Sanity check <%s>\n", testMsgP); +if (oUsageChain.prevP == NULL || oUsageChain.nextP == NULL) + { + assert(oUsageChain.prevP == oUsageChain.nextP); + assert(oCurUsageChainElems == 0); + } +else + { + size_t usageCount; + const usage_chain_t *usageP; + + /* We should see equal or decreasing usage values. + */ + for (usageCount = 0, usageP = oUsageChain.nextP; usageP != &oUsageChain; ++usageCount, usageP = usageP->nextP) + { + assert(usageP->nextP->prevP == usageP); + assert(usageP->prevP->nextP == usageP); + assert(usageP->chainTimer <= oChainTimer); + assert(usageP->ro_fhandle != 0); + assert(oVirtualFHArrayP != NULL); + assert(usageP->virFHP >= oVirtualFHArrayP && usageP->virFHP < &oVirtualFHArrayP[oCurVirtualFHArrayElems]); + assert(usageP->virFHP->usageP == usageP); +// dbg_fprintf(stderr, "%d: usageP %p (oUsageChain.prevP %p), timer %d, usage %d, next usage %d\n", usageCount, usageP, oUsageChain.prevP, usageP->chainTimer, usageP->virFHP->usage, usageP->nextP->virFHP->usage); + assert(usageP == oUsageChain.prevP || usageP->virFHP->usage >= usageP->nextP->virFHP->usage); + } + assert(usageCount == oCurUsageChainElems); + assert(usageCount <= kMaxUsageChainElems); + } + +if (oVirtualFHArrayP != NULL) + { + size_t fhIndex; + + for (fhIndex = 0; fhIndex < oCurVirtualFHArrayElems; ++fhIndex) + { + const virtual_fh_t *virFHP = &oVirtualFHArrayP[fhIndex]; + + if (virFHP->usageP != NULL) + { + const usage_chain_t *usageP; + + assert(virFHP->fontNameP != NULL); + assert(virFHP->xsize > 0 && virFHP->ysize > 0); + assert(virFHP->xres > 0 && virFHP->yres > 0); + for (usageP = oUsageChain.nextP; + usageP != virFHP->usageP && usageP != &oUsageChain; + usageP = usageP->nextP) + /* no body */; + assert(usageP == virFHP->usageP); + } + } + } + +return 0; +} |