/** \file * \brief GL Font functions. * * See Copyright Notice in "iup.h" */ #ifdef WIN32 #include #else #include #endif #include #include #include #include "iup.h" #include "iupgl.h" #include "iup_assert.h" #include "iup_object.h" #include "iup_attrib.h" #include "iup_str.h" #include "iup_array.h" #include "iup_drvfont.h" #include "iup_glcontrols.h" #include "iup_glfont.h" typedef struct _IglFont { char filename[10240]; int size; FTGLfont* ftgl_font; int charwidth, charheight; } IglFont; static char* gl_utf8_buffer = NULL; static int gl_utf8_buffer_len = 0; #ifdef WIN32 typedef char regName[MAX_PATH]; static regName* win_fonts = NULL; static int win_fonts_count = 0; static int iCompareStr(const void *a, const void *b) { const regName*aa = a; const regName*bb = b; return strcmp(*aa, *bb); } static void winInitFontNames(void) { const CHAR* subkey = "Software\\Microsoft\\Windows NT\\CurrentVersion\\Fonts"; HKEY hkey = NULL; DWORD dwIndex = 0; LONG ret; DWORD dwValuesCount, dwValueSize; ret = RegOpenKeyExA(HKEY_LOCAL_MACHINE, subkey, 0, KEY_READ, &hkey); if (ret != ERROR_SUCCESS) return; ret = RegQueryInfoKey(hkey, NULL, NULL, NULL, NULL, NULL, NULL, &dwValuesCount, NULL, NULL, NULL, NULL); if (ret != ERROR_SUCCESS) return; win_fonts_count = (int)dwValuesCount; win_fonts = malloc(sizeof(regName) * win_fonts_count); for (dwIndex = 0; dwIndex < dwValuesCount; dwIndex++) { dwValueSize = MAX_PATH - 1; ret = RegEnumValueA(hkey, dwIndex, win_fonts[dwIndex], &dwValueSize, NULL, NULL, NULL, NULL); if (ret != ERROR_SUCCESS) { win_fonts_count = (int)dwIndex; break; } } qsort(win_fonts, win_fonts_count, sizeof(regName), iCompareStr); RegCloseKey(hkey); } static int winReadStringKey(HKEY base_key, const char* subkey, const char* value_name, char* value) { HKEY key; DWORD max_size = 1024; if (RegOpenKeyExA(base_key, subkey, 0, KEY_READ, &key) != ERROR_SUCCESS) return 0; if (RegQueryValueExA(key, value_name, NULL, NULL, (LPBYTE)value, &max_size) != ERROR_SUCCESS) { RegCloseKey(key); return 0; } RegCloseKey(key); return 1; } static char* winGetFontDir(void) { static char font_dir[1024]; if (!winReadStringKey(HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders", "Fonts", font_dir)) return ""; else return font_dir; } static int iGLGetFontFilenameFromSystem(const char *font_name, int is_bold, int is_italic, char* fileName) { CHAR szData[1024]; const CHAR* subkey = "Software\\Microsoft\\Windows NT\\CurrentVersion\\Fonts"; char localFontName[MAX_PATH]; int i; if (iupStrEqualNoCase(font_name, "Courier") || iupStrEqualNoCase(font_name, "Monospace")) font_name = "Courier New"; else if (iupStrEqualNoCase(font_name, "Times") || iupStrEqualNoCase(font_name, "Serif")) font_name = "Times New Roman"; else if (iupStrEqualNoCase(font_name, "Helvetica") || iupStrEqualNoCase(font_name, "Sans")) font_name = "Arial"; strcpy(localFontName, font_name); if (is_bold) strcat(localFontName, " Bold"); if (is_italic) strcat(localFontName, " Italic"); for (i = 0; i < win_fonts_count; i++) { if (iupStrEqualNoCasePartial(win_fonts[i], localFontName)) { if (winReadStringKey(HKEY_LOCAL_MACHINE, subkey, win_fonts[i], szData)) { /* szData already includes file extension */ sprintf(fileName, "%s\\%s", winGetFontDir(), szData); return 1; } } } return 0; } #else #ifndef NO_FONTCONFIG #include static int iGLGetFontFilenameFromSystem(const char *font_name, int is_bold, int is_italic, char* fileName) { char styles[4][20]; int style_size; FcObjectSet *os = 0; FcFontSet *fs; FcPattern *pat; int bResult = 0; /* Use "fc-match monospace|sans|serif" in Lunux to find a match */ if (iupStrEqualNoCase(font_name, "Courier") || iupStrEqualNoCase(font_name, "Courier New") || iupStrEqualNoCase(font_name, "Monospace")) font_name = "DejaVu Sans Mono"; else if (iupStrEqualNoCase(font_name, "Times") || iupStrEqualNoCase(font_name, "Times New Roman") || iupStrEqualNoCase(font_name, "Serif")) font_name = "DejaVu Serif"; else if (iupStrEqualNoCase(font_name, "Helvetica") || iupStrEqualNoCase(font_name, "Arial") || iupStrEqualNoCase(font_name, "Sans")) font_name = "DejaVu Sans"; if (is_bold && is_italic) { strcpy(styles[0], "BoldItalic"); strcpy(styles[1], "Bold Italic"); strcpy(styles[2], "Bold Oblique"); strcpy(styles[3], "BoldOblique"); style_size = 4; } else if (is_bold) { strcpy(styles[0], "Bold"); style_size = 1; } else if (is_italic) { strcpy(styles[0], "Italic"); strcpy(styles[1], "Oblique"); style_size = 2; } else { strcpy(styles[0], "Regular"); strcpy(styles[1], "Normal"); strcpy(styles[2], "Medium"); strcpy(styles[3], "Book"); style_size = 4; } pat = FcPatternCreate(); os = FcObjectSetBuild(FC_FAMILY, FC_FILE, FC_STYLE, NULL); fs = FcFontList(NULL, pat, os); if (pat) FcPatternDestroy(pat); if (fs) { int j, s; for (j = 0; j < fs->nfont; j++) { FcChar8 *family; FcPatternGetString(fs->fonts[j], FC_FAMILY, 0, &family); if (iupStrEqualNoCase((char*)family, font_name)) { FcChar8 *file; FcChar8 *style; FcPatternGetString(fs->fonts[j], FC_FILE, 0, &file); FcPatternGetString(fs->fonts[j], FC_STYLE, 0, &style); for (s = 0; s < style_size; s++) { if (iupStrEqualNoCase(styles[s], (char*)style)) { strcpy(fileName, (char*)file); FcFontSetDestroy(fs); return 1; } } /* set a value to be used if the style is not found. the last family found will be used */ strcpy(fileName, (char*)file); bResult = 1; } } FcFontSetDestroy(fs); } return bResult; } #else static int iGLGetFontFilenameFromSystem(const char *font_name, int is_bold, int is_italic, char* fileName) { (void)font_name; (void)is_bold; (void)is_italic; (void)fileName; return 0; } #endif #endif static int iGLGetFontFilenameFromTypeface(const char* path, const char* typeface, const char* ext, char* filename) { FILE *file; /* current directory */ sprintf(filename, "%s.%s", typeface, ext); file = fopen(filename, "r"); if (!file) { /* path from the environment */ if (path) { sprintf(filename, "%s/%s.%s", path, typeface, ext); file = fopen(filename, "r"); } if (!file) { #ifdef WIN32 /* Windows Font folder */ sprintf(filename, "%s\\%s.%s", winGetFontDir(), typeface, ext); file = fopen(filename, "r"); if (!file) return 0; #else return 0; #endif } } fclose(file); return 1; } static char* iGLGetFontNameTex(const char* typeface) { if (iupStrEqualNoCase(typeface, "sans") || iupStrEqualNoCase(typeface, "helvetica") || iupStrEqualNoCase(typeface, "arial")) return "heros"; if (iupStrEqualNoCase(typeface, "monospace") || iupStrEqualNoCase(typeface, "courier") || iupStrEqualNoCase(typeface, "courier new")) return "cursor"; if (iupStrEqualNoCase(typeface, "serif") || iupStrEqualNoCase(typeface, "times") || iupStrEqualNoCase(typeface, "times new roman")) return "termes"; return NULL; } static char* iGLGetFontNameTTF(const char* typeface) { if (iupStrEqualNoCase(typeface, "sans") || iupStrEqualNoCase(typeface, "helvetica") || iupStrEqualNoCase(typeface, "arial")) return "arial"; if (iupStrEqualNoCase(typeface, "monospace") || iupStrEqualNoCase(typeface, "courier") || iupStrEqualNoCase(typeface, "courier new")) return "cour"; if (iupStrEqualNoCase(typeface, "serif") || iupStrEqualNoCase(typeface, "times") || iupStrEqualNoCase(typeface, "times new roman")) return "times"; return NULL; } static int iGLGetFontFilenameFromNamesOTF(const char* path, const char* typeface, char* filename, int is_bold, int is_italic) { char fontname[100]; char* face = iGLGetFontNameTex(typeface); if (face) sprintf(fontname, "texgyre%s-", face); else sprintf(fontname, "texgyre%s-", typeface); if (is_bold && is_italic) strcat(fontname, "bolditalic"); else if (is_italic) strcat(fontname, "italic"); else if (is_bold) strcat(fontname, "bold"); else strcat(fontname, "regular"); return iGLGetFontFilenameFromTypeface(path, fontname, "otf", filename); } static int iGLGetFontFilenameFromNamesTTF(const char* path, const char* typeface, char* filename, int is_bold, int is_italic) { char fontname[100]; char* face = iGLGetFontNameTTF(typeface); if (face) strcpy(fontname, face); else strcpy(fontname, typeface); if (is_bold && is_italic) strcat(fontname, "bi"); else if (is_italic) strcat(fontname, "i"); else if (is_bold) strcat(fontname, "bd"); return iGLGetFontFilenameFromTypeface(path, fontname, "ttf", filename); } static void iGLGetFontFilename(char* filename, const char *typeface, int is_bold, int is_italic) { char *path = getenv("FREETYPEFONTS_DIR"); if (!path) path = IupGetGlobal("FREETYPEFONTS_DIR"); /* search for the font in the system */ /* "Helvetica" "Courier" "Times" "Segoe UI" "Tahoma" etc */ if (iGLGetFontFilenameFromSystem(typeface, is_bold, is_italic, filename)) return; /* try typeface as a file title, compose with path to get a filename assume style already in the typeface */ /* "ariali" "courbd" "texgyrecursor-bold" */ if (iGLGetFontFilenameFromTypeface(path, typeface, "ttf", filename)) return; if (iGLGetFontFilenameFromTypeface(path, typeface, "otf", filename)) return; /* check for the pre-defined names, and use style to compose the filename */ /* "cursor" */ if (iGLGetFontFilenameFromNamesTTF(path, typeface, filename, is_bold, is_italic)) return; if (iGLGetFontFilenameFromNamesOTF(path, typeface, filename, is_bold, is_italic)) return; /* try the typeface as file name */ strcpy(filename, typeface); } static int iGLFontGetFontAveWidth(FTGLfont* ftgl_font); static IglFont* iGLFindFont(Ihandle* ih, Ihandle* gl_parent, const char *font) { char filename[10240]; char typeface[50] = ""; FTGLfont* ftgl_font; int size = 8; int is_bold = 0, is_italic = 0, is_underline = 0, is_strikeout = 0; int i, count; IglFont* fonts; double res = IupGetDouble(NULL, "SCREENDPI"); Iarray* gl_fonts = (Iarray*)iupAttribGet(gl_parent, "GL_FONTLIST"); if (!gl_fonts) { gl_fonts = iupArrayCreate(50, sizeof(IglFont)); iupAttribSet(gl_parent, "GL_FONTLIST", (char*)gl_fonts); } if (!iupGetFontInfo(font, typeface, &size, &is_bold, &is_italic, &is_underline, &is_strikeout)) return NULL; if (is_underline) iupAttribSet(ih, "UNDERLINE", "1"); else iupAttribSet(ih, "UNDERLINE", NULL); iGLGetFontFilename(filename, typeface, is_bold, is_italic); if (size < 0) size = iupRound((-size*72.0) / res); /* convert to points */ /* Check if the filename already exists in cache */ fonts = (IglFont*)iupArrayGetData(gl_fonts); count = iupArrayCount(gl_fonts); for (i = 0; i < count; i++) { if (iupStrEqualNoCase(filename, fonts[i].filename) && fonts[i].size == size) return &fonts[i]; } ftgl_font = ftglCreateTextureFont(filename); if (!ftgl_font) return NULL; ftglSetFontFaceSize(ftgl_font, size, (int)res); /* create room in the array */ fonts = (IglFont*)iupArrayInc(gl_fonts); strcpy(fonts[i].filename, filename); fonts[i].ftgl_font = ftgl_font; fonts[i].size = size; /* NOTICE that this is different from CD, here we need average width, there is maximum width. */ fonts[i].charwidth = iGLFontGetFontAveWidth(ftgl_font); fonts[i].charheight = iupRound(ftglGetFontLineHeight(ftgl_font)); return &fonts[i]; } static IglFont* iGLFontCreateNativeFont(Ihandle *ih, const char* value) { IglFont* glfont; Ihandle* gl_parent = (Ihandle*)iupAttribGet(ih, "_IUP_GLCANVAS_PARENT"); IupGLMakeCurrent(gl_parent); glfont = iGLFindFont(ih, gl_parent, value); if (!glfont) { iupERROR1("Failed to create Font: %s", value); return NULL; } iupAttribSet(ih, "_IUP_GLFONT", (char*)glfont); return glfont; } static IglFont* iGLFontGet(Ihandle *ih) { IglFont* glfont = (IglFont*)iupAttribGet(ih, "_IUP_GLFONT"); if (!glfont) glfont = iGLFontCreateNativeFont(ih, IupGetAttribute(ih, "FONT")); return glfont; } IUP_SDK_API char* iupStrConvertToUTF8(const char* str, int len, char* utf8_buffer, int *utf8_buffer_max, int utf8mode); static void iGLFontConvertToUTF8(const char* str, int len) { /* FTGL multibyte strings are always UTF-8 */ int utf8mode = IupGetInt(NULL, "UTF8MODE"); gl_utf8_buffer = iupStrConvertToUTF8(str, len, gl_utf8_buffer, &gl_utf8_buffer_len, utf8mode); } static int iGLFontGetFontAveWidth(FTGLfont* ftgl_font) { static int first = 1; static char sample[512] = ""; if (first) { int utf8mode = IupGetInt(NULL, "UTF8MODE"); /* use all Latin-1 characters */ int i; for (i = 32; i < 256; i++) sample[i - 32] = (char)i; if (utf8mode) IupSetGlobal("UTF8MODE", "No"); iGLFontConvertToUTF8(sample, 256 - 32); if (utf8mode) IupSetGlobal("UTF8MODE", "Yes"); i = (int)strlen(gl_utf8_buffer); memcpy(sample, gl_utf8_buffer, i + 1); first = 0; } return iupRound(ftglGetFontAdvance(ftgl_font, sample) / (256.0f - 32.0f)); } int iupGLFontGetStringWidth(Ihandle* ih, const char* str, int len) { IglFont* glfont; if (!ih->handle) return 0; glfont = iGLFontGet(ih); if (!glfont) return 0; iGLFontConvertToUTF8(str, len); return iupRound(ftglGetFontAdvance(glfont->ftgl_font, gl_utf8_buffer)); } void iupGLFontGetMultiLineStringSize(Ihandle* ih, const char* str, int *w, int *h) { int max_w = 0, line_count = 1; IglFont* glfont; if (!ih->handle) { iupdrvFontGetMultiLineStringSize(ih, str, w, h); return; } glfont = iGLFontGet(ih); if (!glfont) { if (w) *w = 0; if (h) *h = 0; return; } if (!str) { if (w) *w = 0; if (h) *h = glfont->charheight * 1; return; } if (str[0] && w) { int size; int len; const char *nextstr; const char *curstr = str; do { nextstr = iupStrNextLine(curstr, &len); if (len) { iGLFontConvertToUTF8(curstr, len); size = iupRound(ftglGetFontAdvance(glfont->ftgl_font, gl_utf8_buffer)); max_w = iupMAX(max_w, size); } if (*nextstr) line_count++; curstr = nextstr; } while (*nextstr); } if (w) *w = max_w; if (h) *h = glfont->charheight * line_count; } int iupGLFontSetFontAttrib(Ihandle* ih, const char* value) { IglFont* glfont; if (!ih->handle) return iupdrvSetFontAttrib(ih, value); if (!iupdrvSetFontAttrib(ih, value)) return 0; glfont = iGLFontCreateNativeFont(ih, value); if (glfont) { /* If FONT is changed, must update the SIZE attribute */ iupBaseUpdateAttribFromFont(ih); return 1; } return 0; } void iupGLFontGetCharSize(Ihandle* ih, int *charwidth, int *charheight) { IglFont* glfont; if (!ih->handle) { iupdrvFontGetCharSize(ih, charwidth, charheight); return; } glfont = iGLFontGet(ih); if (!glfont) { if (charwidth) *charwidth = 0; if (charheight) *charheight = 0; return; } if (charwidth) *charwidth = glfont->charwidth; if (charheight) *charheight = glfont->charheight; } void iupGLFontGetDim(Ihandle* ih, int *maxwidth, int *height, int *ascent, int *descent) { IglFont* glfont; if (!ih->handle) return; glfont = iGLFontGet(ih); if (!glfont) return; if (maxwidth) *maxwidth = glfont->charwidth; if (height) *height = glfont->charheight; if (ascent) *ascent = iupRound(ftglGetFontAscender(glfont->ftgl_font)); if (descent) *descent = iupRound(-ftglGetFontDescender(glfont->ftgl_font)); } void iupGLFontRenderString(Ihandle* ih, const char* str, int len) { IglFont* glfont; if (!ih->handle) return; glfont = iGLFontGet(ih); if (!glfont) return; iGLFontConvertToUTF8(str, len); /* render always at baseline */ ftglRenderFont(glfont->ftgl_font, gl_utf8_buffer, FTGL_RENDER_ALL); } void iupGLFontInit(void) { #ifdef WIN32 if (!win_fonts) winInitFontNames(); #endif } void iupGLFontRelease(Ihandle* gl_parent) { Iarray* gl_fonts = (Iarray*)iupAttribGet(gl_parent, "GL_FONTLIST"); if (gl_fonts) { int i, count = iupArrayCount(gl_fonts); IglFont* fonts = (IglFont*)iupArrayGetData(gl_fonts); for (i = 0; i < count; i++) { ftglDestroyFont(fonts[i].ftgl_font); fonts[i].ftgl_font = NULL; } iupArrayDestroy(gl_fonts); iupAttribSet(gl_parent, "GL_FONTLIST", NULL); } } void iupGLFontFinish(void) { if (gl_utf8_buffer) { free(gl_utf8_buffer); gl_utf8_buffer = NULL; gl_utf8_buffer_len = 0; } #ifdef WIN32 if (win_fonts) free(win_fonts); win_fonts = NULL; win_fonts_count = 0; #endif }