mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Add CSS3 Fonts support to the fontconfig plugin
This commit is contained in:
parent
6deb138320
commit
488b82b6ff
@ -8,7 +8,7 @@ __docformat__ = 'restructuredtext en'
|
||||
|
||||
import os, sys
|
||||
|
||||
from calibre.constants import plugins, islinux, isbsd, isosx
|
||||
from calibre.constants import plugins, islinux, isbsd, isosx, preferred_encoding
|
||||
|
||||
_fc, _fc_err = plugins['fontconfig']
|
||||
|
||||
@ -26,6 +26,33 @@ class FontConfig(Thread):
|
||||
Thread.__init__(self)
|
||||
self.daemon = True
|
||||
self.failed = False
|
||||
self.css_weight_map = {
|
||||
_fc.FC_WEIGHT_THIN : u'100',
|
||||
_fc.FC_WEIGHT_EXTRALIGHT : u'200', _fc.FC_WEIGHT_ULTRALIGHT : u'200',
|
||||
_fc.FC_WEIGHT_LIGHT : u'300',
|
||||
_fc.FC_WEIGHT_BOOK : u'normal', _fc.FC_WEIGHT_BOOK : u'normal', _fc.FC_WEIGHT_REGULAR : u'normal',
|
||||
_fc.FC_WEIGHT_MEDIUM : u'500',
|
||||
_fc.FC_WEIGHT_DEMIBOLD : u'600', _fc.FC_WEIGHT_SEMIBOLD : u'600',
|
||||
_fc.FC_WEIGHT_BOLD : u'bold',
|
||||
_fc.FC_WEIGHT_EXTRABOLD : u'800', _fc.FC_WEIGHT_ULTRABOLD : u'800',
|
||||
_fc.FC_WEIGHT_HEAVY : u'900', _fc.FC_WEIGHT_BLACK : u'900', _fc.FC_WEIGHT_EXTRABLACK : u'900', _fc.FC_WEIGHT_ULTRABLACK : u'900'
|
||||
}
|
||||
self.css_slant_map = {
|
||||
_fc.FC_SLANT_ROMAN : u'normal',
|
||||
_fc.FC_SLANT_ITALIC : u'italic',
|
||||
_fc.FC_SLANT_OBLIQUE : u'oblique'
|
||||
}
|
||||
self.css_stretch_map = {
|
||||
_fc.FC_WIDTH_ULTRACONDENSED: u'ultra-condensed',
|
||||
_fc.FC_WIDTH_EXTRACONDENSED : u'extra-condensed',
|
||||
_fc.FC_WIDTH_CONDENSED: u'condensed',
|
||||
_fc.FC_WIDTH_SEMICONDENSED: u'semi-condensed',
|
||||
_fc.FC_WIDTH_NORMAL: u'normal',
|
||||
_fc.FC_WIDTH_SEMIEXPANDED: u'semi-expanded',
|
||||
_fc.FC_WIDTH_EXPANDED: u'expanded',
|
||||
_fc.FC_WIDTH_EXTRAEXPANDED: u'extra-expanded',
|
||||
_fc.FC_WIDTH_ULTRAEXPANDED: u'ultra-expanded',
|
||||
}
|
||||
|
||||
def run(self):
|
||||
config = None
|
||||
@ -83,12 +110,13 @@ class FontConfig(Thread):
|
||||
`('normal', 'bold', 'italic', 'bi', 'light', 'li')`
|
||||
'''
|
||||
self.wait()
|
||||
if isinstance(family, unicode):
|
||||
family = family.encode('utf-8')
|
||||
if not isinstance(family, unicode):
|
||||
family = family.decode(preferred_encoding)
|
||||
fonts = {}
|
||||
ofamily = str(family).decode('utf-8')
|
||||
for fullname, path, style, nfamily, weight, slant in \
|
||||
_fc.files_for_family(str(family)):
|
||||
for entry in _fc.files_for_family(family):
|
||||
slant, weight = entry['slant'], entry['weight']
|
||||
fullname, path = entry['fullname'], entry['path']
|
||||
nfamily = entry['family']
|
||||
style = (slant, weight)
|
||||
if normalize:
|
||||
italic = slant > 0
|
||||
@ -104,7 +132,7 @@ class FontConfig(Thread):
|
||||
except UnicodeDecodeError:
|
||||
continue
|
||||
if style in fonts:
|
||||
if nfamily.lower().strip() == ofamily.lower().strip() \
|
||||
if nfamily.lower().strip() == family.lower().strip() \
|
||||
and 'Condensed' not in fullname and 'ExtraLight' not in fullname:
|
||||
fonts[style] = (path, fullname)
|
||||
else:
|
||||
@ -112,6 +140,41 @@ class FontConfig(Thread):
|
||||
|
||||
return fonts
|
||||
|
||||
def faces_for_family(self, family):
|
||||
'''
|
||||
Return all the faces in a font family in a manner suitable for CSS 3.
|
||||
'''
|
||||
self.wait()
|
||||
if not isinstance(family, unicode):
|
||||
family = family.decode(preferred_encoding)
|
||||
seen = set()
|
||||
for entry in _fc.files_for_family(family):
|
||||
slant, weight = entry['slant'], entry['weight']
|
||||
fullname, path = entry['fullname'], entry['path']
|
||||
nfamily, width = entry['family'], entry['width']
|
||||
fingerprint = (slant, weight, width, nfamily)
|
||||
if fingerprint in seen:
|
||||
# Fontconfig returns the same font multiple times if it is
|
||||
# present in multiple locations
|
||||
continue
|
||||
seen.add(fingerprint)
|
||||
|
||||
try:
|
||||
nfamily = nfamily.decode('UTF-8')
|
||||
fullname = fullname.decode('utf-8')
|
||||
path = path.decode('utf-8')
|
||||
except UnicodeDecodeError:
|
||||
continue
|
||||
|
||||
face = {
|
||||
'font-weight': self.css_weight_map[weight],
|
||||
'font-style': self.css_slant_map[slant],
|
||||
'font-stretch': self.css_stretch_map[width],
|
||||
'font-family': nfamily,
|
||||
'fullname': fullname, 'path':path
|
||||
}
|
||||
yield face
|
||||
|
||||
def match(self, name, all=False, verbose=False):
|
||||
'''
|
||||
Find the system font that most closely matches `name`, where `name` is a specification
|
||||
@ -162,7 +225,7 @@ else:
|
||||
def test():
|
||||
from pprint import pprint;
|
||||
pprint(fontconfig.find_font_families())
|
||||
pprint(fontconfig.files_for_family('liberation serif'))
|
||||
pprint(tuple(fontconfig.faces_for_family('liberation serif')))
|
||||
m = 'liberation serif'
|
||||
pprint(fontconfig.match(m+':slant=italic:weight=bold', verbose=True))
|
||||
|
||||
|
@ -141,10 +141,10 @@ fontconfig_files_for_family(PyObject *self, PyObject *args) {
|
||||
FcPattern *pat, *tp;
|
||||
FcObjectSet *oset;
|
||||
FcFontSet *fs;
|
||||
FcValue file, weight, fullname, style, slant, family2;
|
||||
PyObject *ans, *temp, *t;
|
||||
FcValue file, weight, fullname, style, slant, family2, width;
|
||||
PyObject *ans, *temp;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "s", &family))
|
||||
if (!PyArg_ParseTuple(args, "es", "UTF-8", &family))
|
||||
return NULL;
|
||||
|
||||
ans = PyList_New(0);
|
||||
@ -154,6 +154,7 @@ fontconfig_files_for_family(PyObject *self, PyObject *args) {
|
||||
|
||||
pat = FcPatternBuild(0, FC_FAMILY, FcTypeString, family, (char *) 0);
|
||||
if (pat == NULL) { fontconfig_cleanup_find(pat, oset, fs); return PyErr_NoMemory(); }
|
||||
PyMem_Free(family); family = NULL;
|
||||
|
||||
oset = FcObjectSetCreate();
|
||||
if (oset == NULL) { fontconfig_cleanup_find(pat, oset, fs); return PyErr_NoMemory(); }
|
||||
@ -161,8 +162,9 @@ fontconfig_files_for_family(PyObject *self, PyObject *args) {
|
||||
if (!FcObjectSetAdd(oset, FC_STYLE)) { fontconfig_cleanup_find(pat, oset, fs); return PyErr_NoMemory(); }
|
||||
if (!FcObjectSetAdd(oset, FC_SLANT)) { fontconfig_cleanup_find(pat, oset, fs); return PyErr_NoMemory(); }
|
||||
if (!FcObjectSetAdd(oset, FC_WEIGHT)) { fontconfig_cleanup_find(pat, oset, fs); return PyErr_NoMemory(); }
|
||||
if (!FcObjectSetAdd(oset, FC_WIDTH)) { fontconfig_cleanup_find(pat, oset, fs); return PyErr_NoMemory(); }
|
||||
if (!FcObjectSetAdd(oset, FC_FAMILY)) { fontconfig_cleanup_find(pat, oset, fs); return PyErr_NoMemory(); }
|
||||
if (!FcObjectSetAdd(oset, "fullname")) { fontconfig_cleanup_find(pat, oset, fs); return PyErr_NoMemory(); }
|
||||
if (!FcObjectSetAdd(oset, FC_FULLNAME)) { fontconfig_cleanup_find(pat, oset, fs); return PyErr_NoMemory(); }
|
||||
|
||||
fs = FcFontList(FcConfigGetCurrent(), pat, oset);
|
||||
if (fs == NULL) { fontconfig_cleanup_find(pat, oset, fs); return PyErr_NoMemory(); }
|
||||
@ -174,30 +176,21 @@ fontconfig_files_for_family(PyObject *self, PyObject *args) {
|
||||
if (FcPatternGet(tp, FC_FILE, 0, &file) != FcResultMatch) continue;
|
||||
if (FcPatternGet(tp, FC_STYLE, 0, &style) != FcResultMatch) continue;
|
||||
if (FcPatternGet(tp, FC_WEIGHT, 0, &weight) != FcResultMatch) continue;
|
||||
if (FcPatternGet(tp, FC_WIDTH, 0, &width) != FcResultMatch) continue;
|
||||
if (FcPatternGet(tp, FC_SLANT, 0, &slant) != FcResultMatch) continue;
|
||||
if (FcPatternGet(tp, FC_FAMILY, 0, &family2) != FcResultMatch) continue;
|
||||
if (FcPatternGet(tp, "fullname", 0, &fullname) != FcResultMatch) continue;
|
||||
if (FcPatternGet(tp, FC_FULLNAME, 0, &fullname) != FcResultMatch) continue;
|
||||
|
||||
temp = PyTuple_New(6);
|
||||
if(temp == NULL) { fontconfig_cleanup_find(pat, oset, fs); return PyErr_NoMemory(); }
|
||||
t = PyBytes_FromString((char *)fullname.u.s);
|
||||
if(t == NULL) { fontconfig_cleanup_find(pat, oset, fs); return PyErr_NoMemory(); }
|
||||
PyTuple_SET_ITEM(temp, 0, t);
|
||||
t = PyBytes_FromString((char *)file.u.s);
|
||||
if(t == NULL) { fontconfig_cleanup_find(pat, oset, fs); return PyErr_NoMemory(); }
|
||||
PyTuple_SET_ITEM(temp, 1, t);
|
||||
t = PyBytes_FromString((char *)style.u.s);
|
||||
if(t == NULL) { fontconfig_cleanup_find(pat, oset, fs); return PyErr_NoMemory(); }
|
||||
PyTuple_SET_ITEM(temp, 2, t);
|
||||
t = PyBytes_FromString((char *)family2.u.s);
|
||||
if(t == NULL) { fontconfig_cleanup_find(pat, oset, fs); return PyErr_NoMemory(); }
|
||||
PyTuple_SET_ITEM(temp, 3, t);
|
||||
t = PyInt_FromLong((long)weight.u.i);
|
||||
if(t == NULL) { fontconfig_cleanup_find(pat, oset, fs); return PyErr_NoMemory(); }
|
||||
PyTuple_SET_ITEM(temp, 4, t);
|
||||
t = PyInt_FromLong((long)slant.u.i);
|
||||
if(t == NULL) { fontconfig_cleanup_find(pat, oset, fs); return PyErr_NoMemory(); }
|
||||
PyTuple_SET_ITEM(temp, 5, t);
|
||||
temp = Py_BuildValue("{s:s, s:s, s:s, s:s, s:l, s:l, s:l}",
|
||||
"fullname", (char*)fullname.u.s,
|
||||
"path", (char*)file.u.s,
|
||||
"style", (char*)style.u.s,
|
||||
"family", (char*)family2.u.s,
|
||||
"weight", (long)weight.u.i,
|
||||
"slant", (long)slant.u.i,
|
||||
"width", (long)width.u.i
|
||||
);
|
||||
if (temp == NULL) { fontconfig_cleanup_find(pat, oset, fs); return NULL; }
|
||||
if (PyList_Append(ans, temp) != 0)
|
||||
{ fontconfig_cleanup_find(pat, oset, fs); return PyErr_NoMemory(); }
|
||||
}
|
||||
@ -343,5 +336,39 @@ initfontconfig(void) {
|
||||
"Find fonts."
|
||||
);
|
||||
if (m == NULL) return;
|
||||
|
||||
PyModule_AddIntMacro(m, FC_WEIGHT_THIN);
|
||||
PyModule_AddIntMacro(m, FC_WEIGHT_EXTRALIGHT);
|
||||
PyModule_AddIntMacro(m, FC_WEIGHT_ULTRALIGHT);
|
||||
PyModule_AddIntMacro(m, FC_WEIGHT_LIGHT);
|
||||
PyModule_AddIntMacro(m, FC_WEIGHT_BOOK);
|
||||
PyModule_AddIntMacro(m, FC_WEIGHT_REGULAR);
|
||||
PyModule_AddIntMacro(m, FC_WEIGHT_NORMAL);
|
||||
PyModule_AddIntMacro(m, FC_WEIGHT_MEDIUM);
|
||||
PyModule_AddIntMacro(m, FC_WEIGHT_DEMIBOLD);
|
||||
PyModule_AddIntMacro(m, FC_WEIGHT_SEMIBOLD);
|
||||
PyModule_AddIntMacro(m, FC_WEIGHT_BOLD);
|
||||
PyModule_AddIntMacro(m, FC_WEIGHT_EXTRABOLD);
|
||||
PyModule_AddIntMacro(m, FC_WEIGHT_ULTRABOLD);
|
||||
PyModule_AddIntMacro(m, FC_WEIGHT_BLACK);
|
||||
PyModule_AddIntMacro(m, FC_WEIGHT_HEAVY);
|
||||
PyModule_AddIntMacro(m, FC_WEIGHT_EXTRABLACK);
|
||||
PyModule_AddIntMacro(m, FC_WEIGHT_ULTRABLACK);
|
||||
|
||||
PyModule_AddIntMacro(m, FC_SLANT_ROMAN);
|
||||
PyModule_AddIntMacro(m, FC_SLANT_ITALIC);
|
||||
PyModule_AddIntMacro(m, FC_SLANT_OBLIQUE);
|
||||
|
||||
PyModule_AddIntMacro(m, FC_WIDTH_ULTRACONDENSED);
|
||||
PyModule_AddIntMacro(m, FC_WIDTH_EXTRACONDENSED);
|
||||
PyModule_AddIntMacro(m, FC_WIDTH_CONDENSED);
|
||||
PyModule_AddIntMacro(m, FC_WIDTH_SEMICONDENSED);
|
||||
PyModule_AddIntMacro(m, FC_WIDTH_NORMAL);
|
||||
PyModule_AddIntMacro(m, FC_WIDTH_SEMIEXPANDED);
|
||||
PyModule_AddIntMacro(m, FC_WIDTH_EXPANDED);
|
||||
PyModule_AddIntMacro(m, FC_WIDTH_EXTRAEXPANDED);
|
||||
PyModule_AddIntMacro(m, FC_WIDTH_ULTRAEXPANDED);
|
||||
|
||||
#
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user