blob: aa08a2b89f45eb57eb2566ca839771aa8f3744d1 [file] [log] [blame] [edit]
#include "fontcache.h"
/**
* Linux/Mac:
* The strings listed below will be submitted to fontconfig, in this priority,
* with point size specified, and it will try to find something that matches. If it doesn't
* find anything, it will try the next font.
*
* If fontconfig can't find any that look like any of the fonts you specify, (unlikely with the defaults)
* you must find one that it will. Run `fc-match <test_font_name>` to test you font name, and then modify
* this array with that name string. If you'd like, run `fc-list` to list available fonts.
* Note:
* - The fc-* commands are a CLI frontend for fontconfig,
* so anything that works there will work in this graphics library.
* - The font returned by fontconfig may not be the same, but will look similar.
*
* Windows:
* Something with similar effect will be done, but it is harder to tell what will happen.
* Check your installed fonts if the program exits immediately or while changing font size.
*
*/
const char* const fontname_config[]{
"helvetica",
"lucida sans",
"schumacher"
};
/******************************************
* begin FontCache function definitions *
******************************************/
#ifdef X11
#include "graphics_state.h"
void FontCache::close_font(font_ptr font) {
XftFontClose(t_x11_state::getInstance()->display, font);
}
#elif defined WIN32
void FontCache::close_font(font_ptr font) {
free(font);
}
void WIN32_DELETE_ERROR(); //Forward declaration
#else
void FontCache::close_font(font_ptr /*font*/) {
}
#endif
/**
* Loads the font with given attributes, and puts the pointer to in in put_font_ptr_here
*/
font_ptr FontCache::do_font_loading(
#if defined X11 || defined WIN32
int pointsize, int degrees
#else
int /*pointsize*/, int /*degrees*/
#endif
) {
#if defined X11 || defined WIN32
bool success = false;
#endif
font_ptr retval = nullptr;
#ifdef X11
for (int ifont = 0; ifont < NUM_FONT_TYPES; ifont++) {
#ifdef VERBOSE
printf("Loading font: %s-%d\n", fontname_config[ifont], pointsize);
#endif
XftMatrix font_matrix;
XftMatrixInit(&font_matrix);
if (degrees != 0) {
XftMatrixRotate(&font_matrix, cos(PI * (degrees) / 180), sin(PI * degrees / 180));
}
/* Load font and get font information structure. */
retval = XftFontOpen(
t_x11_state::getInstance()->display, t_x11_state::getInstance()->screen_num,
XFT_FAMILY, XftTypeString, fontname_config[ifont],
XFT_SIZE, XftTypeDouble, static_cast<double>(pointsize),
XFT_MATRIX, XftTypeMatrix, &font_matrix,
NULL // (sentinel)
);
if (retval == nullptr) {
#ifdef VERBOSE
fprintf(stderr, "Cannot open font %s", fontname_config[ifont]);
if (degrees != 0) {
fprintf(stderr, "with rotation %f deg", degrees);
}
printf("\n");
#endif
} else {
success = true;
break;
}
}
if (success == false) {
printf("Error in load_font: fontconfig couldn't find any font of pointsize %d.\n", pointsize);
if (degrees != 0) {
printf("and rotation %d deg", degrees);
}
printf("Use `fc-list` to list available fonts, `fc-match` to test, and then modify\n");
printf("the font config array in easygl_constants.h .\n");
exit(1);
}
#elif defined WIN32
LOGFONT *lf = retval = (LOGFONT*) malloc(sizeof (LOGFONT));
ZeroMemory(lf, sizeof (LOGFONT));
// lfHeight specifies the desired height of characters in logical units.
// A positive value of lfHeight will request a font that is appropriate
// for a line spacing of lfHeight. On the other hand, setting lfHeight
// to a negative value will obtain a font height that is compatible with
// the desired pointsize.
lf->lfHeight = -pointsize;
lf->lfWeight = FW_NORMAL;
lf->lfCharSet = ANSI_CHARSET;
lf->lfOutPrecision = OUT_DEFAULT_PRECIS;
lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;
lf->lfQuality = PROOF_QUALITY;
lf->lfPitchAndFamily = VARIABLE_PITCH | FF_SWISS;
lf->lfEscapement = (LONG) degrees * 10;
lf->lfOrientation = (LONG) degrees * 10;
HFONT testfont;
// Convert lf->lfFaceName from a char_t* to a wc_chart*
// "The length of this string must not exceed 32 TCHAR values"
// Source: https://msdn.microsoft.com/en-us/library/windows/desktop/dd145037(v=vs.85).aspx
wchar_t wcFaceName[32];
std::mbstowcs(wcFaceName, lf->lfFaceName, 32);
for (int ifont = 0; ifont < NUM_FONT_TYPES; ++ifont) {
MultiByteToWideChar(CP_UTF8, 0, fontname_config[ifont], -1,
wcFaceName, sizeof (lf->lfFaceName) / sizeof (wchar_t));
testfont = CreateFontIndirect(lf);
if (testfont == NULL) {
#ifdef VERBOSE
fprintf(stderr, "Couldn't open font %s in pointsize %d.\n",
fontname_config[ifont], pointsize);
#endif
continue;
}
if (DeleteObject(testfont) == 0) {
WIN32_DELETE_ERROR();
} else {
success = true;
break;
}
}
if (success == false) {
printf("Error in load_font: Windows couldn't find any font of pointsize %d.\n", pointsize);
printf("check installed fonts, and then modify\n");
printf("the font config array in easygl_constants.h .\n");
exit(1);
}
#endif
return retval;
}
void FontCache::clear() {
for (
auto iter = descriptor2font_zeros.begin();
iter != descriptor2font_zeros.end();
++iter
) {
close_font(iter->second);
}
order_zeros.clear();
descriptor2font_zeros.clear();
for (
auto iter = descriptor2font_rotated.begin();
iter != descriptor2font_rotated.end();
++iter
) {
close_font(iter->second);
}
order_rotated.clear();
descriptor2font_rotated.clear();
}
font_ptr FontCache::get_font_info(size_t pointsize, int degrees) {
if (degrees == 0) {
return get_font_info(
pointsize, degrees,
order_zeros, descriptor2font_zeros, FONT_CACHE_SIZE_FOR_ZEROS
);
} else {
return get_font_info(
pointsize, degrees,
order_rotated, descriptor2font_rotated, FONT_CACHE_SIZE_FOR_ROTATED
);
}
}
template<class queue_type, class map_type>
font_ptr FontCache::get_font_info(
size_t pointsize, int degrees,
queue_type& orderqueue, map_type& descr2font_map, size_t max_size) {
auto search_result = descr2font_map.find(std::make_pair(pointsize, degrees));
if (search_result == descr2font_map.end()) {
if (orderqueue.size() + 1 > max_size) {
// if too many fonts, remove the oldest font from the cache.
font_descriptor fontdesc_to_remove = orderqueue.back();
auto font_to_remove = descr2font_map.find(fontdesc_to_remove);
if (font_to_remove != descr2font_map.end()) {
close_font(font_to_remove->second);
}
descr2font_map.erase(font_to_remove);
orderqueue.pop_back();
puts("font cache overflow");
}
font_ptr new_font = do_font_loading(pointsize, degrees);
font_descriptor new_font_desc = std::make_pair(pointsize, degrees);
orderqueue.push_front(new_font_desc);
descr2font_map.insert(std::make_pair(new_font_desc, new_font));
return new_font;
} else {
return search_result->second;
}
}