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
|
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']
|
_fc, _fc_err = plugins['fontconfig']
|
||||||
|
|
||||||
@ -26,6 +26,33 @@ class FontConfig(Thread):
|
|||||||
Thread.__init__(self)
|
Thread.__init__(self)
|
||||||
self.daemon = True
|
self.daemon = True
|
||||||
self.failed = False
|
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):
|
def run(self):
|
||||||
config = None
|
config = None
|
||||||
@ -83,12 +110,13 @@ class FontConfig(Thread):
|
|||||||
`('normal', 'bold', 'italic', 'bi', 'light', 'li')`
|
`('normal', 'bold', 'italic', 'bi', 'light', 'li')`
|
||||||
'''
|
'''
|
||||||
self.wait()
|
self.wait()
|
||||||
if isinstance(family, unicode):
|
if not isinstance(family, unicode):
|
||||||
family = family.encode('utf-8')
|
family = family.decode(preferred_encoding)
|
||||||
fonts = {}
|
fonts = {}
|
||||||
ofamily = str(family).decode('utf-8')
|
for entry in _fc.files_for_family(family):
|
||||||
for fullname, path, style, nfamily, weight, slant in \
|
slant, weight = entry['slant'], entry['weight']
|
||||||
_fc.files_for_family(str(family)):
|
fullname, path = entry['fullname'], entry['path']
|
||||||
|
nfamily = entry['family']
|
||||||
style = (slant, weight)
|
style = (slant, weight)
|
||||||
if normalize:
|
if normalize:
|
||||||
italic = slant > 0
|
italic = slant > 0
|
||||||
@ -104,7 +132,7 @@ class FontConfig(Thread):
|
|||||||
except UnicodeDecodeError:
|
except UnicodeDecodeError:
|
||||||
continue
|
continue
|
||||||
if style in fonts:
|
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:
|
and 'Condensed' not in fullname and 'ExtraLight' not in fullname:
|
||||||
fonts[style] = (path, fullname)
|
fonts[style] = (path, fullname)
|
||||||
else:
|
else:
|
||||||
@ -112,6 +140,41 @@ class FontConfig(Thread):
|
|||||||
|
|
||||||
return fonts
|
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):
|
def match(self, name, all=False, verbose=False):
|
||||||
'''
|
'''
|
||||||
Find the system font that most closely matches `name`, where `name` is a specification
|
Find the system font that most closely matches `name`, where `name` is a specification
|
||||||
@ -162,7 +225,7 @@ else:
|
|||||||
def test():
|
def test():
|
||||||
from pprint import pprint;
|
from pprint import pprint;
|
||||||
pprint(fontconfig.find_font_families())
|
pprint(fontconfig.find_font_families())
|
||||||
pprint(fontconfig.files_for_family('liberation serif'))
|
pprint(tuple(fontconfig.faces_for_family('liberation serif')))
|
||||||
m = 'liberation serif'
|
m = 'liberation serif'
|
||||||
pprint(fontconfig.match(m+':slant=italic:weight=bold', verbose=True))
|
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;
|
FcPattern *pat, *tp;
|
||||||
FcObjectSet *oset;
|
FcObjectSet *oset;
|
||||||
FcFontSet *fs;
|
FcFontSet *fs;
|
||||||
FcValue file, weight, fullname, style, slant, family2;
|
FcValue file, weight, fullname, style, slant, family2, width;
|
||||||
PyObject *ans, *temp, *t;
|
PyObject *ans, *temp;
|
||||||
|
|
||||||
if (!PyArg_ParseTuple(args, "s", &family))
|
if (!PyArg_ParseTuple(args, "es", "UTF-8", &family))
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
ans = PyList_New(0);
|
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);
|
pat = FcPatternBuild(0, FC_FAMILY, FcTypeString, family, (char *) 0);
|
||||||
if (pat == NULL) { fontconfig_cleanup_find(pat, oset, fs); return PyErr_NoMemory(); }
|
if (pat == NULL) { fontconfig_cleanup_find(pat, oset, fs); return PyErr_NoMemory(); }
|
||||||
|
PyMem_Free(family); family = NULL;
|
||||||
|
|
||||||
oset = FcObjectSetCreate();
|
oset = FcObjectSetCreate();
|
||||||
if (oset == NULL) { fontconfig_cleanup_find(pat, oset, fs); return PyErr_NoMemory(); }
|
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_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_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_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, 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);
|
fs = FcFontList(FcConfigGetCurrent(), pat, oset);
|
||||||
if (fs == NULL) { fontconfig_cleanup_find(pat, oset, fs); return PyErr_NoMemory(); }
|
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_FILE, 0, &file) != FcResultMatch) continue;
|
||||||
if (FcPatternGet(tp, FC_STYLE, 0, &style) != FcResultMatch) continue;
|
if (FcPatternGet(tp, FC_STYLE, 0, &style) != FcResultMatch) continue;
|
||||||
if (FcPatternGet(tp, FC_WEIGHT, 0, &weight) != 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_SLANT, 0, &slant) != FcResultMatch) continue;
|
||||||
if (FcPatternGet(tp, FC_FAMILY, 0, &family2) != 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);
|
temp = Py_BuildValue("{s:s, s:s, s:s, s:s, s:l, s:l, s:l}",
|
||||||
if(temp == NULL) { fontconfig_cleanup_find(pat, oset, fs); return PyErr_NoMemory(); }
|
"fullname", (char*)fullname.u.s,
|
||||||
t = PyBytes_FromString((char *)fullname.u.s);
|
"path", (char*)file.u.s,
|
||||||
if(t == NULL) { fontconfig_cleanup_find(pat, oset, fs); return PyErr_NoMemory(); }
|
"style", (char*)style.u.s,
|
||||||
PyTuple_SET_ITEM(temp, 0, t);
|
"family", (char*)family2.u.s,
|
||||||
t = PyBytes_FromString((char *)file.u.s);
|
"weight", (long)weight.u.i,
|
||||||
if(t == NULL) { fontconfig_cleanup_find(pat, oset, fs); return PyErr_NoMemory(); }
|
"slant", (long)slant.u.i,
|
||||||
PyTuple_SET_ITEM(temp, 1, t);
|
"width", (long)width.u.i
|
||||||
t = PyBytes_FromString((char *)style.u.s);
|
);
|
||||||
if(t == NULL) { fontconfig_cleanup_find(pat, oset, fs); return PyErr_NoMemory(); }
|
if (temp == NULL) { fontconfig_cleanup_find(pat, oset, fs); return NULL; }
|
||||||
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);
|
|
||||||
if (PyList_Append(ans, temp) != 0)
|
if (PyList_Append(ans, temp) != 0)
|
||||||
{ fontconfig_cleanup_find(pat, oset, fs); return PyErr_NoMemory(); }
|
{ fontconfig_cleanup_find(pat, oset, fs); return PyErr_NoMemory(); }
|
||||||
}
|
}
|
||||||
@ -343,5 +336,39 @@ initfontconfig(void) {
|
|||||||
"Find fonts."
|
"Find fonts."
|
||||||
);
|
);
|
||||||
if (m == NULL) return;
|
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