Remove the workarounds for the Qt WebKit embedded font bug as the bug has been fixed in Qt 5

This commit is contained in:
Kovid Goyal 2014-04-22 15:02:29 +05:30
parent d66702e77a
commit 7d0a047026
5 changed files with 7 additions and 175 deletions

View File

@ -75,8 +75,7 @@ class EbookIterator(BookmarksMixin):
return i return i
def __enter__(self, processed=False, only_input_plugin=False, def __enter__(self, processed=False, only_input_plugin=False,
run_char_count=True, read_anchor_map=True, view_kepub=False, run_char_count=True, read_anchor_map=True, view_kepub=False):
extract_embedded_fonts_for_qt=False):
''' Convert an ebook file into an exploded OEB book suitable for ''' Convert an ebook file into an exploded OEB book suitable for
display in viewers/preprocessing etc. ''' display in viewers/preprocessing etc. '''
@ -178,16 +177,6 @@ class EbookIterator(BookmarksMixin):
self.read_bookmarks() self.read_bookmarks()
if extract_embedded_fonts_for_qt:
from calibre.ebooks.oeb.iterator.extract_fonts import extract_fonts
try:
extract_fonts(self.opf, self.log)
except:
ol = self.log.filter_level
self.log.filter_level = self.log.DEBUG
self.log.exception('Failed to extract fonts')
self.log.filter_level = ol
return self return self
def __exit__(self, *args): def __exit__(self, *args):

View File

@ -1,115 +0,0 @@
#!/usr/bin/env python
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:fdm=marker:ai
from __future__ import (unicode_literals, division, absolute_import,
print_function)
__license__ = 'GPL v3'
__copyright__ = '2012, Kovid Goyal <kovid at kovidgoyal.net>'
__docformat__ = 'restructuredtext en'
import re, os, logging
from functools import partial
from future_builtins import map
class FamilyMap(dict):
def __init__(self, log):
dict.__init__(self)
self.replace_map = {}
self.added_fonts = set()
self.log = log
def __call__(self, basedir, match):
self.read_font_fule(basedir, match.group())
return b''
def finalize(self):
if self.replace_map:
self.pat = re.compile(br'(font-family.*?)(' +
b'|'.join([re.escape(x) for x in
self.replace_map.iterkeys()])+b')', re.I)
def replace_font_families(self, raw):
if self.replace_map:
def sub(m):
k = m.group(2).lower()
for q, val in self.replace_map.iteritems():
if q.lower() == k.lower():
return m.group().replace(m.group(2), val)
return m.group()
return self.pat.sub(sub, raw)
def read_font_fule(self, basedir, css):
from PyQt5.Qt import QFontDatabase
import cssutils
cssutils.log.setLevel(logging.ERROR)
try:
sheet = cssutils.parseString(css, validate=False)
except:
return
for rule in sheet.cssRules:
try:
s = rule.style
src = s.getProperty('src').propertyValue[0].uri
font_family = s.getProperty('font-family').propertyValue[0].value
except:
continue
if not src or not font_family:
continue
font_file = os.path.normcase(os.path.abspath(os.path.join(basedir,
src)))
if font_file not in self.added_fonts:
self.added_fonts.add(font_file)
if not os.path.exists(font_file):
from calibre.ebooks.oeb.base import urlunquote
ff = urlunquote(font_file, error_handling='replace')
if os.path.exists(ff):
font_file = ff
if os.path.exists(font_file):
with open(font_file, 'rb') as f:
idx = QFontDatabase.addApplicationFontFromData(f.read())
if idx > -1:
family = map(unicode,
QFontDatabase.applicationFontFamilies(idx)).next()
self.log('Extracted embedded font:', family, 'from',
os.path.basename(font_file))
if (family and family != font_family and
family not in self.replace_map):
self.log('Replacing font family value:',
font_family, 'with', family)
self.replace_map[font_family.encode('utf-8')] = \
family.encode('utf-8')
def extract_fonts(opf, log):
'''
Extract embedded fonts from the ebook and add them explicitly to the Qt
font database to workaround https://bugs.webkit.org/show_bug.cgi?id=29433
Only works if the font-face and font-family rules are all contained in the
CSS files (Also processing the HTML files would be too much of a
performance hit, to do robustly).
'''
css_files = {}
font_family_map = FamilyMap(log)
pat = re.compile(br'^\s*@font-face\s*{[^}]+}', re.M)
for item in opf.manifest:
if item.mime_type and item.mime_type.lower() in {
'text/css', 'text/x-oeb1-css', 'text/x-oeb-css'}:
try:
with open(item.path, 'rb') as f:
raw = f.read()
except EnvironmentError:
continue
css_files[item.path] = pat.sub(partial(font_family_map,
os.path.dirname(item.path)), raw)
font_family_map.finalize()
if font_family_map.added_fonts:
for path, raw in css_files.iteritems():
with open(path, 'wb') as f:
nraw = font_family_map.replace_font_families(raw) or raw
f.write(nraw)

