mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Use fontconfig to manage fonts
This commit is contained in:
parent
261bf8021a
commit
94736825ca
@ -240,6 +240,9 @@ _check_symlinks_prescript()
|
|||||||
os.link(os.path.expanduser('~/pdftohtml'), os.path.join(frameworks_dir, 'pdftohtml'))
|
os.link(os.path.expanduser('~/pdftohtml'), os.path.join(frameworks_dir, 'pdftohtml'))
|
||||||
print 'Adding plugins'
|
print 'Adding plugins'
|
||||||
module_dir = os.path.join(resource_dir, 'lib', 'python2.5', 'lib-dynload')
|
module_dir = os.path.join(resource_dir, 'lib', 'python2.5', 'lib-dynload')
|
||||||
|
print 'Adding fontconfig'
|
||||||
|
for f in glob.glob(os.path.expanduser('~/fontconfig/*')):
|
||||||
|
os.link(f, os.path.join(frameworks_dir, os.path.basename(f)))
|
||||||
for src, dest in plugin_files:
|
for src, dest in plugin_files:
|
||||||
if 'dylib' in dest:
|
if 'dylib' in dest:
|
||||||
os.link(src, os.path.join(frameworks_dir, dest))
|
os.link(src, os.path.join(frameworks_dir, dest))
|
||||||
|
@ -15,15 +15,14 @@ from optparse import OptionParser as _OptionParser
|
|||||||
from optparse import IndentedHelpFormatter
|
from optparse import IndentedHelpFormatter
|
||||||
from logging import Formatter
|
from logging import Formatter
|
||||||
|
|
||||||
from ttfquery import findsystem, describe
|
|
||||||
from PyQt4.QtCore import QSettings, QVariant, QUrl
|
from PyQt4.QtCore import QSettings, QVariant, QUrl
|
||||||
from PyQt4.QtGui import QDesktopServices
|
from PyQt4.QtGui import QDesktopServices
|
||||||
|
|
||||||
from calibre.translations.msgfmt import make
|
from calibre.translations.msgfmt import make
|
||||||
from calibre.ebooks.chardet import detect
|
from calibre.ebooks.chardet import detect
|
||||||
from calibre.terminfo import TerminalController
|
from calibre.terminfo import TerminalController
|
||||||
terminal_controller = TerminalController(sys.stdout)
|
|
||||||
|
|
||||||
|
terminal_controller = TerminalController(sys.stdout)
|
||||||
iswindows = 'win32' in sys.platform.lower() or 'win64' in sys.platform.lower()
|
iswindows = 'win32' in sys.platform.lower() or 'win64' in sys.platform.lower()
|
||||||
isosx = 'darwin' in sys.platform.lower()
|
isosx = 'darwin' in sys.platform.lower()
|
||||||
islinux = not(iswindows or isosx)
|
islinux = not(iswindows or isosx)
|
||||||
@ -306,44 +305,6 @@ def set_translator():
|
|||||||
|
|
||||||
set_translator()
|
set_translator()
|
||||||
|
|
||||||
font_families = {}
|
|
||||||
def get_font_families(cached=None):
|
|
||||||
global font_families
|
|
||||||
if cached is not None:
|
|
||||||
font_families = cached
|
|
||||||
if not font_families:
|
|
||||||
try:
|
|
||||||
ffiles = findsystem.findFonts()
|
|
||||||
except Exception, err:
|
|
||||||
print 'WARNING: Could not find fonts on your system.'
|
|
||||||
print err
|
|
||||||
else:
|
|
||||||
zlist = []
|
|
||||||
for ff in ffiles:
|
|
||||||
try:
|
|
||||||
if 'Optane' in str(ff):
|
|
||||||
font = describe.openFont(ff)
|
|
||||||
wt, italic = describe.modifiers(font)
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
try:
|
|
||||||
font = describe.openFont(ff)
|
|
||||||
except: # Some font files cause ttfquery to raise an exception, in which case they are ignored
|
|
||||||
continue
|
|
||||||
try:
|
|
||||||
wt, italic = describe.modifiers(font)
|
|
||||||
except:
|
|
||||||
wt, italic = 0, 0
|
|
||||||
if wt == 400 and italic == 0:
|
|
||||||
try:
|
|
||||||
family = describe.shortName(font)[1].strip()
|
|
||||||
except: # Windows strikes again!
|
|
||||||
continue
|
|
||||||
zlist.append((family, ff))
|
|
||||||
font_families = dict(zlist)
|
|
||||||
|
|
||||||
return font_families
|
|
||||||
|
|
||||||
def sanitize_file_name(name):
|
def sanitize_file_name(name):
|
||||||
'''
|
'''
|
||||||
Remove characters that are illegal in filenames from name.
|
Remove characters that are illegal in filenames from name.
|
||||||
@ -596,3 +557,12 @@ def entity_to_unicode(match, exceptions=[], encoding='cp1252'):
|
|||||||
except KeyError:
|
except KeyError:
|
||||||
return '&'+ent+';'
|
return '&'+ent+';'
|
||||||
|
|
||||||
|
if isosx:
|
||||||
|
fdir = os.path.expanduser('~/.fonts')
|
||||||
|
if not os.path.exists(fdir):
|
||||||
|
os.makedirs(fdir)
|
||||||
|
if not os.path.exists(os.path.join(fdir, 'LiberationSans_Regular.ttf')):
|
||||||
|
from calibre.ebooks.lrf.fonts.liberation import __all__ as fonts
|
||||||
|
for font in fonts:
|
||||||
|
exec 'from calibre.ebooks.lrf.fonts.liberation.'+font+' import font_data'
|
||||||
|
open(os.path.join(fdir, font+'.ttf'), 'wb').write(font_data)
|
@ -9,7 +9,6 @@ from optparse import OptionValueError
|
|||||||
from htmlentitydefs import name2codepoint
|
from htmlentitydefs import name2codepoint
|
||||||
from uuid import uuid4
|
from uuid import uuid4
|
||||||
|
|
||||||
from ttfquery import describe, findsystem
|
|
||||||
from fontTools.ttLib import TTLibError
|
from fontTools.ttLib import TTLibError
|
||||||
|
|
||||||
from calibre.ebooks.lrf.pylrs.pylrs import Book as _Book
|
from calibre.ebooks.lrf.pylrs.pylrs import Book as _Book
|
||||||
@ -67,20 +66,6 @@ def profile_from_string(option, opt_str, value, parser):
|
|||||||
except KeyError:
|
except KeyError:
|
||||||
raise OptionValueError('Profile: '+value+' is not implemented. Implemented profiles: %s'%(profile_map.keys()))
|
raise OptionValueError('Profile: '+value+' is not implemented. Implemented profiles: %s'%(profile_map.keys()))
|
||||||
|
|
||||||
def font_family(option, opt_str, value, parser):
|
|
||||||
if value:
|
|
||||||
value = value.split(',')
|
|
||||||
if len(value) != 2:
|
|
||||||
raise OptionValueError('Font family specification must be of the form'+\
|
|
||||||
' "path to font directory, font family"')
|
|
||||||
path, family = tuple(value)
|
|
||||||
if not os.path.isdir(path) or not os.access(path, os.R_OK|os.X_OK):
|
|
||||||
raise OptionValueError('Cannot read from ' + path)
|
|
||||||
setattr(parser.values, option.dest, (path, family))
|
|
||||||
else:
|
|
||||||
setattr(parser.values, option.dest, tuple())
|
|
||||||
|
|
||||||
|
|
||||||
def option_parser(usage, gui_mode=False):
|
def option_parser(usage, gui_mode=False):
|
||||||
parser = OptionParser(usage=usage, gui_mode=gui_mode)
|
parser = OptionParser(usage=usage, gui_mode=gui_mode)
|
||||||
metadata = parser.add_option_group('METADATA OPTIONS')
|
metadata = parser.add_option_group('METADATA OPTIONS')
|
||||||
@ -203,18 +188,17 @@ def option_parser(usage, gui_mode=False):
|
|||||||
fonts = parser.add_option_group('FONT FAMILIES',
|
fonts = parser.add_option_group('FONT FAMILIES',
|
||||||
_('''Specify trutype font families for serif, sans-serif and monospace fonts. '''
|
_('''Specify trutype font families for serif, sans-serif and monospace fonts. '''
|
||||||
'''These fonts will be embedded in the LRF file. Note that custom fonts lead to '''
|
'''These fonts will be embedded in the LRF file. Note that custom fonts lead to '''
|
||||||
'''slower page turns. Each family specification is of the form: '''
|
'''slower page turns. '''
|
||||||
'''"path to fonts directory, family" '''
|
|
||||||
'''For example: '''
|
'''For example: '''
|
||||||
'''--serif-family "%s, Times New Roman"
|
'''--serif-family "Times New Roman"
|
||||||
''') % ('C:\Windows\Fonts' if iswindows else '/usr/share/fonts/corefonts'))
|
'''))
|
||||||
fonts.add_option('--serif-family', action='callback', callback=font_family,
|
fonts.add_option('--serif-family',
|
||||||
default=None, dest='serif_family', type='string',
|
default=None, dest='serif_family', type='string',
|
||||||
help=_('The serif family of fonts to embed'))
|
help=_('The serif family of fonts to embed'))
|
||||||
fonts.add_option('--sans-family', action='callback', callback=font_family,
|
fonts.add_option('--sans-family',
|
||||||
default=None, dest='sans_family', type='string',
|
default=None, dest='sans_family', type='string',
|
||||||
help=_('The sans-serif family of fonts to embed'))
|
help=_('The sans-serif family of fonts to embed'))
|
||||||
fonts.add_option('--mono-family', action='callback', callback=font_family,
|
fonts.add_option('--mono-family',
|
||||||
default=None, dest='mono_family', type='string',
|
default=None, dest='mono_family', type='string',
|
||||||
help=_('The monospace family of fonts to embed'))
|
help=_('The monospace family of fonts to embed'))
|
||||||
|
|
||||||
@ -231,45 +215,25 @@ def option_parser(usage, gui_mode=False):
|
|||||||
return parser
|
return parser
|
||||||
|
|
||||||
def find_custom_fonts(options, logger):
|
def find_custom_fonts(options, logger):
|
||||||
|
from calibre.utils.fontconfig import files_for_family
|
||||||
fonts = {'serif' : None, 'sans' : None, 'mono' : None}
|
fonts = {'serif' : None, 'sans' : None, 'mono' : None}
|
||||||
def find_family(option):
|
def family(cmd):
|
||||||
path, family = option
|
return cmd.split(',')[-1].strip()
|
||||||
paths = findsystem.findFonts([path])
|
|
||||||
results = {}
|
|
||||||
for path in paths:
|
|
||||||
if len(results.keys()) == 4:
|
|
||||||
break
|
|
||||||
f = describe.openFont(path)
|
|
||||||
name, cfamily = describe.shortName(f)
|
|
||||||
if cfamily.lower().strip() != family.lower().strip():
|
|
||||||
continue
|
|
||||||
try:
|
|
||||||
wt, italic = describe.modifiers(f)
|
|
||||||
except TTLibError:
|
|
||||||
logger.exception('Could not process fonts in %s', path)
|
|
||||||
wt, italic = 0, 0
|
|
||||||
result = (path, name)
|
|
||||||
if wt == 400 and italic == 0:
|
|
||||||
results['normal'] = result
|
|
||||||
elif wt == 400 and italic > 0:
|
|
||||||
results['italic'] = result
|
|
||||||
elif wt >= 700 and italic == 0:
|
|
||||||
results['bold'] = result
|
|
||||||
elif wt >= 700 and italic > 0:
|
|
||||||
results['bi'] = result
|
|
||||||
return results
|
|
||||||
if options.serif_family:
|
if options.serif_family:
|
||||||
fonts['serif'] = find_family(options.serif_family)
|
f = family(options.serif_family)
|
||||||
|
fonts['serif'] = files_for_family(f)
|
||||||
if not fonts['serif']:
|
if not fonts['serif']:
|
||||||
logger.warn('Unable to find serif family %s in %s'%(options.serif_family[1].strip(), options.serif_family[0]))
|
logger.warn('Unable to find serif family %s'%f)
|
||||||
if options.sans_family:
|
if options.sans_family:
|
||||||
fonts['sans'] = find_family(options.sans_family)
|
f = family(options.sans_family)
|
||||||
|
fonts['sans'] = files_for_family(f)
|
||||||
if not fonts['sans']:
|
if not fonts['sans']:
|
||||||
logger.warn('Unable to find sans family %s in %s'%(options.sans_family[1].strip(), options.sans_family[0]))
|
logger.warn('Unable to find sans family %s'%f)
|
||||||
if options.mono_family:
|
if options.mono_family:
|
||||||
fonts['mono'] = find_family(options.mono_family)
|
f = family(options.mono_family)
|
||||||
|
fonts['mono'] = files_for_family(f)
|
||||||
if not fonts['mono']:
|
if not fonts['mono']:
|
||||||
logger.warn('Unable to find mono family %s in %s'%(options.mono_family[1].strip(), options.mono_family[0]))
|
logger.warn('Unable to find mono family %s'%f)
|
||||||
return fonts
|
return fonts
|
||||||
|
|
||||||
|
|
||||||
|
@ -0,0 +1,5 @@
|
|||||||
|
__all__ = ['LiberationMono_Bold', 'LiberationMono_Regular', 'LiberationSans_Bold',
|
||||||
|
'LiberationSans_Regular', 'LiberationSerif_Bold', 'LiberationSerif_Regular',
|
||||||
|
'LiberationMono_BoldItalic', 'LiberationMono_Italic',
|
||||||
|
'LiberationSans_BoldItalic', 'LiberationSans_Italic',
|
||||||
|
'LiberationSerif_BoldItalic', 'LiberationSerif_Italic']
|
@ -143,7 +143,7 @@ class LRFSingleDialog(QDialog, Ui_LRFSingleDialog):
|
|||||||
for opt in ('--serif-family', '--sans-family', '--mono-family'):
|
for opt in ('--serif-family', '--sans-family', '--mono-family'):
|
||||||
if opt in cmdline:
|
if opt in cmdline:
|
||||||
print 'in'
|
print 'in'
|
||||||
family = cmdline[cmdline.index(opt)+1].split(',')[1].strip()
|
family = cmdline[cmdline.index(opt)+1].split(',')[-1].strip()
|
||||||
obj = getattr(self, 'gui_'+opt[2:].replace('-', '_'))
|
obj = getattr(self, 'gui_'+opt[2:].replace('-', '_'))
|
||||||
try:
|
try:
|
||||||
obj.setCurrentIndex(self.font_family_model.index_of(family))
|
obj.setCurrentIndex(self.font_family_model.index_of(family))
|
||||||
@ -332,12 +332,8 @@ class LRFSingleDialog(QDialog, Ui_LRFSingleDialog):
|
|||||||
for opt in ('--serif-family', '--sans-family', '--mono-family'):
|
for opt in ('--serif-family', '--sans-family', '--mono-family'):
|
||||||
obj = getattr(self, 'gui_'+opt[2:].replace('-', '_'))
|
obj = getattr(self, 'gui_'+opt[2:].replace('-', '_'))
|
||||||
family = qstring_to_unicode(obj.itemText(obj.currentIndex())).strip()
|
family = qstring_to_unicode(obj.itemText(obj.currentIndex())).strip()
|
||||||
try:
|
if family != 'None':
|
||||||
path = self.font_family_model.path_of(family)
|
cmd.extend([opt, family])
|
||||||
except KeyError:
|
|
||||||
continue
|
|
||||||
if path:
|
|
||||||
cmd.extend([opt, os.path.dirname(path)+', '+family])
|
|
||||||
|
|
||||||
return cmd
|
return cmd
|
||||||
|
|
||||||
|
@ -48,6 +48,7 @@ entry_points = {
|
|||||||
'lrf2html = calibre.ebooks.lrf.html.convert_to:main',
|
'lrf2html = calibre.ebooks.lrf.html.convert_to: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',
|
||||||
],
|
],
|
||||||
'gui_scripts' : [
|
'gui_scripts' : [
|
||||||
__appname__+' = calibre.gui2.main:main',
|
__appname__+' = calibre.gui2.main:main',
|
||||||
|
@ -31,7 +31,6 @@ class Distribution(object):
|
|||||||
('libusb', '0.1.12', None, None, None),
|
('libusb', '0.1.12', None, None, None),
|
||||||
('Qt', '4.4.0', 'qt', 'libqt4-core libqt4-gui', 'qt4'),
|
('Qt', '4.4.0', 'qt', 'libqt4-core libqt4-gui', 'qt4'),
|
||||||
('PyQt', '4.4.2', 'PyQt4', 'python-qt4', 'PyQt4'),
|
('PyQt', '4.4.2', 'PyQt4', 'python-qt4', 'PyQt4'),
|
||||||
('fonttools', '2.0-beta1', 'fonttools', 'fonttools', 'fonttools'),
|
|
||||||
('mechanize for python', '0.1.7b', 'dev-python/mechanize', 'python-mechanize', 'python-mechanize'),
|
('mechanize for python', '0.1.7b', 'dev-python/mechanize', 'python-mechanize', 'python-mechanize'),
|
||||||
('ImageMagick', '6.3.5', 'imagemagick', 'imagemagick', 'ImageMagick'),
|
('ImageMagick', '6.3.5', 'imagemagick', 'imagemagick', 'ImageMagick'),
|
||||||
('xdg-utils', '1.0.2', 'xdg-utils', 'xdg-utils', 'xdg-utils'),
|
('xdg-utils', '1.0.2', 'xdg-utils', 'xdg-utils', 'xdg-utils'),
|
||||||
|
@ -34,7 +34,7 @@
|
|||||||
<div py:if="distro.is_generic">
|
<div py:if="distro.is_generic">
|
||||||
<ol>
|
<ol>
|
||||||
<li>Make sure that your system has <code>python >= 2.5</code></li>
|
<li>Make sure that your system has <code>python >= 2.5</code></li>
|
||||||
<li>Install the various dependencies listed below: Make sure that any python packages are installed into python2.5 (e.g. setuptools, python-imaging, PyQt4, fonttools, etc)</li>
|
<li>Install the various dependencies listed below: Make sure that any python packages are installed into python2.5 (e.g. setuptools, python-imaging, PyQt4, etc)</li>
|
||||||
<li>As root run the command <pre class="wiki">easy_install-2.5 -U TTFQuery calibre && calibre_postinstall</pre></li>
|
<li>As root run the command <pre class="wiki">easy_install-2.5 -U TTFQuery calibre && calibre_postinstall</pre></li>
|
||||||
</ol>
|
</ol>
|
||||||
<h2>Dependencies</h2>
|
<h2>Dependencies</h2>
|
||||||
|
@ -85,7 +85,6 @@ def main(args=sys.argv):
|
|||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
cwd = os.getcwd()
|
cwd = os.getcwd()
|
||||||
sys.path.insert(0, os.path.dirname(os.path.dirname(cwd)))
|
sys.path.insert(0, os.path.dirname(os.path.dirname(cwd)))
|
||||||
print sys.path[0]
|
|
||||||
|
|
||||||
sys.exit(main())
|
sys.exit(main())
|
||||||
|
|
||||||
|
320
src/calibre/utils/fontconfig.py
Normal file
320
src/calibre/utils/fontconfig.py
Normal file
@ -0,0 +1,320 @@
|
|||||||
|
#!/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
|
||||||
|
from ctypes import cdll, c_void_p, Structure, c_int, POINTER, c_ubyte, c_char, \
|
||||||
|
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
|
||||||
|
|
||||||
|
def load_library():
|
||||||
|
if isosx:
|
||||||
|
lib = 'libfontconfig.1.dylib'
|
||||||
|
if hasattr(sys, 'frameworks_dir'):
|
||||||
|
lib = os.path.join(getattr(sys, 'frameworks_dir'), lib)
|
||||||
|
return cdll.LoadLibrary(lib)
|
||||||
|
elif iswindows:
|
||||||
|
return cdll.LoadLibrary('libfontconfig-1')
|
||||||
|
else:
|
||||||
|
return cdll.LoadLibrary('libfontconfig.so')
|
||||||
|
|
||||||
|
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)
|
||||||
|
]
|
||||||
|
|
||||||
|
lib = load_library()
|
||||||
|
lib.FcPatternCreate.restype = c_void_p
|
||||||
|
lib.FcObjectSetCreate.restype = c_void_p
|
||||||
|
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)
|
||||||
|
|
||||||
|
|
||||||
|
if not lib.FcInit():
|
||||||
|
raise RuntimeError(_('Could not initialize the fontconfig library'))
|
||||||
|
|
||||||
|
|
||||||
|
def find_font_families(allowed_extensions=['ttf']):
|
||||||
|
'''
|
||||||
|
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']`. If it is empty, it is ignored.
|
||||||
|
'''
|
||||||
|
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 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 isinstance(family, unicode):
|
||||||
|
family = family.encode(preferred_encoding)
|
||||||
|
family_pattern = lib.FcPatternBuild(0, 'family', FcTypeString, family, 0)
|
||||||
|
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(chr(0), 5000))
|
||||||
|
full_name = pointer(create_string_buffer(chr(0), 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 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())
|
@ -46,6 +46,7 @@ BrandingText "${PRODUCT_NAME} created by Kovid Goyal"
|
|||||||
!define CLIT "C:\clit\clit.exe"
|
!define CLIT "C:\clit\clit.exe"
|
||||||
!define PDFTOHTML "C:\pdftohtml\pdftohtml.exe"
|
!define PDFTOHTML "C:\pdftohtml\pdftohtml.exe"
|
||||||
!define IMAGEMAGICK "C:\ImageMagick"
|
!define IMAGEMAGICK "C:\ImageMagick"
|
||||||
|
!DEFINE FONTCONFIG "C:\fontconfig"
|
||||||
|
|
||||||
|
|
||||||
; ---------------PATH manipulation -----------------------------------------------------------------
|
; ---------------PATH manipulation -----------------------------------------------------------------
|
||||||
@ -283,6 +284,7 @@ Section "Main" "secmain"
|
|||||||
File /r "${PY2EXE_DIR}\*"
|
File /r "${PY2EXE_DIR}\*"
|
||||||
File "${CLIT}"
|
File "${CLIT}"
|
||||||
File "${PDFTOHTML}"
|
File "${PDFTOHTML}"
|
||||||
|
File /r "${FONTCONFIG}\*"
|
||||||
|
|
||||||
SetOutPath "$INSTDIR\ImageMagick"
|
SetOutPath "$INSTDIR\ImageMagick"
|
||||||
File /r "${IMAGEMAGICK}\*"
|
File /r "${IMAGEMAGICK}\*"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user