netsurf/riscos/ufont.c

1274 lines
39 KiB
C

/* 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"
// #define DEBUG_UFONT
// #define DEBUG_ACTIVATE_SANITY_CHECK
// #define DEBUG_DUMP_INTERNALS
#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
{
ufont_map_t *mapP;
unsigned 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);
#ifdef DEBUG_DUMP_INTERNALS
static void dump_internals(void);
#endif
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)
{
unsigned int 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;
}
/**
* Convert next sequence of UTF-8 bytes into wchar_t
*
* \param pwc resulting wchar_t result.
* \param s ptr to UTF-8 encoded string
* \param n maximum of bytes which can be consumed via s
* \return
* 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 (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 the usage_chaint_t 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;
}
/***
* Should be called when the usage_chain_t element is no longer in the right
* place in the chain based on its virFHP->usage value.
*
* 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;
}
#ifdef DEBUG_DUMP_INTERNALS
/**
* Prints to stderr the complete internal state of UFont.
*/
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");
}
}
}
}
#endif
/**
* Does do a full sanity check of UFont internal datastructures.
* Will assert() when something odd if found.
*/
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);
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;
}