View File

@ -18,7 +18,7 @@ from calibre import prints, isbytestring
from calibre.ptempfile import PersistentTemporaryDirectory, TemporaryDirectory from calibre.ptempfile import PersistentTemporaryDirectory, TemporaryDirectory
from calibre.ebooks.oeb.base import urlnormalize from calibre.ebooks.oeb.base import urlnormalize
from calibre.ebooks.oeb.polish.main import SUPPORTED, tweak_polish from calibre.ebooks.oeb.polish.main import SUPPORTED, tweak_polish
from calibre.ebooks.oeb.polish.container import get_container as _gc, clone_container, guess_type, OEB_FONTS, OEB_DOCS, OEB_STYLES from calibre.ebooks.oeb.polish.container import get_container as _gc, clone_container, guess_type, OEB_DOCS, OEB_STYLES
from calibre.ebooks.oeb.polish.cover import mark_as_cover, mark_as_titlepage, set_cover from calibre.ebooks.oeb.polish.cover import mark_as_cover, mark_as_titlepage, set_cover
from calibre.ebooks.oeb.polish.css import filter_css from calibre.ebooks.oeb.polish.css import filter_css
from calibre.ebooks.oeb.polish.pretty import fix_all_html, pretty_all from calibre.ebooks.oeb.polish.pretty import fix_all_html, pretty_all
@ -34,7 +34,7 @@ from calibre.gui2.tweak_book import (
from calibre.gui2.tweak_book.undo import GlobalUndoHistory from calibre.gui2.tweak_book.undo import GlobalUndoHistory
from calibre.gui2.tweak_book.file_list import NewFileDialog from calibre.gui2.tweak_book.file_list import NewFileDialog
from calibre.gui2.tweak_book.save import SaveManager, save_container, find_first_existing_ancestor from calibre.gui2.tweak_book.save import SaveManager, save_container, find_first_existing_ancestor
from calibre.gui2.tweak_book.preview import parse_worker, font_cache from calibre.gui2.tweak_book.preview import parse_worker
from calibre.gui2.tweak_book.toc import TOCEditor from calibre.gui2.tweak_book.toc import TOCEditor
from calibre.gui2.tweak_book.editor import editor_from_syntax, syntax_from_mime from calibre.gui2.tweak_book.editor import editor_from_syntax, syntax_from_mime
from calibre.gui2.tweak_book.editor.insert_resource import get_resource_data, NewBook from calibre.gui2.tweak_book.editor.insert_resource import get_resource_data, NewBook
@ -50,14 +50,6 @@ _diff_dialogs = []
def get_container(*args, **kwargs): def get_container(*args, **kwargs):
kwargs['tweak_mode'] = True kwargs['tweak_mode'] = True
container = _gc(*args, **kwargs) container = _gc(*args, **kwargs)
# We preload the embedded fonts from this book, so that the preview panel
# works
font_cache.remove_fonts()
for name, mt in container.mime_map.iteritems():
if mt in OEB_FONTS and container.exists(name):
with container.open(name, 'rb') as f:
raw = f.read()
font_cache.add_font(raw)
return container return container
def setup_cssutils_serialization(): def setup_cssutils_serialization():

View File

@ -12,19 +12,17 @@ from base64 import b64encode
from future_builtins import map from future_builtins import map
from threading import Thread from threading import Thread
from Queue import Queue, Empty from Queue import Queue, Empty
from collections import namedtuple
from functools import partial from functools import partial
from urlparse import urlparse from urlparse import urlparse
from PyQt5.Qt import ( from PyQt5.Qt import (
QWidget, QVBoxLayout, QApplication, QSize, QNetworkAccessManager, QMenu, QIcon, QWidget, QVBoxLayout, QApplication, QSize, QNetworkAccessManager, QMenu, QIcon,
QNetworkReply, QTimer, QNetworkRequest, QUrl, Qt, QNetworkDiskCache, QToolBar, QNetworkReply, QTimer, QNetworkRequest, QUrl, Qt, QNetworkDiskCache, QToolBar,
pyqtSlot, pyqtSignal, QFontDatabase) pyqtSlot, pyqtSignal)
from PyQt5.QtWebKitWidgets import QWebView, QWebInspector, QWebPage from PyQt5.QtWebKitWidgets import QWebView, QWebInspector, QWebPage
from calibre import prints from calibre import prints
from calibre.constants import iswindows from calibre.constants import iswindows
from calibre.ebooks.oeb.polish.container import OEB_FONTS
from calibre.ebooks.oeb.polish.parsing import parse from calibre.ebooks.oeb.polish.parsing import parse
from calibre.ebooks.oeb.base import serialize, OEB_DOCS from calibre.ebooks.oeb.base import serialize, OEB_DOCS
from calibre.ptempfile import PersistentTemporaryDirectory from calibre.ptempfile import PersistentTemporaryDirectory
@ -43,31 +41,6 @@ def get_data(name):
return editors[name].get_raw_data() return editors[name].get_raw_data()
return current_container().raw_data(name) return current_container().raw_data(name)
class FontCache(object):
def __init__(self):
self.cache = {}
self.entry = namedtuple('Entry', 'size hash families')
def remove_fonts(self):
for font_id in self.cache:
QFontDatabase.removeApplicationFont(font_id)
self.cache.clear()
def add_font(self, data):
existing = None
for font_id, entry in self.cache.iteritems():
if entry.size == len(data) and entry.hash == hash(data):
existing = entry
break
if existing is None:
font_id = QFontDatabase.addApplicationFontFromData(data)
if font_id > -1:
families = frozenset(map(lambda x:icu_lower(unicode(x)), QFontDatabase.applicationFontFamilies(font_id)))
self.cache[font_id] = self.entry(len(data), hash(data), families)
font_cache = FontCache()
# Parsing of html to add linenumbers {{{ # Parsing of html to add linenumbers {{{
def parse_html(raw): def parse_html(raw):
root = parse(raw, decoder=lambda x:x.decode('utf-8'), line_numbers=True, linenumber_attribute='data-lnum') root = parse(raw, decoder=lambda x:x.decode('utf-8'), line_numbers=True, linenumber_attribute='data-lnum')
@ -182,12 +155,6 @@ class NetworkReply(QNetworkReply):
self.setHeader(QNetworkRequest.ContentTypeHeader, mime_type) self.setHeader(QNetworkRequest.ContentTypeHeader, mime_type)
self.setHeader(QNetworkRequest.ContentLengthHeader, len(self.__data)) self.setHeader(QNetworkRequest.ContentLengthHeader, len(self.__data))
QTimer.singleShot(0, self.finalize_reply) QTimer.singleShot(0, self.finalize_reply)
if mime_type in OEB_FONTS:
font_cache.add_font(data)
# We prevent the use of the embedded font because of the the
# bug in Qt WebKit,
# https://bugs.webkit.org/show_bug.cgi?id=29433
self.__data = b''
def check_for_parse(self): def check_for_parse(self):
if self._aborted: if self._aborted:
@ -422,8 +389,8 @@ class WebView(QWebView):
<p style="font-size:x-small; color: gray">Note that this is a quick preview <p style="font-size:x-small; color: gray">Note that this is a quick preview
only, it is not intended to simulate an actual ebook reader. Some only, it is not intended to simulate an actual ebook reader. Some
aspects of your ebook will not work, such as, page breaks, aspects of your ebook will not work, such as, page breaks and
page margins and embedded fonts that use font name aliasing. page margins.
''')) '''))
self.page().current_root = None self.page().current_root = None

View File

@ -774,8 +774,7 @@ class EbookViewer(MainWindow):
self.iterator.__exit__() self.iterator.__exit__()
self.iterator = EbookIterator(pathtoebook) self.iterator = EbookIterator(pathtoebook)
self.open_progress_indicator(_('Loading ebook...')) self.open_progress_indicator(_('Loading ebook...'))
worker = Worker(target=partial(self.iterator.__enter__, worker = Worker(target=partial(self.iterator.__enter__, view_kpepub=True))
extract_embedded_fonts_for_qt=True, view_kepub=True))
worker.start() worker.start()
while worker.isAlive(): while worker.isAlive():
worker.join(0.1) worker.join(0.1)