mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Switch from cssutils to css-parser
css-parser is a new fork of the unmaintained cssutils. See https://github.com/ebook-utils/css-parser
This commit is contained in:
parent
3f57acc815
commit
dd7d8ea3c4
@ -8,7 +8,7 @@ __copyright__ = '2014, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||
|
||||
import re
|
||||
from PyQt5.Qt import QAction, QInputDialog
|
||||
from cssutils.css import CSSRule
|
||||
from css_parser.css import CSSRule
|
||||
|
||||
# The base class that all tools must inherit from
|
||||
from calibre.gui2.tweak_book.plugin import Tool
|
||||
@ -17,6 +17,7 @@ from calibre import force_unicode
|
||||
from calibre.gui2 import error_dialog
|
||||
from calibre.ebooks.oeb.polish.container import OEB_DOCS, OEB_STYLES, serialize
|
||||
|
||||
|
||||
class DemoTool(Tool):
|
||||
|
||||
#: Set this to a unique name it will be used as a key
|
||||
@ -79,7 +80,7 @@ class DemoTool(Tool):
|
||||
# stylesheets, <style> tags and style="" attributes
|
||||
for name, media_type in container.mime_map.iteritems():
|
||||
if media_type in OEB_STYLES:
|
||||
# A stylesheet. Parsed stylesheets are cssutils CSSStylesheet
|
||||
# A stylesheet. Parsed stylesheets are css_parser CSSStylesheet
|
||||
# objects.
|
||||
self.magnify_stylesheet(container.parsed(name), factor)
|
||||
container.dirty(name) # Tell the container that we have changed the stylesheet
|
||||
|
@ -262,7 +262,7 @@ class OutputProfile(Plugin):
|
||||
touchscreen = False
|
||||
touchscreen_news_css = ''
|
||||
#: A list of extra (beyond CSS 2.1) modules supported by the device
|
||||
#: Format is a cssutils profile dictionary (see iPad for example)
|
||||
#: Format is a css_parser profile dictionary (see iPad for example)
|
||||
extra_css_modules = []
|
||||
#: If True, the date is appended to the title of downloaded news
|
||||
periodical_date_in_title = True
|
||||
|
@ -2036,12 +2036,12 @@ class KOBOTOUCH(KOBO):
|
||||
|
||||
def get_extra_css(self):
|
||||
extra_sheet = None
|
||||
from cssutils.css import CSSRule
|
||||
from css_parser.css import CSSRule
|
||||
|
||||
if self.modifying_css():
|
||||
extra_css_path = os.path.join(self._main_prefix, self.KOBO_EXTRA_CSSFILE)
|
||||
if os.path.exists(extra_css_path):
|
||||
from cssutils import parseFile as cssparseFile
|
||||
from css_parser import parseFile as cssparseFile
|
||||
try:
|
||||
extra_sheet = cssparseFile(extra_css_path)
|
||||
debug_print("KoboTouch:get_extra_css: Using extra CSS in {0} ({1} rules)".format(extra_css_path, len(extra_sheet.cssRules)))
|
||||
@ -2068,7 +2068,7 @@ class KOBOTOUCH(KOBO):
|
||||
return [r for r in sheet.cssRules.rulesOfType(css_rule)]
|
||||
|
||||
def get_extra_css_rules_widow_orphan(self, sheet):
|
||||
from cssutils.css import CSSRule
|
||||
from css_parser.css import CSSRule
|
||||
return [r for r in self.get_extra_css_rules(sheet, CSSRule.STYLE_RULE)
|
||||
if (r.style['widows'] or r.style['orphans'])]
|
||||
|
||||
@ -2158,7 +2158,7 @@ class KOBOTOUCH(KOBO):
|
||||
return True
|
||||
|
||||
def _modify_stylesheet(self, sheet, fileext, is_dirty=False):
|
||||
from cssutils.css import CSSRule
|
||||
from css_parser.css import CSSRule
|
||||
|
||||
# if fileext in (EPUB_EXT, KEPUB_EXT):
|
||||
|
||||
|
@ -497,7 +497,7 @@ class EPUBOutput(OutputFormatPlugin):
|
||||
|
||||
if stylesheet is not None:
|
||||
# ADE doesn't render lists correctly if they have left margins
|
||||
from cssutils.css import CSSRule
|
||||
from css_parser.css import CSSRule
|
||||
for lb in XPath('//h:ul[@class]|//h:ol[@class]')(root):
|
||||
sel = '.'+lb.get('class')
|
||||
for rule in stylesheet.data.cssRules.rulesOfType(CSSRule.STYLE_RULE):
|
||||
|
@ -73,8 +73,8 @@ class FB2Input(InputFormatPlugin):
|
||||
css += etree.tostring(s, encoding=unicode, method='text',
|
||||
with_tail=False) + '\n\n'
|
||||
if css:
|
||||
import cssutils, logging
|
||||
parser = cssutils.CSSParser(fetcher=None,
|
||||
import css_parser, logging
|
||||
parser = css_parser.CSSParser(fetcher=None,
|
||||
log=logging.getLogger('calibre.css'))
|
||||
|
||||
XHTML_CSS_NAMESPACE = '@namespace "%s";\n' % XHTML_NS
|
||||
|
@ -109,8 +109,8 @@ class HTMLInput(InputFormatPlugin):
|
||||
from calibre.ebooks.html.input import get_filelist
|
||||
from calibre.ebooks.metadata import string_to_authors
|
||||
from calibre.utils.localization import canonicalize_lang
|
||||
import cssutils, logging
|
||||
cssutils.log.setLevel(logging.WARN)
|
||||
import css_parser, logging
|
||||
css_parser.log.setLevel(logging.WARN)
|
||||
self.OEB_STYLES = OEB_STYLES
|
||||
oeb = create_oebbook(log, None, opts, self,
|
||||
encoding=opts.input_encoding, populate=False)
|
||||
@ -189,7 +189,7 @@ class HTMLInput(InputFormatPlugin):
|
||||
if href == item.href:
|
||||
dpath = os.path.dirname(path)
|
||||
break
|
||||
cssutils.replaceUrls(item.data,
|
||||
css_parser.replaceUrls(item.data,
|
||||
partial(self.resource_adder, base=dpath))
|
||||
|
||||
toc = self.oeb.toc
|
||||
|
@ -1051,8 +1051,8 @@ OptionRecommendation(name='search_replace',
|
||||
if self.opts.embed_all_fonts or self.opts.embed_font_family:
|
||||
# Start the threaded font scanner now, for performance
|
||||
from calibre.utils.fonts.scanner import font_scanner # noqa
|
||||
import cssutils, logging
|
||||
cssutils.log.setLevel(logging.WARN)
|
||||
import css_parser, logging
|
||||
css_parser.log.setLevel(logging.WARN)
|
||||
get_types_map() # Ensure the mimetypes module is intialized
|
||||
|
||||
if self.opts.debug_pipeline is not None:
|
||||
|
@ -8,7 +8,7 @@ from functools import partial
|
||||
from collections import OrderedDict
|
||||
import operator
|
||||
|
||||
from cssutils.css import Property, CSSRule
|
||||
from css_parser.css import Property, CSSRule
|
||||
|
||||
from calibre import force_unicode
|
||||
from calibre.ebooks import parse_css_length
|
||||
@ -345,7 +345,7 @@ def export_rules(serialized_rules):
|
||||
|
||||
def import_rules(raw_data):
|
||||
import regex
|
||||
pat = regex.compile('\s*(\S+)\s*:\s*(.+)', flags=regex.VERSION1)
|
||||
pat = regex.compile(r'\s*(\S+)\s*:\s*(.+)', flags=regex.VERSION1)
|
||||
current_rule = {}
|
||||
|
||||
def sanitize(r):
|
||||
|
@ -327,28 +327,6 @@ def upshift_markup(parts):
|
||||
parts[i] = part
|
||||
|
||||
|
||||
def handle_media_queries(raw):
|
||||
# cssutils cannot handle CSS 3 media queries. We look for media queries
|
||||
# that use amzn-mobi or amzn-kf8 and map them to a simple @media screen
|
||||
# rule. See https://bugs.launchpad.net/bugs/1406708 for an example
|
||||
import tinycss
|
||||
parser = tinycss.make_full_parser()
|
||||
|
||||
def replace(m):
|
||||
sheet = parser.parse_stylesheet(m.group() + '}')
|
||||
if len(sheet.rules) > 0:
|
||||
for mq in sheet.rules[0].media:
|
||||
# Only accept KF8 media types
|
||||
if (mq.media_type, mq.negated) in {('amzn-mobi', True), ('amzn-kf8', False)}:
|
||||
return '@media screen {'
|
||||
else:
|
||||
# Empty sheet, doesn't matter what we use
|
||||
return '@media screen {'
|
||||
return m.group()
|
||||
|
||||
return re.sub(r'@media\s[^{;]*?[{;]', replace, raw)
|
||||
|
||||
|
||||
def expand_mobi8_markup(mobi8_reader, resource_map, log):
|
||||
# First update all internal links that are based on offsets
|
||||
parts = update_internal_links(mobi8_reader, log)
|
||||
@ -390,8 +368,6 @@ def expand_mobi8_markup(mobi8_reader, resource_map, log):
|
||||
if not os.path.exists(fi.dir):
|
||||
os.mkdir(fi.dir)
|
||||
with open(os.path.join(fi.dir, fi.fname), 'wb') as f:
|
||||
if fi.fname.endswith('.css') and '@media' in flow:
|
||||
flow = handle_media_queries(flow)
|
||||
f.write(flow.encode('utf-8'))
|
||||
|
||||
return spine
|
||||
|
@ -13,8 +13,8 @@ from collections import defaultdict, namedtuple
|
||||
from io import BytesIO
|
||||
from struct import pack
|
||||
|
||||
import cssutils
|
||||
from cssutils.css import CSSRule
|
||||
import css_parser
|
||||
from css_parser.css import CSSRule
|
||||
from lxml import etree
|
||||
|
||||
from calibre import isbytestring, force_unicode
|
||||
@ -77,9 +77,9 @@ class KF8Writer(object):
|
||||
''' Duplicate data so that any changes we make to markup/CSS only
|
||||
affect KF8 output and not MOBI 6 output '''
|
||||
self._data_cache = {}
|
||||
# Suppress cssutils logging output as it is duplicated anyway earlier
|
||||
# Suppress css_parser logging output as it is duplicated anyway earlier
|
||||
# in the pipeline
|
||||
cssutils.log.setLevel(logging.CRITICAL)
|
||||
css_parser.log.setLevel(logging.CRITICAL)
|
||||
for item in self.oeb.manifest:
|
||||
if item.media_type in XML_DOCS:
|
||||
self._data_cache[item.href] = copy.deepcopy(item.data)
|
||||
@ -87,7 +87,7 @@ class KF8Writer(object):
|
||||
# I can't figure out how to make an efficient copy of the
|
||||
# in-memory CSSStylesheet, as deepcopy doesn't work (raises an
|
||||
# exception)
|
||||
self._data_cache[item.href] = cssutils.parseString(
|
||||
self._data_cache[item.href] = css_parser.parseString(
|
||||
item.data.cssText, validate=False)
|
||||
|
||||
def data(self, item):
|
||||
@ -138,9 +138,9 @@ class KF8Writer(object):
|
||||
|
||||
for tag in XPath('//h:style')(root):
|
||||
if tag.text:
|
||||
sheet = cssutils.parseString(tag.text, validate=False)
|
||||
sheet = css_parser.parseString(tag.text, validate=False)
|
||||
replacer = partial(pointer, item)
|
||||
cssutils.replaceUrls(sheet, replacer,
|
||||
css_parser.replaceUrls(sheet, replacer,
|
||||
ignoreImportRules=True)
|
||||
repl = sheet.cssText
|
||||
if isbytestring(repl):
|
||||
@ -150,7 +150,7 @@ class KF8Writer(object):
|
||||
elif item.media_type in OEB_STYLES:
|
||||
sheet = self.data(item)
|
||||
replacer = partial(pointer, item)
|
||||
cssutils.replaceUrls(sheet, replacer, ignoreImportRules=True)
|
||||
css_parser.replaceUrls(sheet, replacer, ignoreImportRules=True)
|
||||
|
||||
def extract_css_into_flows(self):
|
||||
inlines = defaultdict(list) # Ensure identical <style>s not repeated
|
||||
@ -194,7 +194,7 @@ class KF8Writer(object):
|
||||
if not raw or not raw.strip():
|
||||
extract(tag)
|
||||
continue
|
||||
sheet = cssutils.parseString(raw, validate=False)
|
||||
sheet = css_parser.parseString(raw, validate=False)
|
||||
if fix_import_rules(sheet):
|
||||
raw = force_unicode(sheet.cssText, 'utf-8')
|
||||
|
||||
|
@ -9,8 +9,8 @@ Convert an ODT file into a Open Ebook
|
||||
import os, logging
|
||||
|
||||
from lxml import etree
|
||||
from cssutils import CSSParser
|
||||
from cssutils.css import CSSRule
|
||||
from css_parser import CSSParser
|
||||
from css_parser.css import CSSRule
|
||||
|
||||
from odf.odf2xhtml import ODF2XHTML
|
||||
from odf.opendocument import load as odLoad
|
||||
@ -184,8 +184,8 @@ class Extract(ODF2XHTML):
|
||||
x.set('class', orig + ' ' + ' '.join(extra))
|
||||
|
||||
def do_filter_css(self, css):
|
||||
from cssutils import parseString
|
||||
from cssutils.css import CSSRule
|
||||
from css_parser import parseString
|
||||
from css_parser.css import CSSRule
|
||||
sheet = parseString(css, validate=False)
|
||||
rules = list(sheet.cssRules.rulesOfType(CSSRule.STYLE_RULE))
|
||||
sel_map = {}
|
||||
@ -301,7 +301,3 @@ class Extract(ODF2XHTML):
|
||||
with open('metadata.opf', 'wb') as f:
|
||||
opf.render(f)
|
||||
return os.path.abspath('metadata.opf')
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -217,7 +217,7 @@ def rewrite_links(root, link_repl_func, resolve_base_href=False):
|
||||
If the ``link_repl_func`` returns None, the attribute or
|
||||
tag text will be removed completely.
|
||||
'''
|
||||
from cssutils import replaceUrls, log, CSSParser
|
||||
from css_parser import replaceUrls, log, CSSParser
|
||||
log.setLevel(logging.WARN)
|
||||
log.raiseExceptions = False
|
||||
|
||||
@ -269,7 +269,7 @@ def rewrite_links(root, link_repl_func, resolve_base_href=False):
|
||||
try:
|
||||
stext = parser.parseStyle(text, validate=False)
|
||||
except Exception:
|
||||
# Parsing errors are raised by cssutils
|
||||
# Parsing errors are raised by css_parser
|
||||
continue
|
||||
replaceUrls(stext, link_repl_func)
|
||||
repl = stext.cssText.replace('\n', ' ').replace('\r',
|
||||
@ -971,8 +971,8 @@ class Manifest(object):
|
||||
return self._parse_xhtml(convert_markdown(data, title=title))
|
||||
|
||||
def _parse_css(self, data):
|
||||
from cssutils import CSSParser, log, resolveImports
|
||||
from cssutils.css import CSSRule
|
||||
from css_parser import CSSParser, log, resolveImports
|
||||
from css_parser.css import CSSRule
|
||||
log.setLevel(logging.WARN)
|
||||
log.raiseExceptions = False
|
||||
self.oeb.log.debug('Parsing', self.href, '...')
|
||||
@ -1011,7 +1011,7 @@ class Manifest(object):
|
||||
convert and return as an lxml.etree element in the XHTML
|
||||
namespace.
|
||||
- XML content is parsed and returned as an lxml.etree element.
|
||||
- CSS and CSS-variant content is parsed and returned as a cssutils
|
||||
- CSS and CSS-variant content is parsed and returned as a css_parser
|
||||
CSS DOM stylesheet.
|
||||
- All other content is returned as a :class:`str` object with no
|
||||
special parsing.
|
||||
|
@ -9,11 +9,8 @@ __copyright__ = '2013, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||
from polyglot.builtins import zip
|
||||
from functools import wraps
|
||||
|
||||
try:
|
||||
from cssutils.css import PropertyValue
|
||||
except ImportError:
|
||||
raise RuntimeError('You need cssutils >= 0.9.9 for calibre')
|
||||
from cssutils import profile as cssprofiles, CSSParser
|
||||
from css_parser.css import PropertyValue
|
||||
from css_parser import profile as cssprofiles, CSSParser
|
||||
from tinycss.fonts3 import parse_font, serialize_font_family
|
||||
|
||||
DEFAULTS = {'azimuth': 'center', 'background-attachment': 'scroll', # {{{
|
||||
@ -269,7 +266,7 @@ def condense_sheet(sheet):
|
||||
|
||||
def test_normalization(return_tests=False): # {{{
|
||||
import unittest
|
||||
from cssutils import parseStyle
|
||||
from css_parser import parseStyle
|
||||
from itertools import product
|
||||
|
||||
class TestNormalization(unittest.TestCase):
|
||||
|
@ -10,7 +10,7 @@ from itertools import count
|
||||
from operator import itemgetter
|
||||
import re
|
||||
|
||||
from cssutils.css import CSSStyleSheet, CSSRule, Property
|
||||
from css_parser.css import CSSStyleSheet, CSSRule, Property
|
||||
|
||||
from css_selectors import Select, INAPPROPRIATE_PSEUDO_CLASSES, SelectorError
|
||||
from calibre import as_unicode
|
||||
@ -45,7 +45,8 @@ def iterrules(container, sheet_name, rules=None, media_rule_ok=media_allowed, ru
|
||||
:param sheet_name: The name of the sheet in the container (in case of inline style sheets, the name of the html file)
|
||||
:param media_rule_ok: A function to test if a @media rule is allowed
|
||||
:param rule_index_counter: A counter object, rule numbers will be calculated by incrementing the counter.
|
||||
:param rule_type: Only yield rules of this type, where type is a string type name, see cssutils.css.CSSRule for the names (by default all rules are yielded)
|
||||
:param rule_type: Only yield rules of this type, where type is a string type name, see css_parser.css.CSSRule for the names (
|
||||
by default all rules are yielded)
|
||||
:return: (CSSRule object, the name of the sheet from which it comes, rule index - a monotonically increasing number)
|
||||
'''
|
||||
|
||||
@ -104,7 +105,7 @@ def iterdeclaration(decl):
|
||||
|
||||
class Values(tuple):
|
||||
|
||||
''' A tuple of `cssutils.css.Value ` (and its subclasses) objects. Also has a
|
||||
''' A tuple of `css_parser.css.Value ` (and its subclasses) objects. Also has a
|
||||
`sheet_name` attribute that is the canonical name relative to which URLs
|
||||
for this property should be resolved. '''
|
||||
|
||||
|
@ -6,7 +6,7 @@ from __future__ import (unicode_literals, division, absolute_import,
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2013, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||
|
||||
from cssutils.css import CSSRule
|
||||
from css_parser.css import CSSRule
|
||||
|
||||
from calibre import force_unicode
|
||||
from calibre.ebooks.oeb.base import OEB_DOCS, OEB_STYLES
|
||||
|
@ -50,7 +50,7 @@ def run_checks(container):
|
||||
if err.level > WARN:
|
||||
return errors
|
||||
|
||||
# cssutils is not thread safe
|
||||
# css_parser is not thread safe
|
||||
for name, mt, raw in stylesheets:
|
||||
if not raw:
|
||||
errors.append(EmptyFile(name))
|
||||
@ -107,4 +107,3 @@ def fix_errors(container, errors):
|
||||
# better to have a false positive than a false negative)
|
||||
changed = True
|
||||
return changed
|
||||
|
||||
|
@ -9,7 +9,7 @@ __copyright__ = '2013, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||
import re
|
||||
|
||||
from lxml.etree import XMLParser, fromstring, XMLSyntaxError
|
||||
import cssutils
|
||||
import css_parser
|
||||
|
||||
from calibre import force_unicode, human_readable, prepare_string_for_xml
|
||||
from calibre.ebooks.chardet import replace_encoding_declarations, find_declared_encoding
|
||||
@ -432,7 +432,7 @@ class BareTextInBody(BaseError):
|
||||
|
||||
class ErrorHandler(object):
|
||||
|
||||
' Replacement logger to get useful error/warning info out of cssutils during parsing '
|
||||
' Replacement logger to get useful error/warning info out of css_parser during parsing '
|
||||
|
||||
def __init__(self, name):
|
||||
# may be disabled during setting of known valid items
|
||||
@ -467,7 +467,7 @@ class ErrorHandler(object):
|
||||
|
||||
def check_css_parsing(name, raw, line_offset=0, is_declaration=False):
|
||||
log = ErrorHandler(name)
|
||||
parser = cssutils.CSSParser(fetcher=lambda x: (None, None), log=log)
|
||||
parser = css_parser.CSSParser(fetcher=lambda x: (None, None), log=log)
|
||||
if is_declaration:
|
||||
parser.parseStyle(raw, validate=True)
|
||||
else:
|
||||
|
@ -19,7 +19,7 @@ from io import BytesIO
|
||||
from itertools import count
|
||||
from urlparse import urlparse
|
||||
|
||||
from cssutils import getUrls, replaceUrls
|
||||
from css_parser import getUrls, replaceUrls
|
||||
from lxml import etree
|
||||
|
||||
from calibre import CurrentDir, walk
|
||||
@ -589,7 +589,7 @@ class Container(ContainerBase): # {{{
|
||||
|
||||
def parsed(self, name):
|
||||
''' Return a parsed representation of the file specified by name. For
|
||||
HTML and XML files an lxml tree is returned. For CSS files a cssutils
|
||||
HTML and XML files an lxml tree is returned. For CSS files a css_parser
|
||||
stylesheet is returned. Note that parsed objects are cached for
|
||||
performance. If you make any changes to the parsed object, you must
|
||||
call :meth:`dirty` so that the container knows to update the cache. See also :meth:`replace`.'''
|
||||
@ -605,7 +605,7 @@ class Container(ContainerBase): # {{{
|
||||
def replace(self, name, obj):
|
||||
'''
|
||||
Replace the parsed object corresponding to name with obj, which must be
|
||||
a similar object, i.e. an lxml tree for HTML/XML or a cssutils
|
||||
a similar object, i.e. an lxml tree for HTML/XML or a css_parser
|
||||
stylesheet for a CSS file.
|
||||
'''
|
||||
self.parsed_cache[name] = obj
|
||||
|
@ -9,7 +9,7 @@ __copyright__ = '2014, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||
from collections import defaultdict
|
||||
from functools import partial
|
||||
|
||||
from cssutils.css import CSSRule, CSSStyleDeclaration
|
||||
from css_parser.css import CSSRule, CSSStyleDeclaration
|
||||
from css_selectors import parse, SelectorSyntaxError
|
||||
|
||||
from calibre import force_unicode
|
||||
@ -208,7 +208,7 @@ def filter_declaration(style, properties=()):
|
||||
|
||||
|
||||
def filter_sheet(sheet, properties=()):
|
||||
from cssutils.css import CSSRule
|
||||
from css_parser.css import CSSRule
|
||||
changed = False
|
||||
remove = []
|
||||
for rule in sheet.cssRules.rulesOfType(CSSRule.STYLE_RULE):
|
||||
|
@ -8,7 +8,7 @@ __copyright__ = '2016, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||
|
||||
from functools import partial
|
||||
|
||||
from cssutils import parseStyle
|
||||
from css_parser import parseStyle
|
||||
|
||||
from calibre.constants import iswindows
|
||||
from calibre.ebooks.oeb.base import OEB_STYLES, OEB_DOCS
|
||||
|
@ -16,9 +16,9 @@ def guess_type(x):
|
||||
return _guess_type(x)[0] or 'application/octet-stream'
|
||||
|
||||
|
||||
def setup_cssutils_serialization(tab_width=2):
|
||||
import cssutils
|
||||
prefs = cssutils.ser.prefs
|
||||
def setup_css_parser_serialization(tab_width=2):
|
||||
import css_parser
|
||||
prefs = css_parser.ser.prefs
|
||||
prefs.indent = tab_width * ' '
|
||||
prefs.indentClosingBrace = False
|
||||
prefs.omitLastSemicolon = False
|
||||
@ -163,7 +163,7 @@ def parse_css(data, fname='<string>', is_declaration=False, decode=None, log_lev
|
||||
if log_level is None:
|
||||
import logging
|
||||
log_level = logging.WARNING
|
||||
from cssutils import CSSParser, log
|
||||
from css_parser import CSSParser, log
|
||||
from calibre.ebooks.oeb.base import _css_logger
|
||||
log.setLevel(log_level)
|
||||
log.raiseExceptions = False
|
||||
|
@ -185,7 +185,7 @@ class OEBReader(object):
|
||||
return bad
|
||||
|
||||
def _manifest_add_missing(self, invalid):
|
||||
import cssutils
|
||||
import css_parser
|
||||
manifest = self.oeb.manifest
|
||||
known = set(manifest.hrefs)
|
||||
unchecked = set(manifest.values())
|
||||
@ -225,7 +225,7 @@ class OEBReader(object):
|
||||
new.add(href)
|
||||
elif item.media_type in OEB_STYLES:
|
||||
try:
|
||||
urls = list(cssutils.getUrls(data))
|
||||
urls = list(css_parser.getUrls(data))
|
||||
except:
|
||||
urls = []
|
||||
for url in urls:
|
||||
|
@ -11,10 +11,10 @@ __copyright__ = '2008, Marshall T. Vandegrift <llasram@gmail.com>'
|
||||
import os, re, logging, copy, unicodedata
|
||||
from weakref import WeakKeyDictionary
|
||||
from xml.dom import SyntaxErr as CSSSyntaxError
|
||||
from cssutils.css import (CSSStyleRule, CSSPageRule, CSSFontFaceRule,
|
||||
from css_parser.css import (CSSStyleRule, CSSPageRule, CSSFontFaceRule,
|
||||
cssproperties)
|
||||
from cssutils import (profile as cssprofiles, parseString, parseStyle, log as
|
||||
cssutils_log, CSSParser, profiles, replaceUrls)
|
||||
from css_parser import (profile as cssprofiles, parseString, parseStyle, log as
|
||||
css_parser_log, CSSParser, profiles, replaceUrls)
|
||||
from calibre import force_unicode, as_unicode
|
||||
from calibre.ebooks import unit_convert
|
||||
from calibre.ebooks.oeb.base import XHTML, XHTML_NS, CSS_MIME, OEB_STYLES, xpath, urlnormalize
|
||||
@ -22,7 +22,7 @@ from calibre.ebooks.oeb.normalize_css import DEFAULTS, normalizers
|
||||
from css_selectors import Select, SelectorError, INAPPROPRIATE_PSEUDO_CLASSES
|
||||
from tinycss.media3 import CSSMedia3Parser
|
||||
|
||||
cssutils_log.setLevel(logging.WARN)
|
||||
css_parser_log.setLevel(logging.WARN)
|
||||
|
||||
_html_css_stylesheet = None
|
||||
|
||||
@ -123,7 +123,7 @@ class Stylizer(object):
|
||||
stylesheets.append(parseString(base_css, validate=False))
|
||||
style_tags = xpath(tree, '//*[local-name()="style" or local-name()="link"]')
|
||||
|
||||
# Add cssutils parsing profiles from output_profile
|
||||
# Add css_parser parsing profiles from output_profile
|
||||
for profile in self.opts.output_profile.extra_css_modules:
|
||||
cssprofiles.addProfile(profile['name'],
|
||||
profile['props'],
|
||||
|
@ -9,7 +9,7 @@ __copyright__ = '2013, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||
import logging
|
||||
from collections import defaultdict
|
||||
|
||||
import cssutils
|
||||
import css_parser
|
||||
from lxml import etree
|
||||
|
||||
from calibre import guess_type
|
||||
@ -97,7 +97,7 @@ class EmbedFonts(object):
|
||||
self.sheet_cache = {}
|
||||
self.find_style_rules()
|
||||
self.find_embedded_fonts()
|
||||
self.parser = cssutils.CSSParser(loglevel=logging.CRITICAL, log=logging.getLogger('calibre.css'))
|
||||
self.parser = css_parser.CSSParser(loglevel=logging.CRITICAL, log=logging.getLogger('calibre.css'))
|
||||
self.warned = set()
|
||||
self.warned2 = set()
|
||||
self.newly_embedded_fonts = set()
|
||||
|
@ -25,7 +25,7 @@ class RenameFiles(object): # {{{
|
||||
self.renamed_items_map = renamed_items_map
|
||||
|
||||
def __call__(self, oeb, opts):
|
||||
import cssutils
|
||||
import css_parser
|
||||
self.log = oeb.logger
|
||||
self.opts = opts
|
||||
self.oeb = oeb
|
||||
@ -35,7 +35,7 @@ class RenameFiles(object): # {{{
|
||||
if etree.iselement(item.data):
|
||||
rewrite_links(self.current_item.data, self.url_replacer)
|
||||
elif hasattr(item.data, 'cssText'):
|
||||
cssutils.replaceUrls(item.data, self.url_replacer)
|
||||
css_parser.replaceUrls(item.data, self.url_replacer)
|
||||
|
||||
if self.oeb.guide:
|
||||
for ref in self.oeb.guide.values():
|
||||
@ -184,4 +184,3 @@ class FlatFilenames(object): # {{{
|
||||
renamer = RenameFiles(self.rename_map, self.renamed_items_map)
|
||||
renamer(oeb, opts)
|
||||
# }}}
|
||||
|
||||
|
@ -11,8 +11,8 @@ from collections import defaultdict
|
||||
from xml.dom import SyntaxErr
|
||||
|
||||
from lxml import etree
|
||||
import cssutils
|
||||
from cssutils.css import Property
|
||||
import css_parser
|
||||
from css_parser.css import Property
|
||||
|
||||
from calibre import guess_type
|
||||
from calibre.ebooks import unit_convert
|
||||
@ -139,7 +139,7 @@ class EmbedFontsCSSRules(object):
|
||||
iid, href = oeb.manifest.generate(u'page_styles', u'page_styles.css')
|
||||
rules = [x.cssText for x in self.rules]
|
||||
rules = u'\n\n'.join(rules)
|
||||
sheet = cssutils.parseString(rules, validate=False)
|
||||
sheet = css_parser.parseString(rules, validate=False)
|
||||
self.href = oeb.manifest.add(iid, href, guess_type(href)[0],
|
||||
data=sheet).href
|
||||
return self.href
|
||||
@ -203,7 +203,7 @@ class CSSFlattener(object):
|
||||
# Make all links to resources absolute, as these sheets will be
|
||||
# consolidated into a single stylesheet at the root of the document
|
||||
if item.media_type in OEB_STYLES:
|
||||
cssutils.replaceUrls(item.data, item.abshref,
|
||||
css_parser.replaceUrls(item.data, item.abshref,
|
||||
ignoreImportRules=True)
|
||||
|
||||
self.body_font_family, self.embed_font_rules = self.get_embed_font_info(
|
||||
@ -278,7 +278,7 @@ class CSSFlattener(object):
|
||||
cfont[k] = font[k]
|
||||
rule = '@font-face { %s }'%('; '.join(u'%s:%s'%(k, v) for k, v in
|
||||
cfont.iteritems()))
|
||||
rule = cssutils.parseString(rule)
|
||||
rule = css_parser.parseString(rule)
|
||||
efi.append(rule)
|
||||
|
||||
return body_font_family, efi
|
||||
@ -615,7 +615,7 @@ class CSSFlattener(object):
|
||||
if item.media_type in OEB_STYLES:
|
||||
manifest.remove(item)
|
||||
id, href = manifest.generate('css', 'stylesheet.css')
|
||||
sheet = cssutils.parseString(css, validate=False)
|
||||
sheet = css_parser.parseString(css, validate=False)
|
||||
if self.transform_css_rules:
|
||||
from calibre.ebooks.css_transform_rules import transform_sheet
|
||||
transform_sheet(self.transform_css_rules, sheet)
|
||||
@ -647,7 +647,7 @@ class CSSFlattener(object):
|
||||
href = None
|
||||
if css.strip():
|
||||
id_, href = manifest.generate('page_css', 'page_styles.css')
|
||||
sheet = cssutils.parseString(css, validate=False)
|
||||
sheet = css_parser.parseString(css, validate=False)
|
||||
if self.transform_css_rules:
|
||||
from calibre.ebooks.css_transform_rules import transform_sheet
|
||||
transform_sheet(self.transform_css_rules, sheet)
|
||||
|
@ -65,7 +65,7 @@ class RemoveFakeMargins(object):
|
||||
|
||||
stylesheet = stylesheet.data
|
||||
|
||||
from cssutils.css import CSSRule
|
||||
from css_parser.css import CSSRule
|
||||
for rule in stylesheet.cssRules.rulesOfType(CSSRule.STYLE_RULE):
|
||||
self.selector_map[rule.selectorList.selectorText] = rule.style
|
||||
|
||||
|
@ -23,7 +23,7 @@ class ManifestTrimmer(object):
|
||||
return cls()
|
||||
|
||||
def __call__(self, oeb, context):
|
||||
import cssutils
|
||||
import css_parser
|
||||
oeb.logger.info('Trimming unused files from manifest...')
|
||||
self.opts = context
|
||||
used = set()
|
||||
@ -60,7 +60,7 @@ class ManifestTrimmer(object):
|
||||
if found not in used:
|
||||
new.add(found)
|
||||
elif item.media_type == CSS_MIME:
|
||||
for href in cssutils.getUrls(item.data):
|
||||
for href in css_parser.getUrls(item.data):
|
||||
href = item.abshref(urlnormalize(href))
|
||||
if href in oeb.manifest.hrefs:
|
||||
found = oeb.manifest.hrefs[href]
|
||||
|
@ -26,7 +26,7 @@ from calibre.ebooks.oeb.polish.pretty import fix_all_html, pretty_all
|
||||
from calibre.ebooks.oeb.polish.replace import rename_files, replace_file, get_recommended_folders, rationalize_folders
|
||||
from calibre.ebooks.oeb.polish.split import split, merge, AbortError, multisplit
|
||||
from calibre.ebooks.oeb.polish.toc import remove_names_from_toc, create_inline_toc
|
||||
from calibre.ebooks.oeb.polish.utils import link_stylesheets, setup_cssutils_serialization as scs
|
||||
from calibre.ebooks.oeb.polish.utils import link_stylesheets, setup_css_parser_serialization as scs
|
||||
from calibre.gui2 import error_dialog, choose_files, question_dialog, info_dialog, choose_save_file, open_url, choose_dir, add_to_recent_docs
|
||||
from calibre.gui2.dialogs.confirm_delete import confirm
|
||||
from calibre.gui2.tweak_book import (
|
||||
@ -61,7 +61,7 @@ def get_container(*args, **kwargs):
|
||||
return container
|
||||
|
||||
|
||||
def setup_cssutils_serialization():
|
||||
def setup_css_parser_serialization():
|
||||
scs(tprefs['editor_tab_stop_width'])
|
||||
|
||||
|
||||
@ -91,7 +91,7 @@ class Boss(QObject):
|
||||
self.save_manager.check_for_completion.connect(self.check_terminal_save)
|
||||
self.doing_terminal_save = False
|
||||
self.ignore_preview_to_editor_sync = False
|
||||
setup_cssutils_serialization()
|
||||
setup_css_parser_serialization()
|
||||
get_boss.boss = self
|
||||
self.gui = parent
|
||||
completion_worker().result_callback = self.handle_completion_result_signal.emit
|
||||
@ -172,7 +172,7 @@ class Boss(QObject):
|
||||
bar.setIconSize(QSize(tprefs['toolbar_icon_size'], tprefs['toolbar_icon_size']))
|
||||
|
||||
if ret == p.Accepted:
|
||||
setup_cssutils_serialization()
|
||||
setup_css_parser_serialization()
|
||||
self.gui.apply_settings()
|
||||
self.refresh_file_list()
|
||||
if ret == p.Accepted or p.dictionaries_changed:
|
||||
|
@ -53,9 +53,9 @@ def beautify_text(raw, syntax):
|
||||
elif syntax == 'css':
|
||||
import logging
|
||||
from calibre.ebooks.oeb.base import serialize, _css_logger
|
||||
from calibre.ebooks.oeb.polish.utils import setup_cssutils_serialization
|
||||
from cssutils import CSSParser, log
|
||||
setup_cssutils_serialization(tprefs['editor_tab_stop_width'])
|
||||
from calibre.ebooks.oeb.polish.utils import setup_css_parser_serialization
|
||||
from css_parser import CSSParser, log
|
||||
setup_css_parser_serialization(tprefs['editor_tab_stop_width'])
|
||||
log.setLevel(logging.WARN)
|
||||
log.raiseExceptions = False
|
||||
parser = CSSParser(loglevel=logging.WARNING,
|
||||
|
@ -10,7 +10,7 @@ import sys, re
|
||||
from operator import itemgetter
|
||||
from itertools import chain
|
||||
|
||||
from cssutils import parseStyle
|
||||
from css_parser import parseStyle
|
||||
from PyQt5.Qt import QTextEdit, Qt, QTextCursor
|
||||
|
||||
from calibre import prepare_string_for_xml, xml_entity_to_unicode
|
||||
|
@ -13,8 +13,8 @@ from polyglot.builtins import map
|
||||
from urlparse import urlparse
|
||||
from urllib import quote
|
||||
|
||||
from cssutils import replaceUrls
|
||||
from cssutils.css import CSSRule
|
||||
from css_parser import replaceUrls
|
||||
from css_parser.css import CSSRule
|
||||
|
||||
from calibre import prepare_string_for_xml, force_unicode
|
||||
from calibre.ebooks import parse_css_length
|
||||
|
@ -50,7 +50,7 @@ def serialize_single_font_family(x):
|
||||
xl = 'sans-serif'
|
||||
return xl
|
||||
if SIMPLE_NAME_PAT.match(x) is not None and not x.lower().startswith('and'):
|
||||
# cssutils dies if a font name starts with and
|
||||
# css_parser dies if a font name starts with and
|
||||
return x
|
||||
return '"%s"' % x.replace('"', r'\"')
|
||||
|
||||
@ -58,6 +58,7 @@ def serialize_single_font_family(x):
|
||||
def serialize_font_family(families):
|
||||
return ', '.join(map(serialize_single_font_family, families))
|
||||
|
||||
|
||||
GLOBAL_IDENTS = frozenset('inherit initial unset normal'.split())
|
||||
STYLE_IDENTS = frozenset('italic oblique'.split())
|
||||
VARIANT_IDENTS = frozenset(('small-caps',))
|
||||
|
Loading…
x
Reference in New Issue
Block a user