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):
 | 
					def find_rule(raw, rule_address):
 | 
				
			||||||
    import tinycss
 | 
					    import tinycss
 | 
				
			||||||
    parser = tinycss.make_parser()
 | 
					    parser = tinycss.make_parser('page3', 'fonts3')
 | 
				
			||||||
    sheet = parser.parse_stylesheet(raw)
 | 
					    sheet = parser.parse_stylesheet(raw)
 | 
				
			||||||
    rules = sheet.rules
 | 
					    rules = sheet.rules
 | 
				
			||||||
    ans = None, None
 | 
					    ans = None, None
 | 
				
			||||||
 | 
				
			|||||||
@ -9,17 +9,17 @@
 | 
				
			|||||||
    :license: BSD, see LICENSE for more details.
 | 
					    :license: BSD, see LICENSE for more details.
 | 
				
			||||||
"""
 | 
					"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import sys
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
from .version import VERSION
 | 
					from .version import VERSION
 | 
				
			||||||
__version__ = VERSION
 | 
					__version__ = VERSION
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from .css21 import CSS21Parser
 | 
					from .css21 import CSS21Parser
 | 
				
			||||||
from .page3 import CSSPage3Parser
 | 
					from .page3 import CSSPage3Parser
 | 
				
			||||||
 | 
					from .fonts3 import CSSFonts3Parser
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
PARSER_MODULES = {
 | 
					PARSER_MODULES = {
 | 
				
			||||||
    'page3': CSSPage3Parser,
 | 
					    '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:
 | 
					except NameError:
 | 
				
			||||||
    unicode = str
 | 
					    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):
 | 
					class BaseTest(unittest.TestCase):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    longMessage = True
 | 
					    longMessage = True
 | 
				
			||||||
@ -25,3 +36,8 @@ class BaseTest(unittest.TestCase):
 | 
				
			|||||||
        for error, expected in zip(errors, expected_errors):
 | 
					        for error, expected in zip(errors, expected_errors):
 | 
				
			||||||
            self.assertIn(expected, unicode(error))
 | 
					            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.page3 import CSSPage3Parser
 | 
				
			||||||
from tinycss.tests import BaseTest
 | 
					from tinycss.tests import BaseTest
 | 
				
			||||||
from tinycss.tests.tokenizing import jsonify
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
class TestPage3(BaseTest):
 | 
					class TestPage3(BaseTest):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -79,14 +78,10 @@ class TestPage3(BaseTest):
 | 
				
			|||||||
            stylesheet = CSSPage3Parser().parse_stylesheet(css)
 | 
					            stylesheet = CSSPage3Parser().parse_stylesheet(css)
 | 
				
			||||||
            self.assert_errors(stylesheet.errors, expected_errors)
 | 
					            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)
 | 
					            self.ae(len(stylesheet.rules), 1)
 | 
				
			||||||
            rule = stylesheet.rules[0]
 | 
					            rule = stylesheet.rules[0]
 | 
				
			||||||
            self.ae(rule.at_keyword, '@page')
 | 
					            self.ae(rule.at_keyword, '@page')
 | 
				
			||||||
            self.ae(declarations(rule), expected_declarations)
 | 
					            self.ae(self.jsonify_declarations(rule), expected_declarations)
 | 
				
			||||||
            rules = [(margin_rule.at_keyword, declarations(margin_rule))
 | 
					            rules = [(margin_rule.at_keyword, self.jsonify_declarations(margin_rule))
 | 
				
			||||||
                    for margin_rule in rule.at_rules]
 | 
					                    for margin_rule in rule.at_rules]
 | 
				
			||||||
            self.ae(rules, expected_rules)
 | 
					            self.ae(rules, expected_rules)
 | 
				
			||||||
 | 
				
			|||||||
@ -6,20 +6,9 @@ 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>'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from tinycss.tests import BaseTest
 | 
					from tinycss.tests import BaseTest, jsonify
 | 
				
			||||||
from tinycss.tokenizer import python_tokenize_flat, c_tokenize_flat, regroup
 | 
					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:
 | 
					if c_tokenize_flat is None:
 | 
				
			||||||
    tokenizers = (python_tokenize_flat,)
 | 
					    tokenizers = (python_tokenize_flat,)
 | 
				
			||||||
else:
 | 
					else:
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user