# Source code for tinycss.fonts3

    # coding: utf-8
    """
        tinycss.colors3
        ---------------

        Parser for CSS 3 Fonts syntax:
        https://www.w3.org/TR/css-fonts-3/

        Adds support for font-face and font-feature-values rules.

        :copyright: (c) 2016 by Kozea.
        :license: BSD, see LICENSE for more details.
    """

    from __future__ import division, unicode_literals

    from .css21 import CSS21Parser, ParseError

    class FontFaceRule(object):
        """A parsed at-rule for font faces.

        .. attribute:: at_keyword

            Always ``'@font-face'``.

        .. 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):
            assert at_keyword == '@font-face'
            self.at_keyword = at_keyword
            self.declarations = declarations
            self.line = line
            self.column = column

    class FontFeatureValuesRule(object):
        """A parsed at-rule for font feature values.

        .. attribute:: at_keyword

            Always ``'@font-feature-values'``.

        .. attribute:: line

            Source line where this was read.

        .. attribute:: column

            Source column where this was read.

        .. attribute:: at_rules

            The list of parsed at-rules inside the @font-feature-values block, in
            source order.

        .. attribute:: family_names

            A list of strings representing font families.

        """

        def __init__(self, at_keyword, at_rules, family_names, line, column):
            assert at_keyword == '@font-feature-values'
            self.at_keyword = at_keyword
            self.family_names = family_names
            self.at_rules = at_rules
            self.line = line
            self.column = column

    class FontFeatureRule(object):
        """A parsed at-rule for font features.

        .. attribute:: at_keyword

            One of the 16 following strings:

            * ``@stylistic``
            * ``@styleset``
            * ``@character-variant``
            * ``@swash``
            * ``@ornaments``
            * ``@annotation``

        .. 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 CSSFonts3Parser(CSS21Parser):
        """Extend :class:`~.css21.CSS21Parser` for `CSS 3 Fonts`_ syntax.

        .. _CSS 3 Fonts: https://www.w3.org/TR/css-fonts-3/

        """

        FONT_FEATURE_VALUES_AT_KEYWORDS = [
            '@stylistic',
            '@styleset',
            '@character-variant',
            '@swash',
            '@ornaments',
            '@annotation',
        ]

        def parse_at_rule(self, rule, previous_rules, errors, context):
            if rule.at_keyword == '@font-face':
                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 FontFaceRule(
                    rule.at_keyword, declarations, rule.line, rule.column)
            elif rule.at_keyword == '@font-feature-values':
                family_names = tuple(
                    self.parse_font_feature_values_family_names(rule.head))
                at_rules, body_errors = (
                    self.parse_rules(rule.body or [], '@font-feature-values'))
                errors.extend(body_errors)
                return FontFeatureValuesRule(
                    rule.at_keyword, at_rules, family_names,
                    rule.line, rule.column)
            elif rule.at_keyword in self.FONT_FEATURE_VALUES_AT_KEYWORDS:
                if context != '@font-feature-values':
                    raise ParseError(
                        rule, '{0} rule not allowed in {1}'.format(
                            rule.at_keyword, context))
                declarations, body_errors = self.parse_declaration_list(rule.body)
                errors.extend(body_errors)
                return FontFeatureRule(
                    rule.at_keyword, declarations, rule.line, rule.column)
            return super(CSSFonts3Parser, self).parse_at_rule(
                rule, previous_rules, errors, context)

        def parse_font_feature_values_family_names(self, tokens):
            """Parse an @font-feature-values selector.

            :param tokens:
                An iterable of token, typically from the  ``head`` attribute of
                an unparsed :class:`AtRule`.
            :returns:
                A generator of strings representing font families.
            :raises:
                :class:`~.parsing.ParseError` on invalid selectors

            """
            family = ''
            current_string = False
            for token in tokens:
                if token.type == 'DELIM' and token.value == ',' and family:
                    yield family
                    family = ''
                    current_string = False
                elif token.type == 'STRING' and not family and (
                        current_string is False):
                    family = token.value
                    current_string = True
                elif token.type == 'IDENT' and not current_string:
                    if family:
                        family += ' '
                    family += token.value
                elif token.type != 'S':
                    family = ''
                    break
            if family:
                yield family
            else:
                raise ParseError(token, 'invalid @font-feature-values selector')