Permalink
Find file
Fetching contributors…
Cannot retrieve contributors at this time
163 lines (131 sloc) 5 KB
# coding: utf-8
"""
tinycss.page3
------------------
Support for CSS 3 Paged Media syntax:
http://dev.w3.org/csswg/css3-page/
Adds support for named page selectors and margin rules.
:copyright: (c) 2012 by Simon Sapin.
:license: BSD, see LICENSE for more details.
"""
from __future__ import division, unicode_literals
from .css21 import CSS21Parser, ParseError
class MarginRule(object):
"""A parsed at-rule for margin box.
.. attribute:: at_keyword
One of the 16 following strings:
* ``@top-left-corner``
* ``@top-left``
* ``@top-center``
* ``@top-right``
* ``@top-right-corner``
* ``@bottom-left-corner``
* ``@bottom-left``
* ``@bottom-center``
* ``@bottom-right``
* ``@bottom-right-corner``
* ``@left-top``
* ``@left-middle``
* ``@left-bottom``
* ``@right-top``
* ``@right-middle``
* ``@right-bottom``
.. attribute:: declarations
A list of :class:`~.css21.Declaration` objects.
.. attribute:: line
Source line where this was read.
.. attribute:: column
Source column where this was read.
"""
def __init__(self, at_keyword, declarations, line, column):
self.at_keyword = at_keyword
self.declarations = declarations
self.line = line
self.column = column
class CSSPage3Parser(CSS21Parser):
"""Extend :class:`~.css21.CSS21Parser` for `CSS 3 Paged Media`_ syntax.
.. _CSS 3 Paged Media: http://dev.w3.org/csswg/css3-page/
Compared to CSS 2.1, the ``at_rules`` and ``selector`` attributes of
:class:`~.css21.PageRule` objects are modified:
* ``at_rules`` is not always empty, it is a list of :class:`MarginRule`
objects.
* ``selector``, instead of a single string, is a tuple of the page name
and the pseudo class. Each of these may be a ``None`` or a string.
+--------------------------+------------------------+
| CSS | Parsed selectors |
+==========================+========================+
| .. code-block:: css | .. code-block:: python |
| | |
| @page {} | (None, None) |
| @page :first {} | (None, 'first') |
| @page chapter {} | ('chapter', None) |
| @page table:right {} | ('table', 'right') |
+--------------------------+------------------------+
"""
PAGE_MARGIN_AT_KEYWORDS = [
'@top-left-corner',
'@top-left',
'@top-center',
'@top-right',
'@top-right-corner',
'@bottom-left-corner',
'@bottom-left',
'@bottom-center',
'@bottom-right',
'@bottom-right-corner',
'@left-top',
'@left-middle',
'@left-bottom',
'@right-top',
'@right-middle',
'@right-bottom',
]
def parse_at_rule(self, rule, previous_rules, errors, context):
if rule.at_keyword in self.PAGE_MARGIN_AT_KEYWORDS:
if context != '@page':
raise ParseError(
rule, '{0} rule not allowed in {1}'.format(
rule.at_keyword, context))
if rule.head:
raise ParseError(
rule.head[0],
'unexpected {0} token in {1} rule header'.format(
rule.head[0].type, rule.at_keyword))
declarations, body_errors = self.parse_declaration_list(rule.body)
errors.extend(body_errors)
return MarginRule(
rule.at_keyword, declarations, rule.line, rule.column)
return super(CSSPage3Parser, self).parse_at_rule(
rule, previous_rules, errors, context)
def parse_page_selector(self, head):
"""Parse an @page selector.
:param head:
The ``head`` attribute of an unparsed :class:`AtRule`.
:returns:
A page selector. For CSS 2.1, this is 'first', 'left', 'right'
or None. 'blank' is added by GCPM.
:raises:
:class`~parsing.ParseError` on invalid selectors
"""
if not head:
return (None, None), (0, 0, 0)
if head[0].type == 'IDENT':
name = head.pop(0).value
while head and head[0].type == 'S':
head.pop(0)
if not head:
return (name, None), (1, 0, 0)
name_specificity = (1,)
else:
name = None
name_specificity = (0,)
if (len(head) == 2 and head[0].type == ':' and
head[1].type == 'IDENT'):
pseudo_class = head[1].value
specificity = {
'first': (1, 0), 'blank': (1, 0),
'left': (0, 1), 'right': (0, 1),
}.get(pseudo_class)
if specificity:
return (name, pseudo_class), (name_specificity + specificity)
raise ParseError(head[0], 'invalid @page selector')