Switch to using a C extension module to interface with fontconfig. This means that calibre now has a build time dependency on fontconfig as well. You can tell calibre where to find the font config headers and library via the environment variables: FC_INC_DIR and FC_LIB_DIR

This commit is contained in:
Kovid Goyal 2009-06-01 18:12:57 -07:00
parent f43a4467d3
commit e3fabe843a
22 changed files with 596 additions and 463 deletions

View File

@ -254,8 +254,6 @@ _check_symlinks_prescript()
os.link(os.path.expanduser('~/pdftohtml/pdftohtml'), os.path.join(frameworks_dir, 'pdftohtml'))
os.link(os.path.expanduser('~/pdftohtml/libpoppler.4.dylib'),
os.path.join(frameworks_dir, 'libpoppler.4.dylib'))
print 'Adding plugins'
module_dir = os.path.join(resource_dir, 'lib', 'python2.6', 'lib-dynload')
print 'Adding fontconfig'
for f in glob.glob(os.path.expanduser('~/fontconfig-bundled/*')):
dest = os.path.join(frameworks_dir, os.path.basename(f))

View File

@ -76,8 +76,21 @@ if __name__ == '__main__':
print 'WARNING: PoDoFo not found on your system. Various PDF related',
print 'functionality will not work.'
fc_inc = '/usr/include/fontconfig' if islinux else \
r'C:\cygwin\home\kovid\fontconfig\include\fontconfig' if iswindows else \
'/Users/kovid/fontconfig/include/fontconfig'
fc_lib = '/usr/lib' if islinux else \
r'C:\cygwin\home\kovid\fontconfig\lib' if iswindows else \
'/Users/kovid/fontconfig/lib'
ext_modules = optional + [
Extension('calibre.plugins.fontconfig',
sources = ['src/calibre/utils/fonts/fontconfig.c'],
include_dirs = [os.environ.get('FC_INC_DIR', fc_inc)],
libraries=['fontconfig'],
library_dirs=[os.environ.get('FC_LIB_DIR', fc_lib)]),
Extension('calibre.plugins.lzx',
sources=['src/calibre/utils/lzx/lzxmodule.c',
'src/calibre/utils/lzx/compressor.c',

View File

@ -59,7 +59,8 @@ if plugins is None:
plugin_path = getattr(pkg_resources, 'resource_filename')('calibre', 'plugins')
sys.path.insert(0, plugin_path)
for plugin in ['pictureflow', 'lzx', 'msdes', 'podofo', 'cPalmdoc'] + \
for plugin in ['pictureflow', 'lzx', 'msdes', 'podofo', 'cPalmdoc',
'fontconfig'] + \
(['winutil'] if iswindows else []) + \
(['usbobserver'] if isosx else []):
try:

View File

@ -35,7 +35,8 @@ class PRS500_PROFILE(object):
name = 'prs500'
def find_custom_fonts(options, logger):
from calibre.utils.fontconfig import files_for_family
from calibre.utils.fonts import fontconfig
files_for_family = fontconfig.files_for_family
fonts = {'serif' : None, 'sans' : None, 'mono' : None}
def family(cmd):
return cmd.split(',')[-1].strip()

View File

@ -13,6 +13,7 @@ from calibre.gui2.convert import Widget
class PluginWidget(Widget, Ui_Form):
TITLE = _('Comic Input')
HELP = _('Options specific to')+' comic '+_('input')
def __init__(self, parent, get_option, get_help, db=None, book_id=None):
Widget.__init__(self, parent, 'comic_input',

View File

@ -13,6 +13,7 @@ from calibre.gui2.convert import Widget
class PluginWidget(Widget, Ui_Form):
TITLE = _('EPUB Output')
HELP = _('Options specific to')+' EPUB '+_('output')
def __init__(self, parent, get_option, get_help, db=None, book_id=None):
Widget.__init__(self, parent, 'epub_output',

View File

@ -16,6 +16,7 @@ font_family_model = None
class PluginWidget(Widget, Ui_Form):
TITLE = _('LRF Output')
HELP = _('Options specific to')+' LRF '+_('output')
def __init__(self, parent, get_option, get_help, db=None, book_id=None):
Widget.__init__(self, parent, 'lrf_output',

View File

@ -13,6 +13,8 @@ from calibre.gui2.convert import Widget
class PluginWidget(Widget, Ui_Form):
TITLE = _('MOBI Output')
HELP = _('Options specific to')+' MOBI '+_('output')
def __init__(self, parent, get_option, get_help, db=None, book_id=None):
Widget.__init__(self, parent, 'mobi_output',

View File

@ -14,6 +14,7 @@ format_model = None
class PluginWidget(Widget, Ui_Form):
TITLE = _('PDB Output')
HELP = _('Options specific to')+' PDB '+_('output')
def __init__(self, parent, get_option, get_help, db=None, book_id=None):
Widget.__init__(self, parent, 'pdb_output', ['format'])

View File

@ -15,6 +15,7 @@ orientation_model = None
class PluginWidget(Widget, Ui_Form):
TITLE = _('PDF Output')
HELP = _('Options specific to')+' PDF '+_('output')
def __init__(self, parent, get_option, get_help, db=None, book_id=None):
Widget.__init__(self, parent, 'pdf_output', ['paper_size', 'orientation'])

View File

@ -14,6 +14,7 @@ newline_model = None
class PluginWidget(Widget, Ui_Form):
TITLE = _('TXT Output')
HELP = _('Options specific to')+' TXT '+_('output')
def __init__(self, parent, get_option, get_help, db=None, book_id=None):
Widget.__init__(self, parent, 'txt_output', ['newline'])

View File

@ -38,7 +38,7 @@ class Wizard(QDialog):
def __init__(self, parent=None):
QDialog.__init__(self, parent)
self.resize(400, 300)
self.resize(440, 480)
self.verticalLayout = QVBoxLayout(self)
self.widget = WizardWidget(self)
self.verticalLayout.addWidget(self.widget)

View File

@ -1,7 +1,8 @@
<ui version="4.0" >
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Form</class>
<widget class="QWidget" name="Form" >
<property name="geometry" >
<widget class="QWidget" name="Form">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
@ -9,133 +10,146 @@
<height>381</height>
</rect>
</property>
<property name="windowTitle" >
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QGridLayout" name="gridLayout" >
<item row="0" column="0" colspan="2" >
<widget class="QLabel" name="label" >
<property name="text" >
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Match HTML &amp;tags with tag name:</string>
</property>
<property name="buddy" >
<property name="buddy">
<cstring>tag</cstring>
</property>
</widget>
</item>
<item row="1" column="0" >
<widget class="QComboBox" name="tag" >
<property name="editable" >
<item>
<widget class="QComboBox" name="tag">
<property name="editable">
<bool>true</bool>
</property>
<item>
<property name="text" >
<property name="text">
<string>*</string>
</property>
</item>
<item>
<property name="text" >
<property name="text">
<string>a</string>
</property>
</item>
<item>
<property name="text" >
<property name="text">
<string>br</string>
</property>
</item>
<item>
<property name="text" >
<property name="text">
<string>div</string>
</property>
</item>
<item>
<property name="text" >
<property name="text">
<string>h1</string>
</property>
</item>
<item>
<property name="text" >
<property name="text">
<string>h2</string>
</property>
</item>
<item>
<property name="text" >
<property name="text">
<string>h3</string>
</property>
</item>
<item>
<property name="text" >
<property name="text">
<string>h4</string>
</property>
</item>
<item>
<property name="text" >
<property name="text">
<string>h5</string>
</property>
</item>
<item>
<property name="text" >
<property name="text">
<string>h6</string>
</property>
</item>
<item>
<property name="text" >
<property name="text">
<string>hr</string>
</property>
</item>
<item>
<property name="text" >
<property name="text">
<string>span</string>
</property>
</item>
</widget>
</item>
<item row="2" column="0" colspan="2" >
<widget class="QLabel" name="label_2" >
<property name="text" >
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>Having the &amp;attribute:</string>
</property>
<property name="buddy" >
<property name="buddy">
<cstring>attribute</cstring>
</property>
</widget>
</item>
<item row="3" column="0" colspan="2" >
<widget class="QLineEdit" name="attribute" />
<item>
<widget class="QLineEdit" name="attribute"/>
</item>
<item row="4" column="0" >
<widget class="QLabel" name="label_3" >
<property name="text" >
<item>
<widget class="QLabel" name="label_3">
<property name="text">
<string>With &amp;value:</string>
</property>
<property name="buddy" >
<property name="buddy">
<cstring>value</cstring>
</property>
</widget>
</item>
<item row="5" column="0" >
<widget class="QLineEdit" name="value" />
</item>
<item row="5" column="1" >
<widget class="QLabel" name="label_4" >
<property name="text" >
<item>
<widget class="QLabel" name="label_4">
<property name="text">
<string>(A regular expression)</string>
</property>
</widget>
</item>
<item row="6" column="0" colspan="2" >
<widget class="QLabel" name="label_5" >
<property name="text" >
<string>&lt;p>For example, to match all h2 tags that have class="chapter", set tag to &lt;i>h2&lt;/i>, attribute to &lt;i>class&lt;/i> and value to &lt;i>chapter&lt;/i>.&lt;/p>&lt;p>Leaving attribute blank will match any attribute and leaving value blank will match any value. Setting tag to * will match any tag.&lt;/p>&lt;p>To learn more advanced usage of XPath see the &lt;a href="http://calibre.kovidgoyal.net/user_manual/xpath.html">XPath Tutorial&lt;/a>.</string>
<item>
<widget class="QLineEdit" name="value"/>
</item>
<item>
<widget class="QLabel" name="label_5">
<property name="text">
<string>&lt;p&gt;For example, to match all h2 tags that have class=&quot;chapter&quot;, set tag to &lt;i&gt;h2&lt;/i&gt;, attribute to &lt;i&gt;class&lt;/i&gt; and value to &lt;i&gt;chapter&lt;/i&gt;.&lt;/p&gt;&lt;p&gt;Leaving attribute blank will match any attribute and leaving value blank will match any value. Setting tag to * will match any tag.&lt;/p&gt;&lt;p&gt;To learn more advanced usage of XPath see the &lt;a href=&quot;http://calibre.kovidgoyal.net/user_manual/xpath.html&quot;&gt;XPath Tutorial&lt;/a&gt;.</string>
</property>
<property name="wordWrap" >
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="openExternalLinks" >
<property name="openExternalLinks">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<resources/>

View File

@ -59,7 +59,6 @@ class ConfigTabs(QTabWidget):
fromlist=[1])
pw = input_widget.PluginWidget
pw.ICON = ':/images/forward.svg'
pw.HELP = _('Options specific to the input format.')
self.widgets.append(widget_factory(pw))
except ImportError:
continue
@ -71,14 +70,15 @@ class ConfigTabs(QTabWidget):
fromlist=[1])
pw = output_widget.PluginWidget
pw.ICON = ':/images/forward.svg'
pw.HELP = _('Options specific to the input format.')
self.widgets.append(widget_factory(pw))
except ImportError:
continue
for widget in self.widgets:
for i, widget in enumerate(self.widgets):
self.addTab(widget, widget.TITLE.replace('\n', ' ').replace('&',
'&&'))
self.setTabToolTip(i, widget.HELP if widget.HELP else widget.TITLE)
self.setUsesScrollButtons(True)
def commit(self):
for widget in self.widgets:

View File

@ -6,7 +6,7 @@
<rect>
<x>0</x>
<y>0</y>
<width>738</width>
<width>767</width>
<height>575</height>
</rect>
</property>

View File

@ -107,7 +107,8 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
MainWindow.__init__(self, opts, parent)
# Initialize fontconfig in a separate thread as this can be a lengthy
# process if run for the first time on this machine
self.fc = __import__('calibre.utils.fontconfig', fromlist=1)
from calibre.utils.fonts import fontconfig
self.fc = fontconfig
self.listener = Listener(listener)
self.check_messages_timer = QTimer()
self.connect(self.check_messages_timer, SIGNAL('timeout()'),
@ -1735,6 +1736,11 @@ def run_gui(opts, args, actions, listener, app):
if getattr(main, 'restart_after_quit', False):
e = sys.executable if getattr(sys, 'froze', False) else sys.argv[0]
print 'Restarting with:', e, sys.argv
if hasattr(sys, 'frameworks_dir'):
app = os.path.dirname(os.path.dirname(sys.frameworks_dir))
import subprocess
subprocess.Popen('sleep 3s; open '+app, shell=True)
else:
os.execvp(e, sys.argv)
else:
if iswindows:
@ -1829,7 +1835,9 @@ if __name__ == '__main__':
logfile = os.path.join(os.path.expanduser('~'), 'calibre.log')
if os.path.exists(logfile):
log = open(logfile).read().decode('utf-8', 'ignore')
d = QErrorMessage(('<b>Error:</b>%s<br><b>Traceback:</b><br>'
'%s<b>Log:</b><br>%s')%(unicode(err), unicode(tb), log))
d.exec_()
d = QErrorMessage()
d.showMessage(('<b>Error:</b>%s<br><b>Traceback:</b><br>'
'%s<b>Log:</b><br>%s')%(unicode(err),
unicode(tb).replace('\n', '<br>'),
log.replace('\n', '<br>')))

View File

@ -16,7 +16,7 @@ from calibre.gui2 import human_readable, NONE, TableView, \
from calibre.gui2.dialogs.job_view_ui import Ui_Dialog
from calibre.gui2.filename_pattern_ui import Ui_Form
from calibre import fit_image
from calibre.utils.fontconfig import find_font_families
from calibre.utils.fonts import fontconfig
from calibre.ebooks.metadata.meta import metadata_from_filename
from calibre.utils.config import prefs
@ -293,7 +293,7 @@ class FontFamilyModel(QAbstractListModel):
def __init__(self, *args):
QAbstractListModel.__init__(self, *args)
try:
self.families = find_font_families()
self.families = fontconfig.find_font_families()
except:
self.families = []
print 'WARNING: Could not load fonts'

View File

@ -23,6 +23,7 @@ from calibre.gui2.wizard.library_ui import Ui_WizardPage as LibraryUI
from calibre.gui2.wizard.finish_ui import Ui_WizardPage as FinishUI
from calibre.gui2.wizard.kindle_ui import Ui_WizardPage as KindleUI
from calibre.gui2.wizard.stanza_ui import Ui_WizardPage as StanzaUI
from calibre.gui2 import min_available_height, available_width
from calibre.utils.config import dynamic, prefs
from calibre.gui2 import NONE, choose_dir, error_dialog
@ -483,6 +484,15 @@ class Wizard(QWizard):
self.setPage(self.stanza_page.ID, self.stanza_page)
self.device_extra_page = None
nh, nw = min_available_height()-75, available_width()-30
if nh < 0:
nh = 580
if nw < 0:
nw = 400
nh = min(400, nh)
nw = min(580, nw)
self.resize(nw, nh)
def accept(self):
self.device_page.commit()

View File

@ -27,7 +27,6 @@ entry_points = {
'librarything = calibre.ebooks.metadata.library_thing:main',
'calibre-debug = calibre.debug:main',
'calibredb = calibre.library.cli:main',
'calibre-fontconfig = calibre.utils.fontconfig:main',
'calibre-parallel = calibre.utils.ipc.worker:main',
'calibre-customize = calibre.customize.ui:main',
'calibre-complete = calibre.utils.complete:main',

View File

@ -1,400 +0,0 @@
#!/usr/bin/env python
__license__ = 'GPL v3'
__copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net'
__docformat__ = 'restructuredtext en'
'''
:mod:`fontconfig` -- Query system fonts
=============================================
.. module:: fontconfig
:platform: Unix, Windows, OS X
:synopsis: Query system fonts
.. moduleauthor:: Kovid Goyal <kovid@kovidgoyal.net>
A ctypes based wrapper around the `fontconfig <http://fontconfig.org>`_ library.
It can be used to find all fonts available on the system as well as the closest
match to a given font specification. The main functions in this module are:
.. autofunction:: find_font_families
.. autofunction:: files_for_family
.. autofunction:: match
'''
import sys, os, locale, codecs, subprocess, re
from ctypes import cdll, c_void_p, Structure, c_int, POINTER, c_ubyte, c_char, util, \
pointer, byref, create_string_buffer, Union, c_char_p, c_double
try:
preferred_encoding = locale.getpreferredencoding()
codecs.lookup(preferred_encoding)
except:
preferred_encoding = 'utf-8'
iswindows = 'win32' in sys.platform or 'win64' in sys.platform
isosx = 'darwin' in sys.platform
isbsd = 'bsd' in sys.platform
DISABLED = False
#if isosx:
# libc = ctypes.cdll.LoadLibrary(ctypes.util.find_library('c'))
# size = ctypes.c_uint(0)
# ok = libc.sysctlbyname("hw.cpu64bit_capable", None, byref(size), None, 0)
# if ok != 0:
# is64bit = False
# else:
# buf = ctypes.c_char_p("\0" * size.value)
# ok = libc.sysctlbyname("hw.cpu64bit_capable", buf, byref(size), None, 0)
# if ok != 0:
# is64bit = False
# else:
# is64bit = '1' in buf.value
# DISABLED = is64bit
def load_library():
if isosx:
lib = os.path.join(getattr(sys, 'frameworks_dir'), 'libfontconfig.1.dylib') \
if hasattr(sys, 'frameworks_dir') else util.find_library('fontconfig')
return cdll.LoadLibrary(lib)
elif iswindows:
return cdll.LoadLibrary('libfontconfig-1')
elif isbsd:
raw = subprocess.Popen('pkg-config --libs-only-L fontconfig'.split(),
stdout=subprocess.PIPE).stdout.read().strip()
match = re.search(r'-L([^\s,]+)', raw)
if not match:
return cdll.LoadLibrary('libfontconfig.so')
return cdll.LoadLibrary(match.group(1)+'/libfontconfig.so')
else:
try:
return cdll.LoadLibrary(util.find_library('fontconfig'))
except:
try:
return cdll.LoadLibrary('libfontconfig.so')
except:
return cdll.LoadLibrary('libfontconfig.so.1')
class FcPattern(Structure):
_fields_ = [
('num', c_int),
('size', c_int),
('elts_offset', c_void_p),
('ref', c_int)
]
class FcFontSet(Structure):
_fields_ = [
('nfont', c_int),
('sfont', c_int),
('fonts', POINTER(POINTER(FcPattern)))
]
(
FcTypeVoid,
FcTypeInteger,
FcTypeDouble,
FcTypeString,
FcTypeBool,
FcTypeMatrix,
FcTypeCharSet,
FcTypeFTFace,
FcTypeLangSet
) = map(c_int, range(9))
(FcMatchPattern, FcMatchFont, FcMatchScan) = map(c_int, range(3))
(
FcResultMatch, FcResultNoMatch, FcResultTypeMismatch, FcResultNoId,
FcResultOutOfMemory
) = map(c_int, range(5))
FcFalse, FcTrue = c_int(0), c_int(1)
class _FcValue(Union):
_fields_ = [
('s', c_char_p),
('i', c_int),
('b', c_int),
('d', c_double),
]
class FcValue(Structure):
_fields_ = [
('type', c_int),
('u', _FcValue)
]
class FcObjectSet(Structure): pass
lib = load_library()
lib.FcPatternBuild.restype = POINTER(FcPattern)
lib.FcPatternCreate.restype = c_void_p
lib.FcObjectSetCreate.restype = POINTER(FcObjectSet)
lib.FcFontSetDestroy.argtypes = [POINTER(FcFontSet)]
lib.FcFontList.restype = POINTER(FcFontSet)
lib.FcNameUnparse.argtypes = [POINTER(FcPattern)]
lib.FcNameUnparse.restype = POINTER(c_ubyte)
lib.FcPatternGetString.argtypes = [POINTER(FcPattern), POINTER(c_char), c_int, c_void_p]
lib.FcPatternGetString.restype = c_int
lib.FcPatternAdd.argtypes = [c_void_p, POINTER(c_char), FcValue, c_int]
lib.FcPatternGetInteger.argtypes = [POINTER(FcPattern), POINTER(c_char), c_int, POINTER(c_int)]
lib.FcPatternGetInteger.restype = c_int
lib.FcNameParse.argtypes = [c_char_p]
lib.FcNameParse.restype = POINTER(FcPattern)
lib.FcDefaultSubstitute.argtypes = [POINTER(FcPattern)]
lib.FcConfigSubstitute.argtypes = [c_void_p, POINTER(FcPattern), c_int]
lib.FcFontSetCreate.restype = POINTER(FcFontSet)
lib.FcFontMatch.argtypes = [c_void_p, POINTER(FcPattern), POINTER(c_int)]
lib.FcFontMatch.restype = POINTER(FcPattern)
lib.FcFontSetAdd.argtypes = [POINTER(FcFontSet), POINTER(FcPattern)]
lib.FcFontSort.argtypes = [c_void_p, POINTER(FcPattern), c_int, c_void_p, POINTER(c_int)]
lib.FcFontSort.restype = POINTER(FcFontSet)
lib.FcFontRenderPrepare.argtypes = [c_void_p, POINTER(FcPattern), POINTER(FcPattern)]
lib.FcFontRenderPrepare.restype = POINTER(FcPattern)
lib.FcConfigCreate.restype = c_void_p
lib.FcConfigSetCurrent.argtypes = [c_void_p]
lib.FcConfigSetCurrent.restype = c_int
lib.FcConfigParseAndLoad.argtypes = [c_void_p, POINTER(c_char), c_int]
lib.FcConfigParseAndLoad.restype = c_int
lib.FcConfigBuildFonts.argtypes = [c_void_p]
lib.FcConfigBuildFonts.restype = c_int
_init_error = None
_initialized = False
from threading import Thread
class FontScanner(Thread):
def run(self):
# Initialize the fontconfig library. This has to be done manually
# for the OS X bundle as it may have its own private fontconfig.
if getattr(sys, 'frameworks_dir', False):# and not os.path.exists('/usr/X11/lib/libfontconfig.1.dylib'):
config_dir = os.path.join(os.path.dirname(getattr(sys, 'frameworks_dir')), 'Resources', 'fonts')
if isinstance(config_dir, unicode):
config_dir = config_dir.encode(sys.getfilesystemencoding())
config = lib.FcConfigCreate()
if not lib.FcConfigParseAndLoad(config, os.path.join(config_dir, 'fonts.conf'), 1):
_init_error = 'Could not parse the fontconfig configuration'
return
if not lib.FcConfigBuildFonts(config):
_init_error = 'Could not build fonts'
return
if not lib.FcConfigSetCurrent(config):
_init_error = 'Could not set font config'
return
elif not lib.FcInit():
_init_error = _('Could not initialize the fontconfig library')
return
global _initialized
_initialized = True
if not DISABLED:
_scanner = FontScanner()
_scanner.start()
def join():
_scanner.join(120)
if _scanner.isAlive():
raise RuntimeError('Scanning for system fonts seems to have hung. Try again in a little while.')
if _init_error is not None:
raise RuntimeError(_init_error)
def find_font_families(allowed_extensions=['ttf', 'otf']):
'''
Return an alphabetically sorted list of font families available on the system.
`allowed_extensions`: A list of allowed extensions for font file types. Defaults to
`['ttf', 'otf']`. If it is empty, it is ignored.
'''
if DISABLED:
return []
join()
allowed_extensions = [i.lower() for i in allowed_extensions]
empty_pattern = lib.FcPatternCreate()
oset = lib.FcObjectSetCreate()
if not lib.FcObjectSetAdd(oset, 'file'):
raise RuntimeError('Allocation failure')
if not lib.FcObjectSetAdd(oset, 'family'):
raise RuntimeError('Allocation failure')
fs = lib.FcFontList(0, empty_pattern, oset)
font_set = fs.contents
file = pointer(create_string_buffer(chr(0), 5000))
family = pointer(create_string_buffer(chr(0), 200))
font_families = []
for i in range(font_set.nfont):
pat = font_set.fonts[i]
if lib.FcPatternGetString(pat, 'file', 0, byref(file)) != FcResultMatch.value:
raise RuntimeError('Error processing pattern')
path = str(file.contents.value)
ext = os.path.splitext(path)[1]
if ext:
ext = ext[1:].lower()
if (not allowed_extensions) or (allowed_extensions and ext in allowed_extensions):
if lib.FcPatternGetString(pat, 'family', 0, byref(family)) != FcResultMatch.value:
raise RuntimeError('Error processing pattern')
font_families.append(str(family.contents.value))
lib.FcObjectSetDestroy(oset)
lib.FcPatternDestroy(empty_pattern)
lib.FcFontSetDestroy(fs)
font_families = list(set(font_families))
font_families.sort()
return font_families
def files_for_family(family, normalize=True):
'''
Find all the variants in the font family `family`.
Returns a dictionary of tuples. Each tuple is of the form (Full font name, path to font file).
The keys of the dictionary depend on `normalize`. If `normalize` is `False`,
they are a tuple (slant, weight) otherwise they are strings from the set
`('normal', 'bold', 'italic', 'bi', 'light', 'li')`
'''
if DISABLED:
return {}
join()
if isinstance(family, unicode):
family = family.encode(preferred_encoding)
family_pattern = lib.FcPatternBuild(0, 'family', FcTypeString, family, None)
if not family_pattern:
raise RuntimeError('Allocation failure')
#lib.FcPatternPrint(family_pattern)
oset = lib.FcObjectSetCreate()
if not lib.FcObjectSetAdd(oset, 'file'):
raise RuntimeError('Allocation failure')
if not lib.FcObjectSetAdd(oset, 'weight'):
raise RuntimeError('Allocation failure')
if not lib.FcObjectSetAdd(oset, 'fullname'):
raise RuntimeError('Allocation failure')
if not lib.FcObjectSetAdd(oset, 'slant'):
raise RuntimeError('Allocation failure')
if not lib.FcObjectSetAdd(oset, 'style'):
raise RuntimeError('Allocation failure')
fonts = {}
fs = lib.FcFontList(0, family_pattern, oset)
font_set = fs.contents
file = pointer(create_string_buffer(5000))
full_name = pointer(create_string_buffer(200))
weight = c_int(0)
slant = c_int(0)
fname = ''
for i in range(font_set.nfont):
pat = font_set.fonts[i]
#lib.FcPatternPrint(pat)
pat = font_set.fonts[i]
if lib.FcPatternGetString(pat, 'file', 0, byref(file)) != FcResultMatch.value:
raise RuntimeError('Error processing pattern')
if lib.FcPatternGetInteger(pat, 'weight', 0, byref(weight)) != FcResultMatch.value:
raise RuntimeError('Error processing pattern')
if lib.FcPatternGetString(pat, 'fullname', 0, byref(full_name)) != FcResultMatch.value:
if lib.FcPatternGetString(pat, 'fullname', 0, byref(full_name)) == FcResultNoMatch.value:
if lib.FcPatternGetString(pat, 'style', 0, byref(full_name)) != FcResultMatch.value:
raise RuntimeError('Error processing pattern')
fname = family + ' ' + full_name.contents.value
else:
raise RuntimeError('Error processing pattern')
else:
fname = full_name.contents.value
if lib.FcPatternGetInteger(pat, 'slant', 0, byref(slant)) != FcResultMatch.value:
raise RuntimeError('Error processing pattern')
style = (slant.value, weight.value)
if normalize:
italic = slant.value > 0
normal = weight.value == 80
bold = weight.value > 80
if italic:
style = 'italic' if normal else 'bi' if bold else 'li'
else:
style = 'normal' if normal else 'bold' if bold else 'light'
fonts[style] = (file.contents.value, fname)
lib.FcObjectSetDestroy(oset)
lib.FcPatternDestroy(family_pattern)
if not iswindows:
lib.FcFontSetDestroy(fs)
return fonts
def match(name, sort=False, verbose=False):
'''
Find the system font that most closely matches `name`, where `name` is a specification
of the form::
familyname-<pointsize>:<property1=value1>:<property2=value2>...
For example, `verdana:weight=bold:slant=italic`
Returns a list of dictionaries. Each dictionary has the keys: 'weight', 'slant', 'family', 'file'
`sort`: If `True` return a sorted list of matching fonts, where the sort id in order of
decreasing closeness of matching.
`verbose`: If `True` print debugging information to stdout
'''
if DISABLED:
return []
join()
if isinstance(name, unicode):
name = name.encode(preferred_encoding)
pat = lib.FcNameParse(name)
if not pat:
raise ValueError('Could not parse font name')
if verbose:
print 'Searching for pattern'
lib.FcPatternPrint(pat)
if not lib.FcConfigSubstitute(0, pat, FcMatchPattern):
raise RuntimeError('Allocation failure')
lib.FcDefaultSubstitute(pat)
fs = lib.FcFontSetCreate()
result = c_int(0)
matches = []
if sort:
font_patterns = lib.FcFontSort(0, pat, FcFalse, 0, byref(result))
if not font_patterns:
raise RuntimeError('Allocation failed')
fps = font_patterns.contents
for j in range(fps.nfont):
fpat = fps.fonts[j]
fp = lib.FcFontRenderPrepare(0, pat, fpat)
if fp:
lib.FcFontSetAdd(fs, fp)
lib.FcFontSetDestroy(font_patterns)
else:
match_pat = lib.FcFontMatch(0, pat, byref(result))
if pat:
lib.FcFontSetAdd(fs, match_pat)
if result.value != FcResultMatch.value:
lib.FcPatternDestroy(pat)
return matches
font_set = fs.contents
file = pointer(create_string_buffer(chr(0), 5000))
family = pointer(create_string_buffer(chr(0), 200))
weight = c_int(0)
slant = c_int(0)
for j in range(font_set.nfont):
fpat = font_set.fonts[j]
#lib.FcPatternPrint(fpat)
if lib.FcPatternGetString(fpat, 'file', 0, byref(file)) != FcResultMatch.value:
raise RuntimeError('Error processing pattern')
if lib.FcPatternGetString(fpat, 'family', 0, byref(family)) != FcResultMatch.value:
raise RuntimeError('Error processing pattern')
if lib.FcPatternGetInteger(fpat, 'weight', 0, byref(weight)) != FcResultMatch.value:
raise RuntimeError('Error processing pattern')
if lib.FcPatternGetInteger(fpat, 'slant', 0, byref(slant)) != FcResultMatch.value:
raise RuntimeError('Error processing pattern')
matches.append({
'file' : file.contents.value,
'family' : family.contents.value,
'weight' : weight.value,
'slant' : slant.value,
}
)
lib.FcPatternDestroy(pat)
lib.FcFontSetDestroy(fs)
return matches
def main(args=sys.argv):
print find_font_families()
if len(args) > 1:
print
print files_for_family(args[1])
print
print match(args[1], verbose=True)
return 0
if __name__ == '__main__':
sys.exit(main())

View File

@ -0,0 +1,150 @@
#!/usr/bin/env python
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
from __future__ import with_statement
__license__ = 'GPL v3'
__copyright__ = '2009, Kovid Goyal <kovid@kovidgoyal.net>'
__docformat__ = 'restructuredtext en'
import os, sys
from threading import Thread
from calibre.constants import plugins
_fc, _fc_err = plugins['fontconfig']
if _fc is None:
raise RuntimeError('Failed to load fontconfig with error:'+_fc_err)
class FontConfig(Thread):
def __init__(self):
Thread.__init__(self)
self.daemon = True
self.failed = False
def run(self):
config = None
if getattr(sys, 'frameworks_dir', False):
config_dir = os.path.join(os.path.dirname(
getattr(sys, 'frameworks_dir')), 'Resources', 'fonts')
if isinstance(config_dir, unicode):
config_dir = config_dir.encode(sys.getfilesystemencoding())
config = os.path.join(config_dir, 'fonts.conf')
try:
_fc.initialize(config)
except:
import traceback
traceback.print_exc()
self.failed = True
def wait(self):
self.join()
if self.failed:
raise RuntimeError('Failed to initialize fontconfig')
def find_font_families(self, allowed_extensions=['ttf', 'otf']):
'''
Return an alphabetically sorted list of font families available on the system.
`allowed_extensions`: A list of allowed extensions for font file types. Defaults to
`['ttf', 'otf']`. If it is empty, it is ignored.
'''
self.wait()
ans = _fc.find_font_families([bytes('.'+x) for x in allowed_extensions])
ans = sorted(set(ans), cmp=lambda x,y:cmp(x.lower(), y.lower()))
ans2 = []
for x in ans:
try:
ans2.append(x.decode('utf-8'))
except UnicodeDecodeError:
continue
return ans2
def files_for_family(self, family, normalize=True):
'''
Find all the variants in the font family `family`.
Returns a dictionary of tuples. Each tuple is of the form (Full font name, path to font file).
The keys of the dictionary depend on `normalize`. If `normalize` is `False`,
they are a tuple (slant, weight) otherwise they are strings from the set
`('normal', 'bold', 'italic', 'bi', 'light', 'li')`
'''
self.wait()
if isinstance(family, unicode):
family = family.encode('utf-8')
fonts = {}
ofamily = str(family).decode('utf-8')
for fullname, path, style, nfamily, weight, slant in \
_fc.files_for_family(str(family)):
style = (slant, weight)
if normalize:
italic = slant > 0
normal = weight == 80
bold = weight > 80
if italic:
style = 'italic' if normal else 'bi' if bold else 'li'
else:
style = 'normal' if normal else 'bold' if bold else 'light'
try:
fullname, path = fullname.decode('utf-8'), path.decode('utf-8')
nfamily = nfamily.decode('utf-8')
except UnicodeDecodeError:
continue
if style in fonts:
if nfamily.lower().strip() == ofamily.lower().strip() \
and 'Condensed' not in fullname and 'ExtraLight' not in fullname:
fonts[style] = (path, fullname)
else:
fonts[style] = (path, fullname)
return fonts
def match(self, name, all=False, verbose=False):
'''
Find the system font that most closely matches `name`, where `name` is a specification
of the form::
familyname-<pointsize>:<property1=value1>:<property2=value2>...
For example, `verdana:weight=bold:slant=italic`
Returns a list of dictionaries, or a single dictionary.
Each dictionary has the keys:
'weight', 'slant', 'family', 'file', 'fullname', 'style'
`all`: If `True` return a sorted list of matching fonts, where the sort
is in order of decreasing closeness of matching. If `False` only the
best match is returned. '''
self.wait()
if isinstance(name, unicode):
name = name.encode('utf-8')
fonts = []
for fullname, path, style, family, weight, slant in \
_fc.match(str(name), bool(all), bool(verbose)):
try:
fullname = fullname.decode('utf-8')
path = path.decode('utf-8')
style = style.decode('utf-8')
family = family.decode('utf-8')
fonts.append({
'fullname' : fullname,
'path' : path,
'style' : style,
'family' : family,
'weight' : weight,
'slant' : slant
})
except UnicodeDecodeError:
continue
return fonts if all else fonts[0]
fontconfig = FontConfig()
fontconfig.start()
def test():
from pprint import pprint;
pprint(fontconfig.find_font_families());
pprint(fontconfig.files_for_family('liberation serif'));
pprint(fontconfig.match('liberation serif:slant=italic:weight=bold', verbose=True))
if __name__ == '__main__':
test()

View File

@ -0,0 +1,331 @@
/*
:mod:`fontconfig` -- Pythonic interface to fontconfig
=====================================================
.. module:: fontconfig
:platform: All
:synopsis: Pythonic interface to the fontconfig library
.. moduleauthor:: Kovid Goyal <kovid@kovidgoyal.net> Copyright 2009
*/
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include <stdio.h>
#include <string.h>
#include <fontconfig.h>
static PyObject *
fontconfig_initialize(PyObject *self, PyObject *args) {
char *path;
FcBool ok;
FcConfig *config;
PyThreadState *_save;
if (!PyArg_ParseTuple(args, "z", &path))
return NULL;
if (path == NULL) {
_save = PyEval_SaveThread();
ok = FcInit();
PyEval_RestoreThread(_save);
} else {
config = FcConfigCreate();
if (config == NULL) return PyErr_NoMemory();
_save = PyEval_SaveThread();
ok = FcConfigParseAndLoad(config, path, FcTrue);
if (ok) ok = FcConfigBuildFonts(config);
if (ok) ok = FcConfigSetCurrent(config);
PyEval_RestoreThread(_save);
if (!ok) return PyErr_NoMemory();
ok = 1;
}
if (ok) Py_RETURN_TRUE;
Py_RETURN_FALSE;
}
static
fontconfig_cleanup_find(FcPattern *p, FcObjectSet *oset, FcFontSet *fs) {
if (p != NULL) FcPatternDestroy(p);
if (oset != NULL) FcObjectSetDestroy(oset);
if (fs != NULL) FcFontSetDestroy(fs);
}
static PyObject *
fontconfig_find_font_families(PyObject *self, PyObject *args) {
int i;
size_t flen;
char *ext;
Py_ssize_t l, j, extlen;
FcBool ok;
FcPattern *pat, *temp;
FcObjectSet *oset;
FcFontSet *fs;
FcValue v, w;
PyObject *ans, *exts, *t;
ans = PyList_New(0);
fs = NULL; oset = NULL; pat = NULL;
if (ans == NULL) return PyErr_NoMemory();
if (!PyArg_ParseTuple(args, "O", &exts))
return NULL;
if (!PySequence_Check(exts)) {
PyErr_SetString(PyExc_ValueError, "Must pass sequence of extensions");
return NULL;
}
l = PySequence_Size(exts);
pat = FcPatternCreate();
if (pat == NULL) { fontconfig_cleanup_find(pat, oset, fs); return PyErr_NoMemory(); }
oset = FcObjectSetCreate();
if (oset == NULL) { fontconfig_cleanup_find(pat, oset, fs); return PyErr_NoMemory(); }
if (!FcObjectSetAdd(oset, FC_FILE)) { fontconfig_cleanup_find(pat, oset, fs); return PyErr_NoMemory(); }
if (!FcObjectSetAdd(oset, FC_FAMILY)) { 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(); }
for (i = 0; i < fs->nfont; i++) {
temp = fs->fonts[i];
if (temp == NULL) continue;
if (FcPatternGet(temp, FC_FILE, 0, &v) != FcResultMatch) continue;
if (v.type == FcTypeString) {
flen = strlen((char *)v.u.s);
ok = FcFalse;
if (l == 0) ok = FcTrue;
for ( j = 0; j < l && !ok; j++) {
ext = PyBytes_AS_STRING(PySequence_ITEM(exts, j));
extlen = PyBytes_GET_SIZE(PySequence_ITEM(exts, j));
ok = flen > extlen && extlen > 0 &&
PyOS_strnicmp(ext, v.u.s + (flen - extlen), extlen) == 0;
}
if (ok) {
if (FcPatternGet(temp, FC_FAMILY, 0, &w) != FcResultMatch) continue;
if (w.type != FcTypeString) continue;
t = PyString_FromString(w.u.s);
if (t == NULL) { fontconfig_cleanup_find(pat, oset, fs); return PyErr_NoMemory(); }
if (PyList_Append(ans, t) != 0)
{ fontconfig_cleanup_find(pat, oset, fs); return PyErr_NoMemory(); }
}
}
}
fontconfig_cleanup_find(pat, oset, fs);
Py_INCREF(ans);
return ans;
}
static PyObject *
fontconfig_files_for_family(PyObject *self, PyObject *args) {
char *family; int i;
FcPattern *pat, *tp;
FcObjectSet *oset;
FcFontSet *fs;
FcValue file, weight, fullname, style, slant, family2;
PyObject *ans, *temp, *t;
if (!PyArg_ParseTuple(args, "s", &family))
return NULL;
ans = PyList_New(0);
if (ans == NULL) return PyErr_NoMemory();
fs = NULL; oset = NULL; pat = NULL;
pat = FcPatternBuild(0, FC_FAMILY, FcTypeString, family, (char *) 0);
if (pat == NULL) { fontconfig_cleanup_find(pat, oset, fs); return PyErr_NoMemory(); }
oset = FcObjectSetCreate();
if (oset == NULL) { fontconfig_cleanup_find(pat, oset, fs); return PyErr_NoMemory(); }
if (!FcObjectSetAdd(oset, FC_FILE)) { 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_WEIGHT)) { 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(); }
fs = FcFontList(FcConfigGetCurrent(), pat, oset);
if (fs == NULL) { fontconfig_cleanup_find(pat, oset, fs); return PyErr_NoMemory(); }
for (i = 0; i < fs->nfont; i++) {
tp = fs->fonts[i];
if (tp == NULL) continue;
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_SLANT, 0, &slant) != FcResultMatch) continue;
if (FcPatternGet(tp, FC_FAMILY, 0, &family2) != FcResultMatch) continue;
if (FcPatternGet(tp, "fullname", 0, &fullname) != FcResultMatch) continue;
temp = PyTuple_New(6);
if(temp == NULL) { fontconfig_cleanup_find(pat, oset, fs); return PyErr_NoMemory(); }
t = PyBytes_FromString(fullname.u.s);
if(t == NULL) { fontconfig_cleanup_find(pat, oset, fs); return PyErr_NoMemory(); }
PyTuple_SET_ITEM(temp, 0, t);
t = PyBytes_FromString(file.u.s);
if(t == NULL) { fontconfig_cleanup_find(pat, oset, fs); return PyErr_NoMemory(); }
PyTuple_SET_ITEM(temp, 1, t);
t = PyBytes_FromString(style.u.s);
if(t == NULL) { fontconfig_cleanup_find(pat, oset, fs); return PyErr_NoMemory(); }
PyTuple_SET_ITEM(temp, 2, t);
t = PyBytes_FromString(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)
{ fontconfig_cleanup_find(pat, oset, fs); return PyErr_NoMemory(); }
}
fontconfig_cleanup_find(pat, oset, fs);
Py_INCREF(ans);
return ans;
}
static PyObject *
fontconfig_match(PyObject *self, PyObject *args) {
char *namespec; int i;
FcPattern *pat, *tp;
FcObjectSet *oset;
FcFontSet *fs, *fs2;
FcValue file, weight, fullname, style, slant, family;
FcResult res;
PyObject *ans, *temp, *t, *all, *verbose;
if (!PyArg_ParseTuple(args, "sOO", &namespec, &all, &verbose))
return NULL;
ans = PyList_New(0);
if (ans == NULL) return PyErr_NoMemory();
fs = NULL; oset = NULL; pat = NULL; fs2 = NULL;
pat = FcNameParse(namespec);
if (pat == NULL) { fontconfig_cleanup_find(pat, oset, fs); return PyErr_NoMemory(); }
if (PyObject_IsTrue(verbose)) FcPatternPrint(pat);
if (!FcConfigSubstitute(FcConfigGetCurrent(), pat, FcMatchPattern))
{ fontconfig_cleanup_find(pat, oset, fs); return PyErr_NoMemory(); }
FcDefaultSubstitute(pat);
fs = FcFontSetCreate();
if (fs == NULL) { fontconfig_cleanup_find(pat, oset, fs); return PyErr_NoMemory(); }
if (PyObject_IsTrue(all)) {
fs2 = FcFontSort(FcConfigGetCurrent(), pat, FcTrue, NULL, &res);
if (fs2 == NULL) { fontconfig_cleanup_find(pat, oset, fs); return PyErr_NoMemory(); }
for (i = 0; i < fs2->nfont; i++) {
tp = fs2->fonts[i];
if (tp == NULL) continue;
tp = FcFontRenderPrepare(FcConfigGetCurrent(), pat, tp);
if (tp == NULL) { fontconfig_cleanup_find(pat, oset, fs); return PyErr_NoMemory(); }
if (!FcFontSetAdd(fs, tp))
{ fontconfig_cleanup_find(pat, oset, fs); return PyErr_NoMemory(); }
}
if (fs2 != NULL) FcFontSetDestroy(fs2);
} else {
tp = FcFontMatch(FcConfigGetCurrent(), pat, &res);
if (tp == NULL) { fontconfig_cleanup_find(pat, oset, fs); return PyErr_NoMemory(); }
if (!FcFontSetAdd(fs, tp))
{ fontconfig_cleanup_find(pat, oset, fs); return PyErr_NoMemory(); }
}
for (i = 0; i < fs->nfont; i++) {
tp = fs->fonts[i];
if (tp == NULL) continue;
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_SLANT, 0, &slant) != FcResultMatch) continue;
if (FcPatternGet(tp, FC_FAMILY, 0, &family) != FcResultMatch) continue;
if (FcPatternGet(tp, "fullname", 0, &fullname) != FcResultMatch) continue;
temp = PyTuple_New(6);
if(temp == NULL) { fontconfig_cleanup_find(pat, oset, fs); return PyErr_NoMemory(); }
t = PyBytes_FromString(fullname.u.s);
if(t == NULL) { fontconfig_cleanup_find(pat, oset, fs); return PyErr_NoMemory(); }
PyTuple_SET_ITEM(temp, 0, t);
t = PyBytes_FromString(file.u.s);
if(t == NULL) { fontconfig_cleanup_find(pat, oset, fs); return PyErr_NoMemory(); }
PyTuple_SET_ITEM(temp, 1, t);
t = PyBytes_FromString(style.u.s);
if(t == NULL) { fontconfig_cleanup_find(pat, oset, fs); return PyErr_NoMemory(); }
PyTuple_SET_ITEM(temp, 2, t);
t = PyBytes_FromString(family.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)
{ fontconfig_cleanup_find(pat, oset, fs); return PyErr_NoMemory(); }
}
fontconfig_cleanup_find(pat, oset, fs);
Py_INCREF(ans);
return ans;
}
static
PyMethodDef fontconfig_methods[] = {
{"initialize", fontconfig_initialize, METH_VARARGS,
"initialize(path_to_config_file)\n\n"
"Initialize the library. If path to config file is specified it is used instead of the "
"default configuration. Returns True iff the initialization succeeded."
},
{"find_font_families", fontconfig_find_font_families, METH_VARARGS,
"find_font_families(allowed_extensions)\n\n"
"Find all font families on the system for fonts of the specified types. If no "
"types are specified all font families are returned."
},
{"files_for_family", fontconfig_files_for_family, METH_VARARGS,
"files_for_family(family, normalize)\n\n"
"Find all the variants in the font family `family`. "
"Returns a list of tuples. Each tuple is of the form "
"(fullname, path, style, family, weight, slant). "
},
{"match", fontconfig_match, METH_VARARGS,
"match(namespec,all,verbose)\n\n"
"Find all system fonts that match namespec, in decreasing order "
"of closeness. "
"Returns a list of tuples. Each tuple is of the form "
"(fullname, path, style, family, weight, slant). "
},
{NULL, NULL, 0, NULL}
};
PyMODINIT_FUNC
initfontconfig(void) {
PyObject *m;
m = Py_InitModule3(
"fontconfig", fontconfig_methods,
"Find fonts."
);
if (m == NULL) return;
}