/* ufont.c * Licensed under the GNU General Public License, * http://www.opensource.org/licenses/gpl-license * Copyright 2000 James Bursa * Copyright 2004 John Tytgat */ /** \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 #include #include #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 onwards are set using the RISC OS font with handle */ 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, 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 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 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; }