mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
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:
parent
cbfd5f6e38
commit
8ec93d806f
@ -18,6 +18,34 @@ from PyQt5.Qt import (QFontInfo, QFontMetrics, Qt, QFont, QFontDatabase, QPen,
|
|||||||
from calibre.constants import config_dir
|
from calibre.constants import config_dir
|
||||||
from calibre.gui2 import choose_files, error_dialog, info_dialog
|
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):
|
def writing_system_for_font(font):
|
||||||
has_latin = True
|
has_latin = True
|
||||||
systems = QFontDatabase().writingSystems(font.family())
|
systems = QFontDatabase().writingSystems(font.family())
|
||||||
@ -136,8 +164,8 @@ class Typefaces(QLabel):
|
|||||||
'''%(_('Available faces for %s')%family)
|
'''%(_('Available faces for %s')%family)
|
||||||
entries = []
|
entries = []
|
||||||
for font in faces:
|
for font in faces:
|
||||||
sf = (font['wws_subfamily_name'] or font['preferred_subfamily_name']
|
sf = (font['wws_subfamily_name'] or font['preferred_subfamily_name'] or
|
||||||
or font['subfamily_name'])
|
font['subfamily_name'])
|
||||||
entries.append('''
|
entries.append('''
|
||||||
<dt><b>{sf}</b></dt>
|
<dt><b>{sf}</b></dt>
|
||||||
<dd>font-stretch: <i>{width}</i> font-weight: <i>{weight}</i> font-style:
|
<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)
|
self.m.setStringList(self.families)
|
||||||
|
|
||||||
def add_fonts(self):
|
def add_fonts(self):
|
||||||
from calibre.utils.fonts.metadata import FontMetadata
|
families = add_fonts(self)
|
||||||
files = choose_files(self, 'add fonts to calibre',
|
if not families:
|
||||||
_('Select font files'), filters=[(_('TrueType/OpenType Fonts'),
|
|
||||||
['ttf', 'otf'])], all_files=False)
|
|
||||||
if not files:
|
|
||||||
return
|
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.font_scanner.do_scan()
|
||||||
self.m.beginResetModel()
|
self.m.beginResetModel()
|
||||||
self.build_font_list()
|
self.build_font_list()
|
||||||
|
@ -6,21 +6,51 @@ from __future__ import (unicode_literals, division, absolute_import,
|
|||||||
__license__ = 'GPL v3'
|
__license__ = 'GPL v3'
|
||||||
__copyright__ = '2014, Kovid Goyal <kovid at kovidgoyal.net>'
|
__copyright__ = '2014, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||||
|
|
||||||
import sys
|
import sys, textwrap
|
||||||
|
|
||||||
from PyQt5.Qt import (
|
from PyQt5.Qt import (
|
||||||
QSplitter, QVBoxLayout, QTableView, QWidget, QLabel, QAbstractTableModel,
|
QSplitter, QVBoxLayout, QTableView, QWidget, QLabel, QAbstractTableModel,
|
||||||
Qt, QApplication, QTimer, QPushButton, pyqtSignal, QFormLayout, QLineEdit,
|
Qt, QTimer, QPushButton, pyqtSignal, QFormLayout, QLineEdit, QIcon, QSize,
|
||||||
QIcon, QSize)
|
QHBoxLayout, QTextEdit)
|
||||||
|
|
||||||
from calibre.ebooks.oeb.polish.container import get_container
|
from calibre.ebooks.oeb.polish.container import get_container
|
||||||
from calibre.ebooks.oeb.polish.fonts import font_family_data, change_font
|
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 import current_container, set_current_container
|
||||||
from calibre.gui2.tweak_book.widgets import Dialog, BusyCursor
|
from calibre.gui2.tweak_book.widgets import Dialog, BusyCursor
|
||||||
from calibre.utils.icu import primary_sort_key as sort_key
|
from calibre.utils.icu import primary_sort_key as sort_key
|
||||||
from calibre.utils.fonts.scanner import font_scanner, NoFonts
|
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):
|
class AllFonts(QAbstractTableModel):
|
||||||
|
|
||||||
def __init__(self, parent=None):
|
def __init__(self, parent=None):
|
||||||
@ -65,7 +95,20 @@ class AllFonts(QAbstractTableModel):
|
|||||||
if role == Qt.TextAlignmentRole:
|
if role == Qt.TextAlignmentRole:
|
||||||
col = index.column()
|
col = index.column()
|
||||||
if col == 0:
|
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):
|
def sort(self, col, order=Qt.AscendingOrder):
|
||||||
sorted_on = (('name' if col == 1 else 'embedded'), 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.clear()
|
||||||
self.bb.addButton(self.bb.Close)
|
self.bb.addButton(self.bb.Close)
|
||||||
self.splitter = s = QSplitter(self)
|
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)
|
self.fonts_view = fv = QTableView(self)
|
||||||
|
fv.doubleClicked.connect(self.show_embedding_data)
|
||||||
self.model = m = AllFonts(fv)
|
self.model = m = AllFonts(fv)
|
||||||
fv.horizontalHeader().setStretchLastSection(True)
|
fv.horizontalHeader().setStretchLastSection(True)
|
||||||
fv.setModel(m)
|
fv.setModel(m)
|
||||||
@ -190,14 +240,36 @@ class ManageFonts(Dialog):
|
|||||||
b.setIcon(QIcon(I('view-refresh.png')))
|
b.setIcon(QIcon(I('view-refresh.png')))
|
||||||
b.clicked.connect(self.refresh)
|
b.clicked.connect(self.refresh)
|
||||||
|
|
||||||
self.la = la = QLabel('<p>' + _(
|
self.la = la = QLabel(
|
||||||
''' All the fonts declared in this book are shown to the left, along with whether they are embedded or not.
|
'<p>' + _(
|
||||||
You can remove or replace any selected font and also embed any declared fonts that are not already embedded.'''))
|
''' 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)
|
la.setWordWrap(True)
|
||||||
l.addWidget(la)
|
l.addWidget(la)
|
||||||
|
|
||||||
l.setAlignment(Qt.AlignTop | Qt.AlignHCenter)
|
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):
|
def sizeHint(self):
|
||||||
return Dialog.sizeHint(self) + QSize(100, 50)
|
return Dialog.sizeHint(self) + QSize(100, 50)
|
||||||
|
|
||||||
@ -250,7 +322,8 @@ class ManageFonts(Dialog):
|
|||||||
self.model.build()
|
self.model.build()
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
app = QApplication([])
|
from calibre.gui2 import Application
|
||||||
|
app = Application([])
|
||||||
c = get_container(sys.argv[-1], tweak_mode=True)
|
c = get_container(sys.argv[-1], tweak_mode=True)
|
||||||
set_current_container(c)
|
set_current_container(c)
|
||||||
d = ManageFonts()
|
d = ManageFonts()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user