iup-stack/iup/srcglcontrols/iup_glfont.c

718 lines
17 KiB
C
Raw Permalink Normal View History

2023-02-20 16:44:45 +00:00
/** \file
* \brief GL Font functions.
*
* See Copyright Notice in "iup.h"
*/
#ifdef WIN32
#include <windows.h>
#else
#include <iconv.h>
#endif
#include <FTGL/ftgl.h>
#include <stdio.h>
#include <stdlib.h>
#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 <fontconfig/fontconfig.h>
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
}