mirror of
				https://github.com/kovidgoyal/calibre.git
				synced 2025-11-04 03:27:00 -05: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