mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-06-23 15:30:45 -04:00
Edit Book: Live CSS: Fix going to style declaration in the editor not working correctly when the stylesheet contains @font-face rules
This commit is contained in:
parent
fe0a84985a
commit
397b7cc4e1
@ -10,7 +10,7 @@ from calibre.gui2.tweak_book.editor.smart import NullSmarts
|
||||
|
||||
def find_rule(raw, rule_address):
|
||||
import tinycss
|
||||
parser = tinycss.make_parser()
|
||||
parser = tinycss.make_parser('page3', 'fonts3')
|
||||
sheet = parser.parse_stylesheet(raw)
|
||||
rules = sheet.rules
|
||||
ans = None, None
|
||||
|
@ -9,17 +9,17 @@
|
||||
:license: BSD, see LICENSE for more details.
|
||||
"""
|
||||
|
||||
import sys
|
||||
|
||||
from .version import VERSION
|
||||
__version__ = VERSION
|
||||
|
||||
from .css21 import CSS21Parser
|
||||
from .page3 import CSSPage3Parser
|
||||
from .fonts3 import CSSFonts3Parser
|
||||
|
||||
|
||||
PARSER_MODULES = {
|
||||
'page3': CSSPage3Parser,
|
||||
'fonts3': CSSFonts3Parser,
|
||||
}
|
||||
|
||||
|
||||
|
42
src/tinycss/fonts3.py
Normal file
42
src/tinycss/fonts3.py
Normal file
@ -0,0 +1,42 @@
|
||||
#!/usr/bin/env python
|
||||
# vim:fileencoding=utf-8
|
||||
from __future__ import (unicode_literals, division, absolute_import,
|
||||
print_function)
|
||||
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2014, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||
|
||||
|
||||
from .css21 import CSS21Parser, ParseError
|
||||
|
||||
class FontFaceRule(object):
|
||||
|
||||
at_keyword = '@font-face'
|
||||
|
||||
def __init__(self, declarations, line, column):
|
||||
self.declarations = declarations
|
||||
self.line = line
|
||||
self.column = column
|
||||
|
||||
class CSSFonts3Parser(CSS21Parser):
|
||||
|
||||
''' Parse @font-face rules from the CSS 3 fonts module '''
|
||||
|
||||
ALLOWED_CONTEXTS = {'stylesheet', '@media', '@page'}
|
||||
|
||||
def parse_at_rule(self, rule, previous_rules, errors, context):
|
||||
if rule.at_keyword != '@font-face':
|
||||
return super(CSSFonts3Parser, self).parse_at_rule(
|
||||
rule, previous_rules, errors, context)
|
||||
if context not in self.ALLOWED_CONTEXTS:
|
||||
raise ParseError(rule,
|
||||
'@font-face rule not allowed in ' + context)
|
||||
if rule.body is None:
|
||||
raise ParseError(rule,
|
||||
'invalid {0} rule: missing block'.format(rule.at_keyword))
|
||||
if rule.head:
|
||||
raise ParseError(rule, '{0} rule is not allowed to have content before the descriptor declaration'.format(rule.at_keyword))
|
||||
declarations, decerrors = self.parse_declaration_list(rule.body)
|
||||
errors.extend(decerrors)
|
||||
return FontFaceRule(declarations, rule.line, rule.column)
|
||||
|
@ -13,6 +13,17 @@ try:
|
||||
except NameError:
|
||||
unicode = str
|
||||
|
||||
def jsonify(tokens):
|
||||
"""Turn tokens into "JSON-compatible" data structures."""
|
||||
for token in tokens:
|
||||
if token.type == 'FUNCTION':
|
||||
yield (token.type, token.function_name,
|
||||
list(jsonify(token.content)))
|
||||
elif token.is_container:
|
||||
yield token.type, list(jsonify(token.content))
|
||||
else:
|
||||
yield token.type, token.value
|
||||
|
||||
class BaseTest(unittest.TestCase):
|
||||
|
||||
longMessage = True
|
||||
@ -25,3 +36,8 @@ class BaseTest(unittest.TestCase):
|
||||
for error, expected in zip(errors, expected_errors):
|
||||
self.assertIn(expected, unicode(error))
|
||||
|
||||
def jsonify_declarations(self, rule):
|
||||
return [(decl.name, list(jsonify(decl.value)))
|
||||
for decl in rule.declarations]
|
||||
|
||||
|
||||
|
31
src/tinycss/tests/fonts3.py
Normal file
31
src/tinycss/tests/fonts3.py
Normal file
@ -0,0 +1,31 @@
|
||||
#!/usr/bin/env python
|
||||
# vim:fileencoding=utf-8
|
||||
from __future__ import (unicode_literals, division, absolute_import,
|
||||
print_function)
|
||||
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2014, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||
|
||||
from tinycss.fonts3 import CSSFonts3Parser
|
||||
from tinycss.tests import BaseTest
|
||||
|
||||
class TestFonts3(BaseTest):
|
||||
|
||||
def test_font_face(self):
|
||||
'Test parsing of font face rules'
|
||||
for css, expected_declarations, expected_errors in [
|
||||
('@font-face {}', [], []),
|
||||
|
||||
('@font-face { font-family: Moose; src: url(font1.ttf) }',
|
||||
[('font-family', [('IDENT', 'Moose')]), ('src', [('URI', 'font1.ttf')])], []),
|
||||
]:
|
||||
stylesheet = CSSFonts3Parser().parse_stylesheet(css)
|
||||
self.assert_errors(stylesheet.errors, expected_errors)
|
||||
self.ae(len(stylesheet.rules), 1)
|
||||
rule = stylesheet.rules[0]
|
||||
self.ae(self.jsonify_declarations(rule), expected_declarations)
|
||||
|
||||
stylesheet = CSSFonts3Parser().parse_stylesheet('@font-face;')
|
||||
self.assert_errors(stylesheet.errors, ['missing block'])
|
||||
|
||||
|
@ -8,7 +8,6 @@ __copyright__ = '2014, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||
|
||||
from tinycss.page3 import CSSPage3Parser
|
||||
from tinycss.tests import BaseTest
|
||||
from tinycss.tests.tokenizing import jsonify
|
||||
|
||||
class TestPage3(BaseTest):
|
||||
|
||||
@ -79,14 +78,10 @@ class TestPage3(BaseTest):
|
||||
stylesheet = CSSPage3Parser().parse_stylesheet(css)
|
||||
self.assert_errors(stylesheet.errors, expected_errors)
|
||||
|
||||
def declarations(rule):
|
||||
return [(decl.name, list(jsonify(decl.value)))
|
||||
for decl in rule.declarations]
|
||||
|
||||
self.ae(len(stylesheet.rules), 1)
|
||||
rule = stylesheet.rules[0]
|
||||
self.ae(rule.at_keyword, '@page')
|
||||
self.ae(declarations(rule), expected_declarations)
|
||||
rules = [(margin_rule.at_keyword, declarations(margin_rule))
|
||||
self.ae(self.jsonify_declarations(rule), expected_declarations)
|
||||
rules = [(margin_rule.at_keyword, self.jsonify_declarations(margin_rule))
|
||||
for margin_rule in rule.at_rules]
|
||||
self.ae(rules, expected_rules)
|
||||
|
@ -6,20 +6,9 @@ from __future__ import (unicode_literals, division, absolute_import,
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2014, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||
|
||||
from tinycss.tests import BaseTest
|
||||
from tinycss.tests import BaseTest, jsonify
|
||||
from tinycss.tokenizer import python_tokenize_flat, c_tokenize_flat, regroup
|
||||
|
||||
def jsonify(tokens):
|
||||
"""Turn tokens into "JSON-compatible" data structures."""
|
||||
for token in tokens:
|
||||
if token.type == 'FUNCTION':
|
||||
yield (token.type, token.function_name,
|
||||
list(jsonify(token.content)))
|
||||
elif token.is_container:
|
||||
yield token.type, list(jsonify(token.content))
|
||||
else:
|
||||
yield token.type, token.value
|
||||
|
||||
if c_tokenize_flat is None:
|
||||
tokenizers = (python_tokenize_flat,)
|
||||
else:
|
||||
|
Loading…
x
Reference in New Issue
Block a user