From 172665f6390a86daa1170924d44815a838fbe2ba Mon Sep 17 00:00:00 2001 From: goanpeca Date: Sun, 26 Apr 2020 22:12:26 -0500 Subject: [PATCH] Add list properties --- colosseum/constants.py | 54 ++++++++++++++++++++++++++++++++++- colosseum/declaration.py | 35 ++++++++++++----------- colosseum/parser.py | 61 ++++++++++++++++++++++++++++++++++++++++ colosseum/validators.py | 7 +++++ colosseum/wrappers.py | 4 +++ 5 files changed, 144 insertions(+), 17 deletions(-) diff --git a/colosseum/constants.py b/colosseum/constants.py index 431c9b6ae..c4e1b5cb9 100644 --- a/colosseum/constants.py +++ b/colosseum/constants.py @@ -1,6 +1,6 @@ from .validators import (ValidationError, is_border_spacing, is_color, is_integer, is_length, is_number, is_percentage, - is_quote, is_rect) + is_quote, is_rect, is_uri) class Choices: @@ -305,8 +305,60 @@ def value(self, context): # 12.5 Lists ###################################################################### # list_style_type +DISC = 'disc' +CIRCLE = 'circle' +SQUARE = 'square' +DECIMAL = 'decimal' +DECIMAL_LEADING_ZERO = 'decimal-leading-zero' +LOWER_ROMAN = 'lower-roman' +UPPER_ROMAN = 'upper-roman' +LOWER_GREEK = 'lower-greek' +LOWER_LATIN = 'lower-latin' +UPPER_LATIN = 'upper-latin' +ARMENIAN = 'armenian' +GEORGIAN = 'georgian' +LOWER_ALPHA = 'lower-alpha' +UPPER_ALPHA = 'upper-alpha' + + +LIST_TYPE_CHOICES = Choices( + DISC, + CIRCLE, + SQUARE, + DECIMAL, + DECIMAL_LEADING_ZERO, + LOWER_ROMAN, + UPPER_ROMAN, + LOWER_GREEK, + LOWER_LATIN, + UPPER_LATIN, + ARMENIAN, + GEORGIAN, + LOWER_ALPHA, + UPPER_ALPHA, + None, + explicit_defaulting_constants=[INHERIT], +) + # list_style_image +LIST_IMAGE_CHOICES = Choices( + None, + validators=[is_uri], + explicit_defaulting_constants=[INHERIT], +) + + # list_style_position +INSIDE = 'inside' +OUTSIDE = 'outside' + +LIST_POSITION_CHOICES = Choices( + INSIDE, + OUTSIDE, + explicit_defaulting_constants=[INHERIT], +) + + # list_style # 13. Paged media #################################################### diff --git a/colosseum/declaration.py b/colosseum/declaration.py index 4b2515373..fdd6fd08e 100644 --- a/colosseum/declaration.py +++ b/colosseum/declaration.py @@ -1,22 +1,22 @@ -from . import engine as css_engine -from . import parser +from . import engine as css_engine, parser from .constants import ( # noqa ALIGN_CONTENT_CHOICES, ALIGN_ITEMS_CHOICES, ALIGN_SELF_CHOICES, AUTO, BACKGROUND_COLOR_CHOICES, BORDER_COLLAPSE_CHOICES, BORDER_COLOR_CHOICES, BORDER_SPACING_CHOICES, BORDER_STYLE_CHOICES, BORDER_WIDTH_CHOICES, BOX_OFFSET_CHOICES, CAPTION_SIDE_CHOICES, CLEAR_CHOICES, CLIP_CHOICES, - COLOR_CHOICES, DIRECTION_CHOICES, DISPLAY_CHOICES, EMPTY_CELLS_CHOICES, - FLEX_BASIS_CHOICES, FLEX_DIRECTION_CHOICES, FLEX_GROW_CHOICES, - FLEX_SHRINK_CHOICES, FLEX_START, FLEX_WRAP_CHOICES, FLOAT_CHOICES, - GRID_AUTO_CHOICES, GRID_AUTO_FLOW_CHOICES, GRID_GAP_CHOICES, + COLOR_CHOICES, DIRECTION_CHOICES, DISC, DISPLAY_CHOICES, + EMPTY_CELLS_CHOICES, FLEX_BASIS_CHOICES, FLEX_DIRECTION_CHOICES, + FLEX_GROW_CHOICES, FLEX_SHRINK_CHOICES, FLEX_START, FLEX_WRAP_CHOICES, + FLOAT_CHOICES, GRID_AUTO_CHOICES, GRID_AUTO_FLOW_CHOICES, GRID_GAP_CHOICES, GRID_PLACEMENT_CHOICES, GRID_TEMPLATE_AREA_CHOICES, GRID_TEMPLATE_CHOICES, INITIAL, INLINE, INVERT, JUSTIFY_CONTENT_CHOICES, LETTER_SPACING_CHOICES, - LTR, MARGIN_CHOICES, MAX_SIZE_CHOICES, MEDIUM, MIN_SIZE_CHOICES, NORMAL, - NOWRAP, ORDER_CHOICES, ORPHANS_CHOICES, OUTLINE_COLOR_CHOICES, - OUTLINE_STYLE_CHOICES, OUTLINE_WIDTH_CHOICES, OVERFLOW_CHOICES, + LIST_IMAGE_CHOICES, LIST_POSITION_CHOICES, LIST_TYPE_CHOICES, LTR, + MARGIN_CHOICES, MAX_SIZE_CHOICES, MEDIUM, MIN_SIZE_CHOICES, NORMAL, NOWRAP, + ORDER_CHOICES, ORPHANS_CHOICES, OUTLINE_COLOR_CHOICES, + OUTLINE_STYLE_CHOICES, OUTLINE_WIDTH_CHOICES, OUTSIDE, OVERFLOW_CHOICES, PADDING_CHOICES, PAGE_BREAK_AFTER_CHOICES, PAGE_BREAK_BEFORE_CHOICES, - PAGE_BREAK_INSIDE_CHOICES, POSITION_CHOICES, QUOTES_CHOICES, ROW, - SEPARATE, SHOW, SIZE_CHOICES, STATIC, STRETCH, TABLE_LAYOUT_CHOICES, + PAGE_BREAK_INSIDE_CHOICES, POSITION_CHOICES, QUOTES_CHOICES, ROW, SEPARATE, + SHOW, SIZE_CHOICES, STATIC, STRETCH, TABLE_LAYOUT_CHOICES, TEXT_ALIGN_CHOICES, TEXT_DECORATION_CHOICES, TEXT_INDENT_CHOICES, TEXT_TRANSFORM_CHOICES, TOP, TRANSPARENT, UNICODE_BIDI_CHOICES, VISIBILITY_CHOICES, VISIBLE, WHITE_SPACE_CHOICES, WIDOWS_CHOICES, @@ -24,7 +24,10 @@ TextAlignInitialValue, default, ) from .exceptions import ValidationError -from .wrappers import Border, BorderBottom, BorderLeft, BorderRight, BorderTop, Outline +from .wrappers import ( + Border, BorderBottom, BorderLeft, BorderRight, BorderTop, ListStyle, + Outline, +) _CSS_PROPERTIES = set() @@ -329,10 +332,10 @@ def __init__(self, **style): # counter-increment # 12.5 Lists - # list_style_type - # list_style_image - # list_style_position - # list_style + list_style_type = validated_property('list_style_type', choices=LIST_TYPE_CHOICES, initial=DISC) + list_style_image = validated_property('list_style_type', choices=LIST_IMAGE_CHOICES, initial=None) + list_style_position = validated_property('list_style_position', choices=LIST_POSITION_CHOICES, initial=OUTSIDE) + list_style = validated_shorthand_property('list_style', parser=parser.list_style, wrapper=ListStyle) # 13. Paged media #################################################### # 13.3.1 Page break properties diff --git a/colosseum/parser.py b/colosseum/parser.py index a2b734a6c..12b2d22bf 100644 --- a/colosseum/parser.py +++ b/colosseum/parser.py @@ -369,3 +369,64 @@ def border_bottom(value): def border_top(value): """Parse border string into a dictionary of outline properties.""" return border(value, direction='top') + + +############################################################################## +# List shorthands +############################################################################## +def _parse_list_style_property_part(value, list_style_dict): + """Parse list style shorthand property part for known properties.""" + from .constants import ( # noqa + LIST_TYPE_CHOICES, LIST_IMAGE_CHOICES, LIST_POSITION_CHOICES + ) + + for property_name, choices in {'list_style_type': LIST_TYPE_CHOICES, + 'list_style_image': LIST_IMAGE_CHOICES, + 'list_style_position': LIST_POSITION_CHOICES}.items(): + try: + value = choices.validate(value) + except (ValueError, ValidationError): + continue + + if property_name in list_style_dict: + raise ValueError('Invalid duplicated property!') + + list_style_dict[property_name] = value + return list_style_dict + + raise ValueError('List style value "{value}" not valid!'.format(value=value)) + + +def list_style(value): + """ + Parse list style string into a dictionary of list style properties. + + The font CSS property is a shorthand for list-style-type, list-style-image, and list-style-position. + + Reference: + - https://www.w3.org/TR/2011/REC-CSS2-20110607/generate.html#lists + """ + if value: + if isinstance(value, str): + values = [val.strip() for val in value.split()] + elif isinstance(value, Sequence): + values = value + else: + raise ValueError('Unknown list style %s ' % value) + else: + raise ValueError('Unknown list style %s ' % value) + + # We iteratively split by the first left hand space found and try to validate if that part + # is a valid or or (which can come in any order) + + # We use this dictionary to store parsed values and check that values properties are not + # duplicated + list_style_dict = {} + for idx, part in enumerate(values): + if idx > 2: + # Outline can have a maximum of 3 parts + raise ValueError('List style property shorthand contains too many parts!') + + list_style_dict = _parse_list_style_property_part(part, list_style_dict) + + return list_style_dict diff --git a/colosseum/validators.py b/colosseum/validators.py index 4ed0aebc2..96c0800c5 100644 --- a/colosseum/validators.py +++ b/colosseum/validators.py @@ -147,3 +147,10 @@ def is_quote(value): is_quote.description = '[ ]+' + + +def is_uri(value): + return value + + +is_uri.description = 'TODO' diff --git a/colosseum/wrappers.py b/colosseum/wrappers.py index c1688cb09..65b949323 100644 --- a/colosseum/wrappers.py +++ b/colosseum/wrappers.py @@ -151,3 +151,7 @@ class BorderLeft(Shorthand): class Border(Shorthand): VALID_KEYS = ['border_width', 'border_style', 'border_color'] + + +class ListStyle(Shorthand): + VALID_KEYS = ['list_style_type', 'list_style_image', 'list_style_position']