%PDF- %PDF-
| Direktori : /lib/calibre/calibre/gui2/ |
| Current File : //lib/calibre/calibre/gui2/font_family_chooser.py |
#!/usr/bin/env python3
__license__ = 'GPL v3'
__copyright__ = '2012, Kovid Goyal <kovid at kovidgoyal.net>'
__docformat__ = 'restructuredtext en'
import os
import shutil
from qt.core import (
QAbstractItemView, QDialog, QDialogButtonBox, QFont, QFontComboBox,
QFontDatabase, QFontInfo, QFontMetrics, QGridLayout, QHBoxLayout, QIcon, QLabel,
QLineEdit, QListView, QPen, QPushButton, QSize, QSizePolicy, QStringListModel,
QStyle, QStyledItemDelegate, Qt, QToolButton, QVBoxLayout, QWidget, pyqtSignal
)
from calibre.constants import config_dir
from calibre.gui2 import choose_files, empty_index, error_dialog, info_dialog
def add_fonts(parent):
from calibre.utils.fonts.metadata import FontMetadata
files = choose_files(parent, 'add fonts to calibre',
_('Select font files'), filters=[(_('TrueType/OpenType Fonts'),
['ttf', 'otf'])], all_files=False)
if not files:
return
families = set()
for f in files:
try:
with open(f, 'rb') as stream:
fm = FontMetadata(stream)
except:
import traceback
error_dialog(parent, _('Corrupt font'),
_('Failed to read metadata from the font file: %s')%
f, det_msg=traceback.format_exc(), show=True)
return
families.add(fm.font_family)
families = sorted(families)
dest = os.path.join(config_dir, 'fonts')
for f in files:
shutil.copyfile(f, os.path.join(dest, os.path.basename(f)))
return families
def writing_system_for_font(font):
has_latin = True
systems = QFontDatabase().writingSystems(font.family())
# this just confuses the algorithm below. Vietnamese is Latin with lots of
# special chars
try:
systems.remove(QFontDatabase.WritingSystem.Vietnamese)
except ValueError:
pass
system = QFontDatabase.WritingSystem.Any
if (QFontDatabase.WritingSystem.Latin not in systems):
has_latin = False
# we need to show something
if systems:
system = systems[-1]
else:
systems.remove(QFontDatabase.WritingSystem.Latin)
if not systems:
return system, has_latin
if (len(systems) == 1 and systems[0] > QFontDatabase.WritingSystem.Cyrillic):
return systems[0], has_latin
if (len(systems) <= 2 and
systems[-1] > QFontDatabase.WritingSystem.Armenian and
systems[-1] < QFontDatabase.WritingSystem.Vietnamese):
return systems[-1], has_latin
if (len(systems) <= 5 and
systems[-1] >= QFontDatabase.WritingSystem.SimplifiedChinese and
systems[-1] <= QFontDatabase.WritingSystem.Korean):
system = systems[-1]
return system, has_latin
class FontFamilyDelegate(QStyledItemDelegate):
def sizeHint(self, option, index):
try:
return self.do_size_hint(option, index)
except:
return QSize(300, 50)
def do_size_hint(self, option, index):
text = index.data(Qt.ItemDataRole.DisplayRole) or ''
font = QFont(option.font)
font.setPointSizeF(QFontInfo(font).pointSize() * 1.5)
m = QFontMetrics(font)
return QSize(m.width(text), m.height())
def paint(self, painter, option, index):
QStyledItemDelegate.paint(self, painter, option, empty_index)
painter.save()
try:
self.do_paint(painter, option, index)
except:
import traceback
traceback.print_exc()
painter.restore()
def do_paint(self, painter, option, index):
text = str(index.data(Qt.ItemDataRole.DisplayRole) or '')
font = QFont(option.font)
font.setPointSizeF(QFontInfo(font).pointSize() * 1.5)
font2 = QFont(font)
font2.setFamily(text)
system, has_latin = writing_system_for_font(font2)
if has_latin:
font = font2
r = option.rect
color = option.palette.text()
if option.state & QStyle.StateFlag.State_Selected:
color = option.palette.highlightedText()
painter.setPen(QPen(color, 0))
if (option.direction == Qt.LayoutDirection.RightToLeft):
r.setRight(r.right() - 4)
else:
r.setLeft(r.left() + 4)
painter.setFont(font)
painter.drawText(r, Qt.AlignmentFlag.AlignVCenter|Qt.AlignmentFlag.AlignLeading|Qt.TextFlag.TextSingleLine, text)
if (system != QFontDatabase.WritingSystem.Any):
w = painter.fontMetrics().width(text + " ")
painter.setFont(font2)
sample = QFontDatabase().writingSystemSample(system)
if (option.direction == Qt.LayoutDirection.RightToLeft):
r.setRight(r.right() - w)
else:
r.setLeft(r.left() + w)
painter.drawText(r, Qt.AlignmentFlag.AlignVCenter|Qt.AlignmentFlag.AlignLeading|Qt.TextFlag.TextSingleLine, sample)
class Typefaces(QLabel):
def __init__(self, parent=None):
QLabel.__init__(self, parent)
self.setMinimumWidth(400)
self.base_msg = '<h3>'+_('Choose a font family')+'</h3>'
self.setText(self.base_msg)
self.setWordWrap(True)
def show_family(self, family, faces):
if not family:
self.setText(self.base_msg)
return
msg = '''
<h3>%s</h3>
<dl style="font-size: smaller">
{0}
</dl>
'''%(_('Available faces for %s')%family)
entries = []
for font in faces:
sf = (font['wws_subfamily_name'] or font['preferred_subfamily_name'] or
font['subfamily_name'])
entries.append('''
<dt><b>{sf}</b></dt>
<dd>font-stretch: <i>{width}</i> font-weight: <i>{weight}</i> font-style:
<i>{style}</i></dd>
'''.format(sf=sf, width=font['font-stretch'],
weight=font['font-weight'], style=font['font-style']))
msg = msg.format('\n\n'.join(entries))
self.setText(msg)
class FontsView(QListView):
changed = pyqtSignal()
def __init__(self, parent):
QListView.__init__(self, parent)
self.setSelectionMode(QAbstractItemView.SelectionMode.SingleSelection)
self.setAlternatingRowColors(True)
self.d = FontFamilyDelegate(self)
self.setItemDelegate(self.d)
def currentChanged(self, current, previous):
self.changed.emit()
QListView.currentChanged(self, current, previous)
class FontFamilyDialog(QDialog):
def __init__(self, current_family, parent=None):
QDialog.__init__(self, parent)
self.setWindowTitle(_('Choose font family'))
self.setWindowIcon(QIcon(I('font.png')))
from calibre.utils.fonts.scanner import font_scanner
self.font_scanner = font_scanner
self.m = QStringListModel(self)
self.build_font_list()
self.l = l = QGridLayout()
self.setLayout(l)
self.view = FontsView(self)
self.view.setModel(self.m)
self.view.setCurrentIndex(self.m.index(0))
if current_family:
for i, val in enumerate(self.families):
if icu_lower(val) == icu_lower(current_family):
self.view.setCurrentIndex(self.m.index(i))
break
self.view.doubleClicked.connect(self.accept, type=Qt.ConnectionType.QueuedConnection)
self.view.changed.connect(self.current_changed,
type=Qt.ConnectionType.QueuedConnection)
self.faces = Typefaces(self)
self.bb = QDialogButtonBox(QDialogButtonBox.StandardButton.Ok|QDialogButtonBox.StandardButton.Cancel)
self.bb.accepted.connect(self.accept)
self.bb.rejected.connect(self.reject)
self.add_fonts_button = afb = self.bb.addButton(_('Add &fonts'),
QDialogButtonBox.ButtonRole.ActionRole)
afb.setIcon(QIcon(I('plus.png')))
afb.clicked.connect(self.add_fonts)
self.ml = QLabel(_('Choose a font family from the list below:'))
self.search = QLineEdit(self)
self.search.setPlaceholderText(_('Search'))
self.search.returnPressed.connect(self.find)
self.nb = QToolButton(self)
self.nb.setIcon(QIcon(I('arrow-down.png')))
self.nb.setToolTip(_('Find next'))
self.pb = QToolButton(self)
self.pb.setIcon(QIcon(I('arrow-up.png')))
self.pb.setToolTip(_('Find previous'))
self.nb.clicked.connect(self.find_next)
self.pb.clicked.connect(self.find_previous)
l.addWidget(self.ml, 0, 0, 1, 4)
l.addWidget(self.search, 1, 0, 1, 1)
l.addWidget(self.nb, 1, 1, 1, 1)
l.addWidget(self.pb, 1, 2, 1, 1)
l.addWidget(self.view, 2, 0, 1, 3)
l.addWidget(self.faces, 1, 3, 2, 1)
l.addWidget(self.bb, 3, 0, 1, 4)
l.setAlignment(self.faces, Qt.AlignmentFlag.AlignTop)
self.resize(800, 600)
def set_current(self, i):
self.view.setCurrentIndex(self.m.index(i))
def keyPressEvent(self, e):
if e.key() == Qt.Key.Key_Return:
return
return QDialog.keyPressEvent(self, e)
def find(self, backwards=False):
i = self.view.currentIndex().row()
if i < 0:
i = 0
q = icu_lower(str(self.search.text())).strip()
if not q:
return
r = (range(i-1, -1, -1) if backwards else range(i+1,
len(self.families)))
for j in r:
f = self.families[j]
if q in icu_lower(f):
self.set_current(j)
return
def find_next(self):
self.find()
def find_previous(self):
self.find(backwards=True)
def build_font_list(self):
try:
self.families = list(self.font_scanner.find_font_families())
except:
self.families = []
print('WARNING: Could not load fonts')
import traceback
traceback.print_exc()
self.families.insert(0, _('None'))
self.m.setStringList(self.families)
def add_fonts(self):
families = add_fonts(self)
if not families:
return
self.font_scanner.do_scan()
self.m.beginResetModel()
self.build_font_list()
self.m.endResetModel()
self.view.setCurrentIndex(self.m.index(0))
if families:
for i, val in enumerate(self.families):
if icu_lower(val) == icu_lower(families[0]):
self.view.setCurrentIndex(self.m.index(i))
break
info_dialog(self, _('Added fonts'),
_('Added font families: %s')%(
', '.join(families)), show=True)
@property
def font_family(self):
idx = self.view.currentIndex().row()
if idx == 0:
return None
return self.families[idx]
def current_changed(self):
fam = self.font_family
self.faces.show_family(fam, self.font_scanner.fonts_for_family(fam)
if fam else None)
class FontFamilyChooser(QWidget):
family_changed = pyqtSignal(object)
def __init__(self, parent=None):
QWidget.__init__(self, parent)
self.l = l = QHBoxLayout()
l.setContentsMargins(0, 0, 0, 0)
self.setLayout(l)
self.button = QPushButton(self)
self.button.setIcon(QIcon(I('font.png')))
self.button.setSizePolicy(QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Fixed)
l.addWidget(self.button)
self.default_text = _('Choose &font family')
self.font_family = None
self.button.clicked.connect(self.show_chooser)
self.clear_button = QToolButton(self)
self.clear_button.setIcon(QIcon(I('clear_left.png')))
self.clear_button.clicked.connect(self.clear_family)
l.addWidget(self.clear_button)
self.setToolTip = self.button.setToolTip
self.toolTip = self.button.toolTip
self.clear_button.setToolTip(_('Clear the font family'))
l.addStretch(1)
def clear_family(self):
self.font_family = None
@property
def font_family(self):
return self._current_family
@font_family.setter
def font_family(self, val):
if not val:
val = None
self._current_family = val
self.button.setText(val or self.default_text)
self.family_changed.emit(val)
def show_chooser(self):
d = FontFamilyDialog(self.font_family, self)
if d.exec() == QDialog.DialogCode.Accepted:
self.font_family = d.font_family
def test():
from calibre.gui2 import Application
app = Application([])
app
d = QDialog()
d.setLayout(QVBoxLayout())
d.layout().addWidget(FontFamilyChooser(d))
d.layout().addWidget(QFontComboBox(d))
d.exec()
if __name__ == '__main__':
test()