mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-08 02:34:06 -04:00
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:
parent
f43a4467d3
commit
e3fabe843a
@ -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/pdftohtml'), os.path.join(frameworks_dir, 'pdftohtml'))
|
||||||
os.link(os.path.expanduser('~/pdftohtml/libpoppler.4.dylib'),
|
os.link(os.path.expanduser('~/pdftohtml/libpoppler.4.dylib'),
|
||||||
os.path.join(frameworks_dir, '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'
|
print 'Adding fontconfig'
|
||||||
for f in glob.glob(os.path.expanduser('~/fontconfig-bundled/*')):
|
for f in glob.glob(os.path.expanduser('~/fontconfig-bundled/*')):
|
||||||
dest = os.path.join(frameworks_dir, os.path.basename(f))
|
dest = os.path.join(frameworks_dir, os.path.basename(f))
|
||||||
|
13
setup.py
13
setup.py
@ -76,8 +76,21 @@ if __name__ == '__main__':
|
|||||||
print 'WARNING: PoDoFo not found on your system. Various PDF related',
|
print 'WARNING: PoDoFo not found on your system. Various PDF related',
|
||||||
print 'functionality will not work.'
|
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 + [
|
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',
|
Extension('calibre.plugins.lzx',
|
||||||
sources=['src/calibre/utils/lzx/lzxmodule.c',
|
sources=['src/calibre/utils/lzx/lzxmodule.c',
|
||||||
'src/calibre/utils/lzx/compressor.c',
|
'src/calibre/utils/lzx/compressor.c',
|
||||||
|
@ -59,7 +59,8 @@ if plugins is None:
|
|||||||
plugin_path = getattr(pkg_resources, 'resource_filename')('calibre', 'plugins')
|
plugin_path = getattr(pkg_resources, 'resource_filename')('calibre', 'plugins')
|
||||||
sys.path.insert(0, plugin_path)
|
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 []) + \
|
(['winutil'] if iswindows else []) + \
|
||||||
(['usbobserver'] if isosx else []):
|
(['usbobserver'] if isosx else []):
|
||||||
try:
|
try:
|
||||||
|
@ -35,7 +35,8 @@ class PRS500_PROFILE(object):
|
|||||||
name = 'prs500'
|
name = 'prs500'
|
||||||
|
|
||||||
def find_custom_fonts(options, logger):
|
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}
|
fonts = {'serif' : None, 'sans' : None, 'mono' : None}
|
||||||
def family(cmd):
|
def family(cmd):
|
||||||
return cmd.split(',')[-1].strip()
|
return cmd.split(',')[-1].strip()
|
||||||
|
@ -13,6 +13,7 @@ from calibre.gui2.convert import Widget
|
|||||||
class PluginWidget(Widget, Ui_Form):
|
class PluginWidget(Widget, Ui_Form):
|
||||||
|
|
||||||
TITLE = _('Comic Input')
|
TITLE = _('Comic Input')
|
||||||
|
HELP = _('Options specific to')+' comic '+_('input')
|
||||||
|
|
||||||
def __init__(self, parent, get_option, get_help, db=None, book_id=None):
|
def __init__(self, parent, get_option, get_help, db=None, book_id=None):
|
||||||
Widget.__init__(self, parent, 'comic_input',
|
Widget.__init__(self, parent, 'comic_input',
|
||||||
|
@ -13,6 +13,7 @@ from calibre.gui2.convert import Widget
|
|||||||
class PluginWidget(Widget, Ui_Form):
|
class PluginWidget(Widget, Ui_Form):
|
||||||
|
|
||||||
TITLE = _('EPUB Output')
|
TITLE = _('EPUB Output')
|
||||||
|
HELP = _('Options specific to')+' EPUB '+_('output')
|
||||||
|
|
||||||
def __init__(self, parent, get_option, get_help, db=None, book_id=None):
|
def __init__(self, parent, get_option, get_help, db=None, book_id=None):
|
||||||
Widget.__init__(self, parent, 'epub_output',
|
Widget.__init__(self, parent, 'epub_output',
|
||||||
|
@ -16,6 +16,7 @@ font_family_model = None
|
|||||||
class PluginWidget(Widget, Ui_Form):
|
class PluginWidget(Widget, Ui_Form):
|
||||||
|
|
||||||
TITLE = _('LRF Output')
|
TITLE = _('LRF Output')
|
||||||
|
HELP = _('Options specific to')+' LRF '+_('output')
|
||||||
|
|
||||||
def __init__(self, parent, get_option, get_help, db=None, book_id=None):
|
def __init__(self, parent, get_option, get_help, db=None, book_id=None):
|
||||||
Widget.__init__(self, parent, 'lrf_output',
|
Widget.__init__(self, parent, 'lrf_output',
|
||||||
|
@ -13,6 +13,8 @@ from calibre.gui2.convert import Widget
|
|||||||
class PluginWidget(Widget, Ui_Form):
|
class PluginWidget(Widget, Ui_Form):
|
||||||
|
|
||||||
TITLE = _('MOBI Output')
|
TITLE = _('MOBI Output')
|
||||||
|
HELP = _('Options specific to')+' MOBI '+_('output')
|
||||||
|
|
||||||
|
|
||||||
def __init__(self, parent, get_option, get_help, db=None, book_id=None):
|
def __init__(self, parent, get_option, get_help, db=None, book_id=None):
|
||||||
Widget.__init__(self, parent, 'mobi_output',
|
Widget.__init__(self, parent, 'mobi_output',
|
||||||
|
@ -14,6 +14,7 @@ format_model = None
|
|||||||
class PluginWidget(Widget, Ui_Form):
|
class PluginWidget(Widget, Ui_Form):
|
||||||
|
|
||||||
TITLE = _('PDB Output')
|
TITLE = _('PDB Output')
|
||||||
|
HELP = _('Options specific to')+' PDB '+_('output')
|
||||||
|
|
||||||
def __init__(self, parent, get_option, get_help, db=None, book_id=None):
|
def __init__(self, parent, get_option, get_help, db=None, book_id=None):
|
||||||
Widget.__init__(self, parent, 'pdb_output', ['format'])
|
Widget.__init__(self, parent, 'pdb_output', ['format'])
|
||||||
|
@ -15,6 +15,7 @@ orientation_model = None
|
|||||||
class PluginWidget(Widget, Ui_Form):
|
class PluginWidget(Widget, Ui_Form):
|
||||||
|
|
||||||
TITLE = _('PDF Output')
|
TITLE = _('PDF Output')
|
||||||
|
HELP = _('Options specific to')+' PDF '+_('output')
|
||||||
|
|
||||||
def __init__(self, parent, get_option, get_help, db=None, book_id=None):
|
def __init__(self, parent, get_option, get_help, db=None, book_id=None):
|
||||||
Widget.__init__(self, parent, 'pdf_output', ['paper_size', 'orientation'])
|
Widget.__init__(self, parent, 'pdf_output', ['paper_size', 'orientation'])
|
||||||
|
@ -14,6 +14,7 @@ newline_model = None
|
|||||||
class PluginWidget(Widget, Ui_Form):
|
class PluginWidget(Widget, Ui_Form):
|
||||||
|
|
||||||
TITLE = _('TXT Output')
|
TITLE = _('TXT Output')
|
||||||
|
HELP = _('Options specific to')+' TXT '+_('output')
|
||||||
|
|
||||||
def __init__(self, parent, get_option, get_help, db=None, book_id=None):
|
def __init__(self, parent, get_option, get_help, db=None, book_id=None):
|
||||||
Widget.__init__(self, parent, 'txt_output', ['newline'])
|
Widget.__init__(self, parent, 'txt_output', ['newline'])
|
||||||
|
@ -38,7 +38,7 @@ class Wizard(QDialog):
|
|||||||
|
|
||||||
def __init__(self, parent=None):
|
def __init__(self, parent=None):
|
||||||
QDialog.__init__(self, parent)
|
QDialog.__init__(self, parent)
|
||||||
self.resize(400, 300)
|
self.resize(440, 480)
|
||||||
self.verticalLayout = QVBoxLayout(self)
|
self.verticalLayout = QVBoxLayout(self)
|
||||||
self.widget = WizardWidget(self)
|
self.widget = WizardWidget(self)
|
||||||
self.verticalLayout.addWidget(self.widget)
|
self.verticalLayout.addWidget(self.widget)
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
<ui version="4.0" >
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
<class>Form</class>
|
<class>Form</class>
|
||||||
<widget class="QWidget" name="Form" >
|
<widget class="QWidget" name="Form">
|
||||||
<property name="geometry" >
|
<property name="geometry">
|
||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
@ -9,133 +10,146 @@
|
|||||||
<height>381</height>
|
<height>381</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="windowTitle" >
|
<property name="windowTitle">
|
||||||
<string>Form</string>
|
<string>Form</string>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QGridLayout" name="gridLayout" >
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
<item row="0" column="0" colspan="2" >
|
<item>
|
||||||
<widget class="QLabel" name="label" >
|
<widget class="QLabel" name="label">
|
||||||
<property name="text" >
|
<property name="text">
|
||||||
<string>Match HTML &tags with tag name:</string>
|
<string>Match HTML &tags with tag name:</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="buddy" >
|
<property name="buddy">
|
||||||
<cstring>tag</cstring>
|
<cstring>tag</cstring>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="1" column="0" >
|
<item>
|
||||||
<widget class="QComboBox" name="tag" >
|
<widget class="QComboBox" name="tag">
|
||||||
<property name="editable" >
|
<property name="editable">
|
||||||
<bool>true</bool>
|
<bool>true</bool>
|
||||||
</property>
|
</property>
|
||||||
<item>
|
<item>
|
||||||
<property name="text" >
|
<property name="text">
|
||||||
<string>*</string>
|
<string>*</string>
|
||||||
</property>
|
</property>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<property name="text" >
|
<property name="text">
|
||||||
<string>a</string>
|
<string>a</string>
|
||||||
</property>
|
</property>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<property name="text" >
|
<property name="text">
|
||||||
<string>br</string>
|
<string>br</string>
|
||||||
</property>
|
</property>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<property name="text" >
|
<property name="text">
|
||||||
<string>div</string>
|
<string>div</string>
|
||||||
</property>
|
</property>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<property name="text" >
|
<property name="text">
|
||||||
<string>h1</string>
|
<string>h1</string>
|
||||||
</property>
|
</property>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<property name="text" >
|
<property name="text">
|
||||||
<string>h2</string>
|
<string>h2</string>
|
||||||
</property>
|
</property>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<property name="text" >
|
<property name="text">
|
||||||
<string>h3</string>
|
<string>h3</string>
|
||||||
</property>
|
</property>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<property name="text" >
|
<property name="text">
|
||||||
<string>h4</string>
|
<string>h4</string>
|
||||||
</property>
|
</property>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<property name="text" >
|
<property name="text">
|
||||||
<string>h5</string>
|
<string>h5</string>
|
||||||
</property>
|
</property>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<property name="text" >
|
<property name="text">
|
||||||
<string>h6</string>
|
<string>h6</string>
|
||||||
</property>
|
</property>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<property name="text" >
|
<property name="text">
|
||||||
<string>hr</string>
|
<string>hr</string>
|
||||||
</property>
|
</property>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<property name="text" >
|
<property name="text">
|
||||||
<string>span</string>
|
<string>span</string>
|
||||||
</property>
|
</property>
|
||||||
</item>
|
</item>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="2" column="0" colspan="2" >
|
<item>
|
||||||
<widget class="QLabel" name="label_2" >
|
<widget class="QLabel" name="label_2">
|
||||||
<property name="text" >
|
<property name="text">
|
||||||
<string>Having the &attribute:</string>
|
<string>Having the &attribute:</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="buddy" >
|
<property name="buddy">
|
||||||
<cstring>attribute</cstring>
|
<cstring>attribute</cstring>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="3" column="0" colspan="2" >
|
<item>
|
||||||
<widget class="QLineEdit" name="attribute" />
|
<widget class="QLineEdit" name="attribute"/>
|
||||||
</item>
|
</item>
|
||||||
<item row="4" column="0" >
|
<item>
|
||||||
<widget class="QLabel" name="label_3" >
|
<widget class="QLabel" name="label_3">
|
||||||
<property name="text" >
|
<property name="text">
|
||||||
<string>With &value:</string>
|
<string>With &value:</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="buddy" >
|
<property name="buddy">
|
||||||
<cstring>value</cstring>
|
<cstring>value</cstring>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="5" column="0" >
|
<item>
|
||||||
<widget class="QLineEdit" name="value" />
|
<widget class="QLabel" name="label_4">
|
||||||
</item>
|
<property name="text">
|
||||||
<item row="5" column="1" >
|
|
||||||
<widget class="QLabel" name="label_4" >
|
|
||||||
<property name="text" >
|
|
||||||
<string>(A regular expression)</string>
|
<string>(A regular expression)</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="6" column="0" colspan="2" >
|
<item>
|
||||||
<widget class="QLabel" name="label_5" >
|
<widget class="QLineEdit" name="value"/>
|
||||||
<property name="text" >
|
</item>
|
||||||
<string><p>For example, to match all h2 tags that have class="chapter", set tag to <i>h2</i>, attribute to <i>class</i> and value to <i>chapter</i>.</p><p>Leaving attribute blank will match any attribute and leaving value blank will match any value. Setting tag to * will match any tag.</p><p>To learn more advanced usage of XPath see the <a href="http://calibre.kovidgoyal.net/user_manual/xpath.html">XPath Tutorial</a>.</string>
|
<item>
|
||||||
|
<widget class="QLabel" name="label_5">
|
||||||
|
<property name="text">
|
||||||
|
<string><p>For example, to match all h2 tags that have class="chapter", set tag to <i>h2</i>, attribute to <i>class</i> and value to <i>chapter</i>.</p><p>Leaving attribute blank will match any attribute and leaving value blank will match any value. Setting tag to * will match any tag.</p><p>To learn more advanced usage of XPath see the <a href="http://calibre.kovidgoyal.net/user_manual/xpath.html">XPath Tutorial</a>.</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="wordWrap" >
|
<property name="wordWrap">
|
||||||
<bool>true</bool>
|
<bool>true</bool>
|
||||||
</property>
|
</property>
|
||||||
<property name="openExternalLinks" >
|
<property name="openExternalLinks">
|
||||||
<bool>true</bool>
|
<bool>true</bool>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</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>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
<resources/>
|
<resources/>
|
||||||
|
@ -59,7 +59,6 @@ class ConfigTabs(QTabWidget):
|
|||||||
fromlist=[1])
|
fromlist=[1])
|
||||||
pw = input_widget.PluginWidget
|
pw = input_widget.PluginWidget
|
||||||
pw.ICON = ':/images/forward.svg'
|
pw.ICON = ':/images/forward.svg'
|
||||||
pw.HELP = _('Options specific to the input format.')
|
|
||||||
self.widgets.append(widget_factory(pw))
|
self.widgets.append(widget_factory(pw))
|
||||||
except ImportError:
|
except ImportError:
|
||||||
continue
|
continue
|
||||||
@ -71,14 +70,15 @@ class ConfigTabs(QTabWidget):
|
|||||||
fromlist=[1])
|
fromlist=[1])
|
||||||
pw = output_widget.PluginWidget
|
pw = output_widget.PluginWidget
|
||||||
pw.ICON = ':/images/forward.svg'
|
pw.ICON = ':/images/forward.svg'
|
||||||
pw.HELP = _('Options specific to the input format.')
|
|
||||||
self.widgets.append(widget_factory(pw))
|
self.widgets.append(widget_factory(pw))
|
||||||
except ImportError:
|
except ImportError:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
for widget in self.widgets:
|
for i, widget in enumerate(self.widgets):
|
||||||
self.addTab(widget, widget.TITLE.replace('\n', ' ').replace('&',
|
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):
|
def commit(self):
|
||||||
for widget in self.widgets:
|
for widget in self.widgets:
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>738</width>
|
<width>767</width>
|
||||||
<height>575</height>
|
<height>575</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
|
@ -107,7 +107,8 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
|
|||||||
MainWindow.__init__(self, opts, parent)
|
MainWindow.__init__(self, opts, parent)
|
||||||
# Initialize fontconfig in a separate thread as this can be a lengthy
|
# Initialize fontconfig in a separate thread as this can be a lengthy
|
||||||
# process if run for the first time on this machine
|
# 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.listener = Listener(listener)
|
||||||
self.check_messages_timer = QTimer()
|
self.check_messages_timer = QTimer()
|
||||||
self.connect(self.check_messages_timer, SIGNAL('timeout()'),
|
self.connect(self.check_messages_timer, SIGNAL('timeout()'),
|
||||||
@ -1735,7 +1736,12 @@ def run_gui(opts, args, actions, listener, app):
|
|||||||
if getattr(main, 'restart_after_quit', False):
|
if getattr(main, 'restart_after_quit', False):
|
||||||
e = sys.executable if getattr(sys, 'froze', False) else sys.argv[0]
|
e = sys.executable if getattr(sys, 'froze', False) else sys.argv[0]
|
||||||
print 'Restarting with:', e, sys.argv
|
print 'Restarting with:', e, sys.argv
|
||||||
os.execvp(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:
|
else:
|
||||||
if iswindows:
|
if iswindows:
|
||||||
try:
|
try:
|
||||||
@ -1829,7 +1835,9 @@ if __name__ == '__main__':
|
|||||||
logfile = os.path.join(os.path.expanduser('~'), 'calibre.log')
|
logfile = os.path.join(os.path.expanduser('~'), 'calibre.log')
|
||||||
if os.path.exists(logfile):
|
if os.path.exists(logfile):
|
||||||
log = open(logfile).read().decode('utf-8', 'ignore')
|
log = open(logfile).read().decode('utf-8', 'ignore')
|
||||||
d = QErrorMessage(('<b>Error:</b>%s<br><b>Traceback:</b><br>'
|
d = QErrorMessage()
|
||||||
'%s<b>Log:</b><br>%s')%(unicode(err), unicode(tb), log))
|
d.showMessage(('<b>Error:</b>%s<br><b>Traceback:</b><br>'
|
||||||
d.exec_()
|
'%s<b>Log:</b><br>%s')%(unicode(err),
|
||||||
|
unicode(tb).replace('\n', '<br>'),
|
||||||
|
log.replace('\n', '<br>')))
|
||||||
|
|
||||||
|
@ -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.dialogs.job_view_ui import Ui_Dialog
|
||||||
from calibre.gui2.filename_pattern_ui import Ui_Form
|
from calibre.gui2.filename_pattern_ui import Ui_Form
|
||||||
from calibre import fit_image
|
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.ebooks.metadata.meta import metadata_from_filename
|
||||||
from calibre.utils.config import prefs
|
from calibre.utils.config import prefs
|
||||||
|
|
||||||
@ -293,7 +293,7 @@ class FontFamilyModel(QAbstractListModel):
|
|||||||
def __init__(self, *args):
|
def __init__(self, *args):
|
||||||
QAbstractListModel.__init__(self, *args)
|
QAbstractListModel.__init__(self, *args)
|
||||||
try:
|
try:
|
||||||
self.families = find_font_families()
|
self.families = fontconfig.find_font_families()
|
||||||
except:
|
except:
|
||||||
self.families = []
|
self.families = []
|
||||||
print 'WARNING: Could not load fonts'
|
print 'WARNING: Could not load fonts'
|
||||||
|
@ -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.finish_ui import Ui_WizardPage as FinishUI
|
||||||
from calibre.gui2.wizard.kindle_ui import Ui_WizardPage as KindleUI
|
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.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.utils.config import dynamic, prefs
|
||||||
from calibre.gui2 import NONE, choose_dir, error_dialog
|
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.setPage(self.stanza_page.ID, self.stanza_page)
|
||||||
|
|
||||||
self.device_extra_page = None
|
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):
|
def accept(self):
|
||||||
self.device_page.commit()
|
self.device_page.commit()
|
||||||
|
@ -27,7 +27,6 @@ entry_points = {
|
|||||||
'librarything = calibre.ebooks.metadata.library_thing:main',
|
'librarything = calibre.ebooks.metadata.library_thing:main',
|
||||||
'calibre-debug = calibre.debug:main',
|
'calibre-debug = calibre.debug:main',
|
||||||
'calibredb = calibre.library.cli:main',
|
'calibredb = calibre.library.cli:main',
|
||||||
'calibre-fontconfig = calibre.utils.fontconfig:main',
|
|
||||||
'calibre-parallel = calibre.utils.ipc.worker:main',
|
'calibre-parallel = calibre.utils.ipc.worker:main',
|
||||||
'calibre-customize = calibre.customize.ui:main',
|
'calibre-customize = calibre.customize.ui:main',
|
||||||
'calibre-complete = calibre.utils.complete:main',
|
'calibre-complete = calibre.utils.complete:main',
|
||||||
|
@ -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())
|
|
150
src/calibre/utils/fonts/__init__.py
Normal file
150
src/calibre/utils/fonts/__init__.py
Normal 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()
|
331
src/calibre/utils/fonts/fontconfig.c
Normal file
331
src/calibre/utils/fonts/fontconfig.c
Normal 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;
|
||||||
|
}
|
||||||
|
|
Loading…
x
Reference in New Issue
Block a user