Edit Book: Make embedding fonts using the manage fonts dialog easier. You can now double click on a font family to see what faces for that family area available on your computer and install new fonts directly from the dialog.

This commit is contained in:
Kovid Goyal 2015-04-11 09:35:18 +05:30
parent cbfd5f6e38
commit 8ec93d806f
2 changed files with 115 additions and 34 deletions

View File

@ -18,6 +18,34 @@ from PyQt5.Qt import (QFontInfo, QFontMetrics, Qt, QFont, QFontDatabase, QPen,
from calibre.constants import config_dir
from calibre.gui2 import choose_files, 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())
@ -136,8 +164,8 @@ class Typefaces(QLabel):
'''%(_('Available faces for %s')%family)
entries = []
for font in faces:
sf = (font['wws_subfamily_name'] or font['preferred_subfamily_name']
or font['subfamily_name'])
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:
@ -261,29 +289,9 @@ class FontFamilyDialog(QDialog):
self.m.setStringList(self.families)
def add_fonts(self):
from calibre.utils.fonts.metadata import FontMetadata
files = choose_files(self, 'add fonts to calibre',
_('Select font files'), filters=[(_('TrueType/OpenType Fonts'),
['ttf', 'otf'])], all_files=False)
if not files:
families = add_fonts(self)
if not families:
return
families = set()
for f in files:
try:
with open(f, 'rb') as stream:
fm = FontMetadata(stream)
except:
import traceback
error_dialog(self, _('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)))
self.font_scanner.do_scan()
self.m.beginResetModel()
self.build_font_list()

View File

@ -6,21 +6,51 @@ from __future__ import (unicode_literals, division, absolute_import,
__license__ = 'GPL v3'
__copyright__ = '2014, Kovid Goyal <kovid at kovidgoyal.net>'
import sys
import sys, textwrap
from PyQt5.Qt import (
QSplitter, QVBoxLayout, QTableView, QWidget, QLabel, QAbstractTableModel,
Qt, QApplication, QTimer, QPushButton, pyqtSignal, QFormLayout, QLineEdit,
QIcon, QSize)
Qt, QTimer, QPushButton, pyqtSignal, QFormLayout, QLineEdit, QIcon, QSize,
QHBoxLayout, QTextEdit)
from calibre.ebooks.oeb.polish.container import get_container
from calibre.ebooks.oeb.polish.fonts import font_family_data, change_font
from calibre.gui2 import error_dialog
from calibre.gui2 import error_dialog, info_dialog
from calibre.gui2.tweak_book import current_container, set_current_container
from calibre.gui2.tweak_book.widgets import Dialog, BusyCursor
from calibre.utils.icu import primary_sort_key as sort_key
from calibre.utils.fonts.scanner import font_scanner, NoFonts
class EmbeddingData(Dialog):
def __init__(self, family, faces, parent=None):
Dialog.__init__(self, _('Font faces for %s') % family, 'editor-embedding-data', parent)
self.family, self.faces = family, faces
self.populate_text()
def sizeHint(self):
return QSize(600, 500)
def setup_ui(self):
self.l = l = QVBoxLayout(self)
self.text = t = QTextEdit(self)
t.setReadOnly(True)
l.addWidget(t), l.addWidget(self.bb)
self.bb.clear(), self.bb.setStandardButtons(self.bb.Close)
def populate_text(self):
text = ['<h2>' + self.windowTitle() + '</h2><ul>']
for face in self.faces:
text.append('<li style="margin-bottom:2em">' + _('Path to font file:') + '\xa0\xa0' + '<b>' + face['path'] + '</b>')
name = face.get('full_name') or face.get('family_name') or face.get('subfamily_name')
if name:
text.append('<br>' + _('Name:') + '\xa0<b>' + type('')(name) + '</b>')
if 'font-weight' in face:
text.append('<br>' + 'font-weight:\xa0' + type('')(face['font-weight']))
if 'font-style' in face:
text.append('<br>' + 'font-style:\xa0' + type('')(face['font-style']))
self.text.setHtml('\n'.join(text))
class AllFonts(QAbstractTableModel):
def __init__(self, parent=None):
@ -65,7 +95,20 @@ class AllFonts(QAbstractTableModel):
if role == Qt.TextAlignmentRole:
col = index.column()
if col == 0:
return Qt.AlignHCenter
return Qt.AlignHCenter | Qt.AlignVCenter
if role in (Qt.UserRole, Qt.UserRole + 1):
row = index.row()
try:
name = self.items[row]
except (IndexError, KeyError):
return
if role == Qt.UserRole:
try:
return font_scanner.fonts_for_family(name)
except NoFonts:
return []
else:
return name
def sort(self, col, order=Qt.AscendingOrder):
sorted_on = (('name' if col == 1 else 'embedded'), order == Qt.AscendingOrder)
@ -152,9 +195,16 @@ class ManageFonts(Dialog):
self.bb.clear()
self.bb.addButton(self.bb.Close)
self.splitter = s = QSplitter(self)
l.addWidget(s), l.addWidget(self.bb)
self.h = h = QHBoxLayout()
h.setContentsMargins(0, 0, 0, 0)
self.install_fonts_button = b = QPushButton(_('&Install fonts'), self)
h.addWidget(b), b.setIcon(QIcon(I('plus.png')))
b.setToolTip(textwrap.fill(_('Install fonts from .ttf/.otf files to make them available for embedding')))
b.clicked.connect(self.install_fonts)
l.addWidget(s), l.addLayout(h), h.addStretch(10), h.addWidget(self.bb)
self.fonts_view = fv = QTableView(self)
fv.doubleClicked.connect(self.show_embedding_data)
self.model = m = AllFonts(fv)
fv.horizontalHeader().setStretchLastSection(True)
fv.setModel(m)
@ -190,14 +240,36 @@ class ManageFonts(Dialog):
b.setIcon(QIcon(I('view-refresh.png')))
b.clicked.connect(self.refresh)
self.la = la = QLabel('<p>' + _(
''' All the fonts declared in this book are shown to the left, along with whether they are embedded or not.
You can remove or replace any selected font and also embed any declared fonts that are not already embedded.'''))
self.la = la = QLabel(
'<p>' + _(
''' All the fonts declared in this book are shown to the left, along with whether they are embedded or not.
You can remove or replace any selected font and also embed any declared fonts that are not already embedded.''') + '<p>' + _(
''' Double click any font family to see if the font is available for embedding on your computer. ''')
)
la.setWordWrap(True)
l.addWidget(la)
l.setAlignment(Qt.AlignTop | Qt.AlignHCenter)
def show_embedding_data(self, index):
faces = index.data(Qt.UserRole)
family = index.data(Qt.UserRole + 1)
if not faces:
return error_dialog(self, _('Not found'), _(
'The font <b>%s</b> was not found on your computer. If you have the font files,'
' you can install it using the "Install fonts" button in the lower left corner.'
) % family, show=True)
EmbeddingData(family, faces, self).exec_()
def install_fonts(self):
from calibre.gui2.font_family_chooser import add_fonts
families = add_fonts(self)
if not families:
return
font_scanner.do_scan()
self.refresh()
info_dialog(self, _('Added fonts'), _('Added font families: %s')%(', '.join(families)), show=True)
def sizeHint(self):
return Dialog.sizeHint(self) + QSize(100, 50)
@ -250,7 +322,8 @@ class ManageFonts(Dialog):
self.model.build()
if __name__ == '__main__':
app = QApplication([])
from calibre.gui2 import Application
app = Application([])
c = get_container(sys.argv[-1], tweak_mode=True)
set_current_container(c)
d = ManageFonts()