Linux: Use libfontconfig directly instead of calling fc-match
There are apparently some linux systems out there with versions of fontconfig that do not understanf the :charset query when passed to fc-match. See #46
This commit is contained in:
parent
882a4f2ab3
commit
02ef3c6dc8
@ -12,7 +12,7 @@ matrix:
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- libfreetype6-dev
|
||||
- libfontconfig1-dev
|
||||
- libglew-dev
|
||||
- libxi-dev
|
||||
- libxrandr-dev
|
||||
@ -30,7 +30,7 @@ matrix:
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- libfreetype6-dev
|
||||
- libfontconfig1-dev
|
||||
- libglew-dev
|
||||
- libxi-dev
|
||||
- libxrandr-dev
|
||||
|
||||
@ -40,6 +40,7 @@ static PyMethodDef module_methods[] = {
|
||||
{"read_bytes_dump", (PyCFunction)read_bytes_dump, METH_VARARGS, ""},
|
||||
{"wcwidth", (PyCFunction)wcwidth_wrap, METH_O, ""},
|
||||
{"change_wcwidth", (PyCFunction)change_wcwidth_wrap, METH_O, ""},
|
||||
{"get_fontconfig_font", (PyCFunction)get_fontconfig_font, METH_VARARGS, ""},
|
||||
GLFW_FUNC_WRAPPERS
|
||||
{NULL, NULL, 0, NULL} /* Sentinel */
|
||||
};
|
||||
@ -77,6 +78,7 @@ PyInit_fast_data_types(void) {
|
||||
#else
|
||||
if (!init_Face(m)) return NULL;
|
||||
if (!init_freetype_library(m)) return NULL;
|
||||
if (!init_fontconfig_library(m)) return NULL;
|
||||
#endif
|
||||
PyModule_AddIntConstant(m, "BOLD", BOLD_SHIFT);
|
||||
PyModule_AddIntConstant(m, "ITALIC", ITALIC_SHIFT);
|
||||
|
||||
@ -415,3 +415,5 @@ DECLARE_CH_SCREEN_HANDLER(linefeed)
|
||||
DECLARE_CH_SCREEN_HANDLER(carriage_return)
|
||||
|
||||
bool init_freetype_library(PyObject*);
|
||||
bool init_fontconfig_library(PyObject*);
|
||||
PyObject *get_fontconfig_font(PyObject *self, PyObject *args);
|
||||
|
||||
84
kitty/fontconfig.c
Normal file
84
kitty/fontconfig.c
Normal file
@ -0,0 +1,84 @@
|
||||
/*
|
||||
* fontconfig.c
|
||||
* Copyright (C) 2017 Kovid Goyal <kovid at kovidgoyal.net>
|
||||
*
|
||||
* Distributed under terms of the GPL3 license.
|
||||
*/
|
||||
|
||||
#include "data-types.h"
|
||||
#include <fontconfig/fontconfig.h>
|
||||
|
||||
PyObject*
|
||||
get_fontconfig_font(PyObject UNUSED *self, PyObject *args) {
|
||||
char *family;
|
||||
int bold, italic, allow_bitmapped_fonts, index = 0, hint_style=0, weight=0, slant=0;
|
||||
double size_in_pts, dpi;
|
||||
unsigned int character;
|
||||
FcBool hinting, scalable, outline;
|
||||
FcChar8 *path = NULL;
|
||||
FcPattern *pat = NULL, *match = NULL;
|
||||
FcResult result;
|
||||
FcCharSet *charset = NULL;
|
||||
PyObject *ans = NULL;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "spppdId", &family, &bold, &italic, &allow_bitmapped_fonts, &size_in_pts, &character, &dpi)) return NULL;
|
||||
pat = FcPatternCreate();
|
||||
if (pat == NULL) return PyErr_NoMemory();
|
||||
|
||||
#define AP(func, which, in, desc) if (!func(pat, which, in)) { PyErr_Format(PyExc_RuntimeError, "Failed to add %s to fontconfig patter", desc, NULL); goto end; }
|
||||
AP(FcPatternAddString, FC_FAMILY, (const FcChar8*)family, "family");
|
||||
if (!allow_bitmapped_fonts) {
|
||||
AP(FcPatternAddBool, FC_OUTLINE, true, "outline");
|
||||
AP(FcPatternAddBool, FC_SCALABLE, true, "scalable");
|
||||
}
|
||||
if (size_in_pts > 0) { AP(FcPatternAddDouble, FC_SIZE, size_in_pts, "size"); }
|
||||
if (dpi > 0) { AP(FcPatternAddDouble, FC_DPI, dpi, "dpi"); }
|
||||
if (bold) { AP(FcPatternAddInteger, FC_WEIGHT, FC_WEIGHT_BOLD, "weight"); }
|
||||
if (italic) { AP(FcPatternAddInteger, FC_SLANT, FC_SLANT_ITALIC, "slant"); }
|
||||
if (character > 0) {
|
||||
charset = FcCharSetCreate();
|
||||
if (charset == NULL) { PyErr_NoMemory(); goto end; }
|
||||
if (!FcCharSetAddChar(charset, character)) { PyErr_SetString(PyExc_RuntimeError, "Failed to add character to fontconfig charset"); goto end; }
|
||||
AP(FcPatternAddCharSet, FC_CHARSET, charset, "charset");
|
||||
}
|
||||
#undef AP
|
||||
FcConfigSubstitute(NULL, pat, FcMatchPattern);
|
||||
FcDefaultSubstitute(pat);
|
||||
match = FcFontMatch(NULL, pat, &result);
|
||||
if (match == NULL) { PyErr_SetString(PyExc_KeyError, "FcFontMatch() failed"); goto end; }
|
||||
|
||||
#define GI(func, which, out, desc) \
|
||||
if (func(match, which, 0, & out) != FcResultMatch) { \
|
||||
PyErr_Format(PyExc_RuntimeError, "Failed to get %s from match object", desc, NULL); goto end; \
|
||||
}
|
||||
|
||||
GI(FcPatternGetString, FC_FILE, path, "file path");
|
||||
GI(FcPatternGetInteger, FC_INDEX, index, "face index");
|
||||
GI(FcPatternGetInteger, FC_WEIGHT, weight, "weight");
|
||||
GI(FcPatternGetInteger, FC_SLANT, slant, "slant");
|
||||
GI(FcPatternGetInteger, FC_HINT_STYLE, hint_style, "hint style");
|
||||
GI(FcPatternGetBool, FC_HINTING, hinting, "hinting");
|
||||
GI(FcPatternGetBool, FC_SCALABLE, scalable, "scalable");
|
||||
GI(FcPatternGetBool, FC_OUTLINE, outline, "outline");
|
||||
#undef GI
|
||||
|
||||
#define BP(x) (x ? Py_True : Py_False)
|
||||
ans = Py_BuildValue("siiOOOii", path, index, hint_style, BP(hinting), BP(scalable), BP(outline), weight, slant);
|
||||
#undef BP
|
||||
|
||||
end:
|
||||
if (pat != NULL) FcPatternDestroy(pat);
|
||||
if (match != NULL) FcPatternDestroy(match);
|
||||
if (charset != NULL) FcCharSetDestroy(charset);
|
||||
if (PyErr_Occurred()) return NULL;
|
||||
return ans;
|
||||
}
|
||||
|
||||
bool
|
||||
init_fontconfig_library(PyObject UNUSED *m) {
|
||||
if (!FcInit()) {
|
||||
PyErr_SetString(PyExc_RuntimeError, "Failed to initialize the fontconfig library");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -7,7 +7,7 @@ import re
|
||||
import subprocess
|
||||
from collections import namedtuple
|
||||
|
||||
from kitty.fast_data_types import Face
|
||||
from kitty.fast_data_types import Face, get_fontconfig_font
|
||||
|
||||
|
||||
def escape_family_name(name):
|
||||
@ -15,7 +15,8 @@ def escape_family_name(name):
|
||||
|
||||
|
||||
Font = namedtuple(
|
||||
'Font', 'face hinting hintstyle bold italic scalable outline weight slant index'
|
||||
'Font',
|
||||
'face hinting hintstyle bold italic scalable outline weight slant index'
|
||||
)
|
||||
|
||||
|
||||
@ -27,10 +28,10 @@ def to_bool(x):
|
||||
return x.lower() == 'true'
|
||||
|
||||
|
||||
def get_font(
|
||||
family,
|
||||
bold,
|
||||
italic,
|
||||
def get_font_subprocess(
|
||||
family='monospace',
|
||||
bold=False,
|
||||
italic=False,
|
||||
allow_bitmaped_fonts=False,
|
||||
size_in_pts=None,
|
||||
character=None,
|
||||
@ -63,10 +64,34 @@ def get_font(
|
||||
path,
|
||||
to_bool(hinting),
|
||||
int(hintstyle), bold, italic,
|
||||
to_bool(scalable), to_bool(outline), int(weight), int(slant), int(index)
|
||||
to_bool(scalable),
|
||||
to_bool(outline), int(weight), int(slant), int(index)
|
||||
)
|
||||
|
||||
|
||||
def get_font_lib(
|
||||
family='monospace',
|
||||
bold=False,
|
||||
italic=False,
|
||||
allow_bitmaped_fonts=False,
|
||||
size_in_pts=None,
|
||||
character=None,
|
||||
dpi=None
|
||||
):
|
||||
path, index, hintstyle, hinting, scalable, outline, weight, slant = get_fontconfig_font(
|
||||
family, bold, italic, allow_bitmaped_fonts, size_in_pts or 0,
|
||||
0 if character is None else ord(character[0]), dpi or 0
|
||||
)
|
||||
|
||||
return Font(
|
||||
path, hinting, hintstyle, bold, italic, scalable, outline, weight,
|
||||
slant, index
|
||||
)
|
||||
|
||||
|
||||
get_font = get_font_lib
|
||||
|
||||
|
||||
def find_font_for_character(
|
||||
family,
|
||||
char,
|
||||
|
||||
6
setup.py
6
setup.py
@ -106,8 +106,8 @@ def init_env(debug=False, asan=False):
|
||||
if isosx:
|
||||
font_libs = ['-framework', 'CoreText', '-framework', 'CoreGraphics']
|
||||
else:
|
||||
cflags.extend(pkg_config('freetype2', '--cflags-only-I'))
|
||||
font_libs = pkg_config('freetype2', '--libs')
|
||||
cflags.extend(pkg_config('fontconfig', '--cflags-only-I'))
|
||||
font_libs = pkg_config('fontconfig', '--libs')
|
||||
cflags.extend(pkg_config('glfw3', '--cflags-only-I'))
|
||||
ldflags.append('-shared')
|
||||
pylib = get_python_flags(cflags)
|
||||
@ -190,7 +190,7 @@ def option_parser():
|
||||
def find_c_files():
|
||||
ans, headers = [], []
|
||||
d = os.path.join(base, 'kitty')
|
||||
exclude = {'freetype.c'} if isosx else {'core_text.m'}
|
||||
exclude = {'freetype.c', 'fontconfig.c'} if isosx else {'core_text.m'}
|
||||
for x in os.listdir(d):
|
||||
ext = os.path.splitext(x)[1]
|
||||
if ext in ('.c', '.m') and os.path.basename(x) not in exclude:
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user