weasyprint 65.1__py3-none-any.whl → 67.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- weasyprint/__init__.py +17 -7
- weasyprint/__main__.py +21 -10
- weasyprint/anchors.py +4 -4
- weasyprint/css/__init__.py +732 -67
- weasyprint/css/computed_values.py +65 -170
- weasyprint/css/counters.py +1 -1
- weasyprint/css/functions.py +206 -0
- weasyprint/css/html5_ua.css +3 -7
- weasyprint/css/html5_ua_form.css +2 -2
- weasyprint/css/media_queries.py +3 -1
- weasyprint/css/properties.py +6 -2
- weasyprint/css/{utils.py → tokens.py} +306 -397
- weasyprint/css/units.py +91 -0
- weasyprint/css/validation/__init__.py +1 -1
- weasyprint/css/validation/descriptors.py +47 -19
- weasyprint/css/validation/expanders.py +7 -8
- weasyprint/css/validation/properties.py +341 -357
- weasyprint/document.py +20 -19
- weasyprint/draw/__init__.py +56 -63
- weasyprint/draw/border.py +121 -69
- weasyprint/draw/color.py +1 -1
- weasyprint/draw/text.py +60 -41
- weasyprint/formatting_structure/boxes.py +24 -5
- weasyprint/formatting_structure/build.py +33 -45
- weasyprint/images.py +76 -62
- weasyprint/layout/__init__.py +32 -26
- weasyprint/layout/absolute.py +7 -6
- weasyprint/layout/background.py +7 -7
- weasyprint/layout/block.py +195 -152
- weasyprint/layout/column.py +19 -24
- weasyprint/layout/flex.py +54 -26
- weasyprint/layout/float.py +12 -7
- weasyprint/layout/grid.py +284 -90
- weasyprint/layout/inline.py +121 -68
- weasyprint/layout/page.py +45 -12
- weasyprint/layout/percent.py +14 -10
- weasyprint/layout/preferred.py +105 -63
- weasyprint/layout/replaced.py +9 -6
- weasyprint/layout/table.py +16 -9
- weasyprint/pdf/__init__.py +58 -18
- weasyprint/pdf/anchors.py +3 -4
- weasyprint/pdf/fonts.py +126 -69
- weasyprint/pdf/metadata.py +36 -4
- weasyprint/pdf/pdfa.py +19 -3
- weasyprint/pdf/pdfua.py +7 -115
- weasyprint/pdf/pdfx.py +83 -0
- weasyprint/pdf/stream.py +57 -49
- weasyprint/pdf/tags.py +307 -0
- weasyprint/stacking.py +14 -15
- weasyprint/svg/__init__.py +59 -32
- weasyprint/svg/bounding_box.py +4 -2
- weasyprint/svg/defs.py +4 -9
- weasyprint/svg/images.py +11 -3
- weasyprint/svg/text.py +11 -2
- weasyprint/svg/utils.py +15 -8
- weasyprint/text/constants.py +1 -1
- weasyprint/text/ffi.py +4 -3
- weasyprint/text/fonts.py +13 -5
- weasyprint/text/line_break.py +146 -43
- weasyprint/urls.py +41 -13
- {weasyprint-65.1.dist-info → weasyprint-67.0.dist-info}/METADATA +5 -6
- weasyprint-67.0.dist-info/RECORD +77 -0
- weasyprint/draw/stack.py +0 -13
- weasyprint-65.1.dist-info/RECORD +0 -74
- {weasyprint-65.1.dist-info → weasyprint-67.0.dist-info}/WHEEL +0 -0
- {weasyprint-65.1.dist-info → weasyprint-67.0.dist-info}/entry_points.txt +0 -0
- {weasyprint-65.1.dist-info → weasyprint-67.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -7,17 +7,17 @@ See https://www.w3.org/TR/CSS21/propidx.html and various CSS3 modules.
|
|
|
7
7
|
from math import inf
|
|
8
8
|
|
|
9
9
|
from tinycss2 import parse_component_value_list
|
|
10
|
-
from tinycss2.
|
|
10
|
+
from tinycss2.color5 import parse_color
|
|
11
11
|
|
|
12
12
|
from .. import computed_values
|
|
13
|
+
from ..functions import Function, check_var
|
|
13
14
|
from ..properties import KNOWN_PROPERTIES, ZERO_PIXELS, Dimension
|
|
14
15
|
|
|
15
|
-
from ..
|
|
16
|
-
InvalidValues, Pending,
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
remove_whitespace, single_keyword, single_token)
|
|
16
|
+
from ..tokens import ( # isort:skip
|
|
17
|
+
InvalidValues, Pending, comma_separated_list, get_angle, get_content_list,
|
|
18
|
+
get_content_list_token, get_custom_ident, get_image, get_keyword, get_length,
|
|
19
|
+
get_number, get_percentage, get_resolution, get_single_keyword, get_url,
|
|
20
|
+
parse_2d_position, parse_position, remove_whitespace, single_keyword, single_token)
|
|
21
21
|
|
|
22
22
|
PREFIX = '-weasy-'
|
|
23
23
|
PROPRIETARY = set()
|
|
@@ -94,7 +94,7 @@ def validate_non_shorthand(tokens, name, base_url=None, required=False):
|
|
|
94
94
|
|
|
95
95
|
function = PROPERTIES[name]
|
|
96
96
|
for token in tokens:
|
|
97
|
-
if
|
|
97
|
+
if check_var(token):
|
|
98
98
|
# Found CSS variable, return pending-substitution values.
|
|
99
99
|
return ((name, PendingProperty(tokens, name)),)
|
|
100
100
|
|
|
@@ -128,7 +128,8 @@ def background_attachment(keyword):
|
|
|
128
128
|
@property('text-decoration-color')
|
|
129
129
|
@single_token
|
|
130
130
|
def other_colors(token):
|
|
131
|
-
|
|
131
|
+
if parse_color(token):
|
|
132
|
+
return token
|
|
132
133
|
|
|
133
134
|
|
|
134
135
|
@property()
|
|
@@ -137,7 +138,7 @@ def outline_color(token):
|
|
|
137
138
|
if get_keyword(token) == 'invert':
|
|
138
139
|
return 'currentcolor'
|
|
139
140
|
else:
|
|
140
|
-
return
|
|
141
|
+
return token
|
|
141
142
|
|
|
142
143
|
|
|
143
144
|
@property()
|
|
@@ -161,7 +162,7 @@ def color(token):
|
|
|
161
162
|
if result == 'currentcolor':
|
|
162
163
|
return 'inherit'
|
|
163
164
|
else:
|
|
164
|
-
return
|
|
165
|
+
return token
|
|
165
166
|
|
|
166
167
|
|
|
167
168
|
@property('background-image', wants_base_url=True)
|
|
@@ -278,8 +279,7 @@ def border_spacing(tokens):
|
|
|
278
279
|
@property('border-top-left-radius')
|
|
279
280
|
def border_corner_radius(tokens):
|
|
280
281
|
"""Validator for the `border-*-radius` properties."""
|
|
281
|
-
lengths = [
|
|
282
|
-
get_length(token, negative=False, percentage=True) for token in tokens]
|
|
282
|
+
lengths = [get_length(token, negative=False, percentage=True) for token in tokens]
|
|
283
283
|
if all(lengths):
|
|
284
284
|
if len(lengths) == 1:
|
|
285
285
|
return (lengths[0], lengths[0])
|
|
@@ -344,12 +344,10 @@ def continue_(keyword):
|
|
|
344
344
|
@property(unstable=True)
|
|
345
345
|
@single_token
|
|
346
346
|
def max_lines(token):
|
|
347
|
-
if
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
if keyword == 'none':
|
|
352
|
-
return keyword
|
|
347
|
+
if number := get_number(token, negative=False, integer=True):
|
|
348
|
+
return number.value
|
|
349
|
+
elif get_keyword(token) == 'none':
|
|
350
|
+
return 'none'
|
|
353
351
|
|
|
354
352
|
|
|
355
353
|
@property(unstable=True)
|
|
@@ -374,8 +372,7 @@ def page(token):
|
|
|
374
372
|
@single_token
|
|
375
373
|
def bleed(token):
|
|
376
374
|
"""``bleed`` property validation."""
|
|
377
|
-
|
|
378
|
-
if keyword == 'auto':
|
|
375
|
+
if get_keyword(token) == 'auto':
|
|
379
376
|
return 'auto'
|
|
380
377
|
else:
|
|
381
378
|
return get_length(token)
|
|
@@ -389,8 +386,7 @@ def marks(tokens):
|
|
|
389
386
|
if 'crop' in keywords and 'cross' in keywords:
|
|
390
387
|
return keywords
|
|
391
388
|
elif len(tokens) == 1:
|
|
392
|
-
keyword
|
|
393
|
-
if keyword in ('crop', 'cross'):
|
|
389
|
+
if (keyword := get_keyword(tokens[0])) in ('crop', 'cross'):
|
|
394
390
|
return (keyword,)
|
|
395
391
|
elif keyword == 'none':
|
|
396
392
|
return ()
|
|
@@ -413,11 +409,9 @@ def outline_style(keyword):
|
|
|
413
409
|
@single_token
|
|
414
410
|
def border_width(token):
|
|
415
411
|
"""Border, column rule and outline widths properties validation."""
|
|
416
|
-
length
|
|
417
|
-
if length:
|
|
412
|
+
if length := get_length(token, negative=False):
|
|
418
413
|
return length
|
|
419
|
-
keyword
|
|
420
|
-
if keyword in ('thin', 'medium', 'thick'):
|
|
414
|
+
if (keyword := get_keyword(token)) in ('thin', 'medium', 'thick'):
|
|
421
415
|
return keyword
|
|
422
416
|
|
|
423
417
|
|
|
@@ -437,13 +431,13 @@ def border_image_slice(tokens):
|
|
|
437
431
|
fill = False
|
|
438
432
|
for i, token in enumerate(tokens):
|
|
439
433
|
# Don't use get_length() because a dimension with a unit is disallowed.
|
|
440
|
-
if
|
|
441
|
-
values.append(
|
|
442
|
-
elif token.type == 'number' and token.value >= 0:
|
|
443
|
-
values.append(Dimension(token.value, None))
|
|
434
|
+
if percentage := get_percentage(token, negative=False):
|
|
435
|
+
values.append(percentage)
|
|
444
436
|
elif get_keyword(token) == 'fill' and not fill and i in (0, len(tokens) - 1):
|
|
445
437
|
fill = True
|
|
446
438
|
values.append('fill')
|
|
439
|
+
elif number := get_number(token, negative=False):
|
|
440
|
+
values.append(number)
|
|
447
441
|
else:
|
|
448
442
|
return
|
|
449
443
|
|
|
@@ -458,13 +452,12 @@ def border_image_width(tokens):
|
|
|
458
452
|
for token in tokens:
|
|
459
453
|
if get_keyword(token) == 'auto':
|
|
460
454
|
values.append('auto')
|
|
461
|
-
elif
|
|
462
|
-
values.append(
|
|
455
|
+
elif number := get_number(token, negative=False):
|
|
456
|
+
values.append(number)
|
|
457
|
+
elif length := get_length(token, negative=False, percentage=True):
|
|
458
|
+
values.append(length)
|
|
463
459
|
else:
|
|
464
|
-
|
|
465
|
-
values.append(length)
|
|
466
|
-
else:
|
|
467
|
-
return
|
|
460
|
+
return
|
|
468
461
|
|
|
469
462
|
if 1 <= len(values) <= 4:
|
|
470
463
|
return tuple(values)
|
|
@@ -475,13 +468,12 @@ def border_image_width(tokens):
|
|
|
475
468
|
def border_image_outset(tokens):
|
|
476
469
|
values = []
|
|
477
470
|
for token in tokens:
|
|
478
|
-
if
|
|
479
|
-
values.append(
|
|
471
|
+
if number := get_number(token, negative=False):
|
|
472
|
+
values.append(number)
|
|
473
|
+
elif length := get_length(token, negative=False):
|
|
474
|
+
values.append(length)
|
|
480
475
|
else:
|
|
481
|
-
|
|
482
|
-
values.append(length)
|
|
483
|
-
else:
|
|
484
|
-
return
|
|
476
|
+
return
|
|
485
477
|
|
|
486
478
|
if 1 <= len(values) <= 4:
|
|
487
479
|
return tuple(values)
|
|
@@ -506,8 +498,7 @@ def mask_border_mode(keyword):
|
|
|
506
498
|
@single_token
|
|
507
499
|
def column_width(token):
|
|
508
500
|
"""``column-width`` property validation."""
|
|
509
|
-
length
|
|
510
|
-
if length:
|
|
501
|
+
if length := get_length(token, negative=False):
|
|
511
502
|
return length
|
|
512
503
|
keyword = get_keyword(token)
|
|
513
504
|
if keyword == 'auto':
|
|
@@ -546,21 +537,19 @@ def clear(keyword):
|
|
|
546
537
|
@single_token
|
|
547
538
|
def clip(token):
|
|
548
539
|
"""Validation for the ``clip`` property."""
|
|
549
|
-
function =
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
return tuple(values)
|
|
563
|
-
if get_keyword(token) == 'auto':
|
|
540
|
+
function = Function(token)
|
|
541
|
+
arguments = function.split_comma()
|
|
542
|
+
if function.name == 'rect' and len(arguments) == 4:
|
|
543
|
+
values = []
|
|
544
|
+
for argument in arguments:
|
|
545
|
+
if get_keyword(argument) == 'auto':
|
|
546
|
+
values.append('auto')
|
|
547
|
+
elif length := get_length(argument):
|
|
548
|
+
values.append(length)
|
|
549
|
+
else:
|
|
550
|
+
return
|
|
551
|
+
return tuple(values)
|
|
552
|
+
elif get_keyword(token) == 'auto':
|
|
564
553
|
return ()
|
|
565
554
|
|
|
566
555
|
|
|
@@ -571,12 +560,9 @@ def content(tokens, base_url):
|
|
|
571
560
|
tokens = list(tokens)
|
|
572
561
|
parsed_tokens = []
|
|
573
562
|
while tokens:
|
|
574
|
-
if len(tokens) >= 2 and
|
|
575
|
-
tokens[1].type == 'literal' and tokens[1].value == ','):
|
|
563
|
+
if len(tokens) >= 2 and tokens[1].type == 'literal' and tokens[1].value == ',':
|
|
576
564
|
token, tokens = tokens[0], tokens[2:]
|
|
577
|
-
parsed_token
|
|
578
|
-
get_image(token, base_url) or get_url(token, base_url))
|
|
579
|
-
if parsed_token:
|
|
565
|
+
if parsed_token := get_image(token, base_url) or get_url(token, base_url):
|
|
580
566
|
parsed_tokens.append(parsed_token)
|
|
581
567
|
else:
|
|
582
568
|
return
|
|
@@ -627,14 +613,13 @@ def counter(tokens, default_integer):
|
|
|
627
613
|
if counter_name in ('none', 'initial', 'inherit'):
|
|
628
614
|
raise InvalidValues(f'Invalid counter name: {counter_name}')
|
|
629
615
|
token = next(tokens, None)
|
|
630
|
-
if token
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
integer = token.int_value
|
|
616
|
+
if token and (number := get_number(token, integer=True)):
|
|
617
|
+
# Found an integer. Use it and get the next token.
|
|
618
|
+
integer = number.value
|
|
634
619
|
token = next(tokens, None)
|
|
635
620
|
else:
|
|
636
|
-
# Not an integer. Might be the next counter name.
|
|
637
|
-
#
|
|
621
|
+
# Not an integer. Might be the next counter name. Keep `token` for the next
|
|
622
|
+
# loop iteration.
|
|
638
623
|
integer = default_integer
|
|
639
624
|
results.append((counter_name, integer))
|
|
640
625
|
return tuple(results)
|
|
@@ -652,8 +637,7 @@ def counter(tokens, default_integer):
|
|
|
652
637
|
@single_token
|
|
653
638
|
def lenght_precentage_or_auto(token):
|
|
654
639
|
"""``margin-*`` and various other properties validation."""
|
|
655
|
-
length
|
|
656
|
-
if length:
|
|
640
|
+
if length := get_length(token, percentage=True):
|
|
657
641
|
return length
|
|
658
642
|
if get_keyword(token) == 'auto':
|
|
659
643
|
return 'auto'
|
|
@@ -664,8 +648,7 @@ def lenght_precentage_or_auto(token):
|
|
|
664
648
|
@single_token
|
|
665
649
|
def width_height(token):
|
|
666
650
|
"""Validation for the ``width`` and ``height`` properties."""
|
|
667
|
-
length
|
|
668
|
-
if length:
|
|
651
|
+
if length := get_length(token, negative=False, percentage=True):
|
|
669
652
|
return length
|
|
670
653
|
if get_keyword(token) == 'auto':
|
|
671
654
|
return 'auto'
|
|
@@ -676,8 +659,7 @@ def width_height(token):
|
|
|
676
659
|
@single_token
|
|
677
660
|
def gap(token):
|
|
678
661
|
"""Validation for the ``column-gap`` and ``row-gap`` properties."""
|
|
679
|
-
length
|
|
680
|
-
if length:
|
|
662
|
+
if length := get_length(token, percentage=True, negative=False):
|
|
681
663
|
return length
|
|
682
664
|
keyword = get_keyword(token)
|
|
683
665
|
if keyword == 'normal':
|
|
@@ -867,9 +849,8 @@ def font_feature_settings(tokens):
|
|
|
867
849
|
tokens, token = tokens[:-1], tokens[-1]
|
|
868
850
|
if token.type == 'ident':
|
|
869
851
|
value = {'on': 1, 'off': 0}.get(token.value)
|
|
870
|
-
elif (token
|
|
871
|
-
|
|
872
|
-
value = token.int_value
|
|
852
|
+
elif number := get_number(token, negative=False):
|
|
853
|
+
value = number.value
|
|
873
854
|
elif len(tokens) == 1:
|
|
874
855
|
value = 1
|
|
875
856
|
|
|
@@ -943,8 +924,7 @@ def font_variation_settings(tokens):
|
|
|
943
924
|
@single_token
|
|
944
925
|
def font_size(token):
|
|
945
926
|
"""``font-size`` property validation."""
|
|
946
|
-
length
|
|
947
|
-
if length:
|
|
927
|
+
if length := get_length(token, negative=False, percentage=True):
|
|
948
928
|
return length
|
|
949
929
|
font_size_keyword = get_keyword(token)
|
|
950
930
|
if font_size_keyword in ('smaller', 'larger'):
|
|
@@ -1005,8 +985,7 @@ def spacing(token):
|
|
|
1005
985
|
"""Validation for ``letter-spacing`` and ``word-spacing``."""
|
|
1006
986
|
if get_keyword(token) == 'normal':
|
|
1007
987
|
return 'normal'
|
|
1008
|
-
length
|
|
1009
|
-
if length:
|
|
988
|
+
if length := get_length(token):
|
|
1010
989
|
return length
|
|
1011
990
|
|
|
1012
991
|
|
|
@@ -1014,8 +993,7 @@ def spacing(token):
|
|
|
1014
993
|
@single_token
|
|
1015
994
|
def outline_offset(token):
|
|
1016
995
|
"""Validation for ``outline-offset``."""
|
|
1017
|
-
length
|
|
1018
|
-
if length:
|
|
996
|
+
if length := get_length(token):
|
|
1019
997
|
return length
|
|
1020
998
|
|
|
1021
999
|
|
|
@@ -1025,12 +1003,10 @@ def line_height(token):
|
|
|
1025
1003
|
"""``line-height`` property validation."""
|
|
1026
1004
|
if get_keyword(token) == 'normal':
|
|
1027
1005
|
return 'normal'
|
|
1028
|
-
|
|
1029
|
-
return
|
|
1030
|
-
|
|
1031
|
-
return
|
|
1032
|
-
elif token.type == 'dimension' and token.value >= 0:
|
|
1033
|
-
return get_length(token)
|
|
1006
|
+
elif number := get_number(token, negative=False):
|
|
1007
|
+
return number
|
|
1008
|
+
elif length := get_length(token, negative=False, percentage=True):
|
|
1009
|
+
return length
|
|
1034
1010
|
|
|
1035
1011
|
|
|
1036
1012
|
@property()
|
|
@@ -1049,30 +1025,29 @@ def list_style_type(token):
|
|
|
1049
1025
|
elif token.type == 'string':
|
|
1050
1026
|
return ('string', token.value)
|
|
1051
1027
|
elif token.type == 'function' and token.name == 'symbols':
|
|
1052
|
-
allowed_types = (
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
if function_arguments[0].
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
arguments.append(function_arguments[0].value)
|
|
1061
|
-
else:
|
|
1062
|
-
return
|
|
1028
|
+
allowed_types = ('cyclic', 'numeric', 'alphabetic', 'symbolic', 'fixed')
|
|
1029
|
+
if not (function_arguments := remove_whitespace(token.arguments)):
|
|
1030
|
+
return
|
|
1031
|
+
arguments = []
|
|
1032
|
+
if function_arguments[0].type == 'ident':
|
|
1033
|
+
if function_arguments[0].value in allowed_types:
|
|
1034
|
+
index = 1
|
|
1035
|
+
arguments.append(function_arguments[0].value)
|
|
1063
1036
|
else:
|
|
1064
|
-
arguments.append('symbolic')
|
|
1065
|
-
index = 0
|
|
1066
|
-
if len(function_arguments) < index + 1:
|
|
1067
1037
|
return
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1038
|
+
else:
|
|
1039
|
+
arguments.append('symbolic')
|
|
1040
|
+
index = 0
|
|
1041
|
+
if len(function_arguments) < index + 1:
|
|
1042
|
+
return
|
|
1043
|
+
for i in range(index, len(function_arguments)):
|
|
1044
|
+
if function_arguments[i].type != 'string':
|
|
1045
|
+
return
|
|
1046
|
+
arguments.append(function_arguments[i].value)
|
|
1047
|
+
if arguments[0] in ('alphabetic', 'numeric'):
|
|
1048
|
+
if len(arguments) < 3:
|
|
1049
|
+
return
|
|
1050
|
+
return ('symbols()', tuple(arguments))
|
|
1076
1051
|
|
|
1077
1052
|
|
|
1078
1053
|
@property('min-width')
|
|
@@ -1081,9 +1056,8 @@ def list_style_type(token):
|
|
|
1081
1056
|
def min_width_height(token):
|
|
1082
1057
|
"""``min-width`` and ``min-height`` properties validation."""
|
|
1083
1058
|
# See https://www.w3.org/TR/css-flexbox-1/#min-size-auto
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
return keyword
|
|
1059
|
+
if get_keyword(token) == 'auto':
|
|
1060
|
+
return 'auto'
|
|
1087
1061
|
else:
|
|
1088
1062
|
return length_or_precentage([token])
|
|
1089
1063
|
|
|
@@ -1095,8 +1069,7 @@ def min_width_height(token):
|
|
|
1095
1069
|
@single_token
|
|
1096
1070
|
def length_or_precentage(token):
|
|
1097
1071
|
"""``padding-*`` properties validation."""
|
|
1098
|
-
length
|
|
1099
|
-
if length:
|
|
1072
|
+
if length := get_length(token, negative=False, percentage=True):
|
|
1100
1073
|
return length
|
|
1101
1074
|
|
|
1102
1075
|
|
|
@@ -1105,8 +1078,7 @@ def length_or_precentage(token):
|
|
|
1105
1078
|
@single_token
|
|
1106
1079
|
def max_width_height(token):
|
|
1107
1080
|
"""Validation for max-width and max-height"""
|
|
1108
|
-
length
|
|
1109
|
-
if length:
|
|
1081
|
+
if length := get_length(token, negative=False, percentage=True):
|
|
1110
1082
|
return length
|
|
1111
1083
|
if get_keyword(token) == 'none':
|
|
1112
1084
|
return Dimension(inf, 'px')
|
|
@@ -1116,10 +1088,10 @@ def max_width_height(token):
|
|
|
1116
1088
|
@single_token
|
|
1117
1089
|
def opacity(token):
|
|
1118
1090
|
"""Validation for the ``opacity`` property."""
|
|
1119
|
-
if
|
|
1120
|
-
return min(1, max(0,
|
|
1121
|
-
|
|
1122
|
-
return min(1, max(0,
|
|
1091
|
+
if number := get_number(token):
|
|
1092
|
+
return min(1, max(0, number.value))
|
|
1093
|
+
elif percentage := get_percentage(token):
|
|
1094
|
+
return min(1, max(0, percentage.value / 100))
|
|
1123
1095
|
|
|
1124
1096
|
|
|
1125
1097
|
@property()
|
|
@@ -1128,8 +1100,8 @@ def z_index(token):
|
|
|
1128
1100
|
"""Validation for the ``z-index`` property."""
|
|
1129
1101
|
if get_keyword(token) == 'auto':
|
|
1130
1102
|
return 'auto'
|
|
1131
|
-
|
|
1132
|
-
return
|
|
1103
|
+
elif number := get_number(token, integer=True):
|
|
1104
|
+
return number.value
|
|
1133
1105
|
|
|
1134
1106
|
|
|
1135
1107
|
@property('orphans')
|
|
@@ -1137,21 +1109,19 @@ def z_index(token):
|
|
|
1137
1109
|
@single_token
|
|
1138
1110
|
def orphans_widows(token):
|
|
1139
1111
|
"""Validation for the ``orphans`` and ``widows`` properties."""
|
|
1140
|
-
if
|
|
1141
|
-
value
|
|
1142
|
-
|
|
1143
|
-
return value
|
|
1112
|
+
if number := get_number(token, negative=False, integer=True):
|
|
1113
|
+
if number.value >= 1:
|
|
1114
|
+
return number.value
|
|
1144
1115
|
|
|
1145
1116
|
|
|
1146
1117
|
@property(unstable=True)
|
|
1147
1118
|
@single_token
|
|
1148
1119
|
def column_count(token):
|
|
1149
1120
|
"""Validation for the ``column-count`` property."""
|
|
1150
|
-
if
|
|
1151
|
-
value
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
if get_keyword(token) == 'auto':
|
|
1121
|
+
if number := get_number(token, negative=False, integer=True):
|
|
1122
|
+
if number.value >= 1:
|
|
1123
|
+
return number.value
|
|
1124
|
+
elif get_keyword(token) == 'auto':
|
|
1155
1125
|
return 'auto'
|
|
1156
1126
|
|
|
1157
1127
|
|
|
@@ -1214,15 +1184,13 @@ def text_align_all(keyword):
|
|
|
1214
1184
|
@single_keyword
|
|
1215
1185
|
def text_align_last(keyword):
|
|
1216
1186
|
"""``text-align-last`` property validation."""
|
|
1217
|
-
return keyword in (
|
|
1218
|
-
'auto', 'left', 'right', 'center', 'justify', 'start', 'end')
|
|
1187
|
+
return keyword in ('auto', 'left', 'right', 'center', 'justify', 'start', 'end')
|
|
1219
1188
|
|
|
1220
1189
|
|
|
1221
1190
|
@property()
|
|
1222
1191
|
def text_decoration_line(tokens):
|
|
1223
1192
|
"""``text-decoration-line`` property validation."""
|
|
1224
|
-
keywords
|
|
1225
|
-
if keywords == {'none'}:
|
|
1193
|
+
if (keywords := {get_keyword(token) for token in tokens}) == {'none'}:
|
|
1226
1194
|
return 'none'
|
|
1227
1195
|
allowed_values = {'underline', 'overline', 'line-through', 'blink'}
|
|
1228
1196
|
if len(tokens) == len(keywords) and keywords.issubset(allowed_values):
|
|
@@ -1241,10 +1209,9 @@ def text_decoration_style(keyword):
|
|
|
1241
1209
|
@single_token
|
|
1242
1210
|
def text_decoration_thickness(token):
|
|
1243
1211
|
"""``text-decoration-thickness`` property validation."""
|
|
1244
|
-
length
|
|
1245
|
-
if length:
|
|
1212
|
+
if length := get_length(token, percentage=True):
|
|
1246
1213
|
return length
|
|
1247
|
-
|
|
1214
|
+
elif keyword := get_keyword(token) in ('auto', 'from-font'):
|
|
1248
1215
|
return keyword
|
|
1249
1216
|
|
|
1250
1217
|
|
|
@@ -1252,8 +1219,7 @@ def text_decoration_thickness(token):
|
|
|
1252
1219
|
@single_token
|
|
1253
1220
|
def text_indent(token):
|
|
1254
1221
|
"""``text-indent`` property validation."""
|
|
1255
|
-
length
|
|
1256
|
-
if length:
|
|
1222
|
+
if length := get_length(token, percentage=True):
|
|
1257
1223
|
return length
|
|
1258
1224
|
|
|
1259
1225
|
|
|
@@ -1261,16 +1227,14 @@ def text_indent(token):
|
|
|
1261
1227
|
@single_keyword
|
|
1262
1228
|
def text_transform(keyword):
|
|
1263
1229
|
"""``text-align`` property validation."""
|
|
1264
|
-
return keyword in (
|
|
1265
|
-
'none', 'uppercase', 'lowercase', 'capitalize', 'full-width')
|
|
1230
|
+
return keyword in ('none', 'uppercase', 'lowercase', 'capitalize', 'full-width')
|
|
1266
1231
|
|
|
1267
1232
|
|
|
1268
1233
|
@property()
|
|
1269
1234
|
@single_token
|
|
1270
1235
|
def vertical_align(token):
|
|
1271
1236
|
"""Validation for the ``vertical-align`` property"""
|
|
1272
|
-
length
|
|
1273
|
-
if length:
|
|
1237
|
+
if length := get_length(token, percentage=True):
|
|
1274
1238
|
return length
|
|
1275
1239
|
keyword = get_keyword(token)
|
|
1276
1240
|
if keyword in ('baseline', 'middle', 'sub', 'super',
|
|
@@ -1310,10 +1274,9 @@ def word_break(keyword):
|
|
|
1310
1274
|
@single_token
|
|
1311
1275
|
def flex_basis(token):
|
|
1312
1276
|
"""``flex-basis`` property validation."""
|
|
1313
|
-
basis
|
|
1314
|
-
if basis is not None:
|
|
1277
|
+
if (basis := width_height([token])) is not None:
|
|
1315
1278
|
return basis
|
|
1316
|
-
|
|
1279
|
+
elif get_keyword(token) == 'content':
|
|
1317
1280
|
return 'content'
|
|
1318
1281
|
|
|
1319
1282
|
|
|
@@ -1328,77 +1291,68 @@ def flex_direction(keyword):
|
|
|
1328
1291
|
@property('flex-shrink')
|
|
1329
1292
|
@single_token
|
|
1330
1293
|
def flex_grow_shrink(token):
|
|
1331
|
-
if
|
|
1332
|
-
return
|
|
1294
|
+
if number := get_number(token):
|
|
1295
|
+
return number.value
|
|
1333
1296
|
|
|
1334
1297
|
|
|
1335
1298
|
def _inflexible_breadth(token):
|
|
1336
1299
|
"""Parse ``inflexible-breadth``."""
|
|
1337
|
-
keyword
|
|
1338
|
-
if keyword in ('auto', 'min-content', 'max-content'):
|
|
1300
|
+
if (keyword := get_keyword(token)) in ('auto', 'min-content', 'max-content'):
|
|
1339
1301
|
return keyword
|
|
1340
1302
|
elif keyword:
|
|
1341
1303
|
return
|
|
1342
|
-
length
|
|
1343
|
-
if length:
|
|
1304
|
+
elif length := get_length(token, negative=False, percentage=True):
|
|
1344
1305
|
return length
|
|
1345
1306
|
|
|
1346
1307
|
|
|
1347
1308
|
def _track_breadth(token):
|
|
1348
1309
|
"""Parse ``track-breadth``."""
|
|
1349
|
-
if token.type == 'dimension' and token.value >= 0 and token.unit == 'fr':
|
|
1350
|
-
return Dimension(token.value, token.unit)
|
|
1310
|
+
if token.type == 'dimension' and token.value >= 0 and token.unit.lower() == 'fr':
|
|
1311
|
+
return Dimension(token.value, token.unit.lower())
|
|
1351
1312
|
return _inflexible_breadth(token)
|
|
1352
1313
|
|
|
1353
1314
|
|
|
1354
1315
|
def _track_size(token):
|
|
1355
1316
|
"""Parse ``track-size``."""
|
|
1356
|
-
track_breadth
|
|
1357
|
-
if track_breadth:
|
|
1317
|
+
if track_breadth := _track_breadth(token):
|
|
1358
1318
|
return track_breadth
|
|
1359
|
-
function =
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
if
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
if
|
|
1370
|
-
|
|
1371
|
-
if length:
|
|
1372
|
-
return ('fit-content()', length)
|
|
1319
|
+
function = Function(token)
|
|
1320
|
+
arguments = function.split_comma()
|
|
1321
|
+
if function.name == 'minmax':
|
|
1322
|
+
if len(arguments) == 2:
|
|
1323
|
+
inflexible_breadth = _inflexible_breadth(arguments[0])
|
|
1324
|
+
track_breadth = _track_breadth(arguments[1])
|
|
1325
|
+
if inflexible_breadth and track_breadth:
|
|
1326
|
+
return ('minmax()', inflexible_breadth, track_breadth)
|
|
1327
|
+
elif function.name == 'fit-content':
|
|
1328
|
+
if len(arguments) == 1:
|
|
1329
|
+
if length := get_length(arguments[0], negative=False, percentage=True):
|
|
1330
|
+
return ('fit-content()', length)
|
|
1373
1331
|
|
|
1374
1332
|
|
|
1375
1333
|
def _fixed_size(token):
|
|
1376
1334
|
"""Parse ``fixed-size``."""
|
|
1377
|
-
length
|
|
1378
|
-
if length:
|
|
1335
|
+
if length := get_length(token, negative=False, percentage=True):
|
|
1379
1336
|
return length
|
|
1380
|
-
function =
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
if
|
|
1384
|
-
|
|
1385
|
-
if
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
if
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
def _line_names(arg):
|
|
1337
|
+
function = Function(token)
|
|
1338
|
+
arguments = function.split_comma()
|
|
1339
|
+
if function.name == 'minmax' and len(arguments) == 2:
|
|
1340
|
+
if length := get_length(arguments[0], negative=False, percentage=True):
|
|
1341
|
+
track_breadth = _track_breadth(arguments[1])
|
|
1342
|
+
if track_breadth:
|
|
1343
|
+
return ('minmax()', length, track_breadth)
|
|
1344
|
+
keyword = get_keyword(arguments[0])
|
|
1345
|
+
if keyword in ('min-content', 'max-content', 'auto') or length:
|
|
1346
|
+
fixed_breadth = get_length(arguments[1], negative=False, percentage=True)
|
|
1347
|
+
if fixed_breadth:
|
|
1348
|
+
return ('minmax()', length or keyword, fixed_breadth)
|
|
1349
|
+
|
|
1350
|
+
|
|
1351
|
+
def _line_names(token):
|
|
1398
1352
|
"""Parse ``line-names``."""
|
|
1399
1353
|
return_line_names = []
|
|
1400
|
-
if
|
|
1401
|
-
for token in
|
|
1354
|
+
if token.type == '[] block':
|
|
1355
|
+
for token in token.content:
|
|
1402
1356
|
if token.type == 'ident':
|
|
1403
1357
|
return_line_names.append(token.value)
|
|
1404
1358
|
elif token.type != 'whitespace':
|
|
@@ -1412,8 +1366,7 @@ def grid_auto(tokens):
|
|
|
1412
1366
|
"""``grid-auto-columns`` and ``grid-auto-rows`` properties validation."""
|
|
1413
1367
|
return_tokens = []
|
|
1414
1368
|
for token in tokens:
|
|
1415
|
-
track_size
|
|
1416
|
-
if track_size:
|
|
1369
|
+
if track_size := _track_size(token):
|
|
1417
1370
|
return_tokens.append(track_size)
|
|
1418
1371
|
continue
|
|
1419
1372
|
return
|
|
@@ -1450,25 +1403,29 @@ def grid_template(tokens):
|
|
|
1450
1403
|
if line_names is not None:
|
|
1451
1404
|
subgrid_tokens.append(line_names)
|
|
1452
1405
|
continue
|
|
1453
|
-
function =
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1406
|
+
function = Function(token)
|
|
1407
|
+
arguments = function.split_comma(single_tokens=False)
|
|
1408
|
+
if arguments is None or len(arguments) != 2:
|
|
1409
|
+
return
|
|
1410
|
+
repeat, tracks = arguments
|
|
1411
|
+
if len(repeat) != 1 or not tracks:
|
|
1412
|
+
return
|
|
1413
|
+
repeat, = repeat
|
|
1414
|
+
if function.name == 'repeat' and len(arguments) >= 2:
|
|
1415
|
+
if (repeat.type == 'number' and float(repeat.value).is_integer() and
|
|
1416
|
+
repeat.value >= 1):
|
|
1417
|
+
number = repeat.int_value
|
|
1418
|
+
elif get_keyword(repeat) == 'auto-fill':
|
|
1419
|
+
number = 'auto-fill'
|
|
1420
|
+
else:
|
|
1421
|
+
return
|
|
1422
|
+
line_names_list = []
|
|
1423
|
+
for argument in tracks:
|
|
1424
|
+
line_names = _line_names(argument)
|
|
1425
|
+
if line_names is not None:
|
|
1426
|
+
line_names_list.append(line_names)
|
|
1427
|
+
subgrid_tokens.append(('repeat()', number, tuple(line_names_list)))
|
|
1428
|
+
continue
|
|
1472
1429
|
return
|
|
1473
1430
|
return_tokens.append(tuple(subgrid_tokens))
|
|
1474
1431
|
else:
|
|
@@ -1498,57 +1455,61 @@ def grid_template(tokens):
|
|
|
1498
1455
|
return_tokens.append(track_size)
|
|
1499
1456
|
includes_track = True
|
|
1500
1457
|
continue
|
|
1501
|
-
function =
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
number =
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
names_and_sizes = []
|
|
1517
|
-
repeat_last_is_line_name = False
|
|
1518
|
-
for arg in args[1:]:
|
|
1519
|
-
line_names = _line_names(arg)
|
|
1520
|
-
if line_names is not None:
|
|
1521
|
-
if repeat_last_is_line_name:
|
|
1522
|
-
return
|
|
1523
|
-
names_and_sizes.append(line_names)
|
|
1524
|
-
repeat_last_is_line_name = True
|
|
1525
|
-
continue
|
|
1526
|
-
# fixed-repead
|
|
1527
|
-
fixed_size = _fixed_size(arg)
|
|
1528
|
-
if fixed_size:
|
|
1529
|
-
if not repeat_last_is_line_name:
|
|
1530
|
-
names_and_sizes.append(())
|
|
1531
|
-
repeat_last_is_line_name = False
|
|
1532
|
-
names_and_sizes.append(fixed_size)
|
|
1533
|
-
continue
|
|
1534
|
-
# track-repeat
|
|
1535
|
-
track_size = _track_size(arg)
|
|
1536
|
-
if track_size:
|
|
1537
|
-
includes_track = True
|
|
1538
|
-
if not repeat_last_is_line_name:
|
|
1539
|
-
names_and_sizes.append(())
|
|
1540
|
-
repeat_last_is_line_name = False
|
|
1541
|
-
names_and_sizes.append(track_size)
|
|
1542
|
-
continue
|
|
1458
|
+
function = Function(token)
|
|
1459
|
+
arguments = function.split_comma(single_tokens=False)
|
|
1460
|
+
if arguments is None or len(arguments) != 2:
|
|
1461
|
+
return
|
|
1462
|
+
repeat, tracks = arguments
|
|
1463
|
+
if len(repeat) != 1 or not tracks:
|
|
1464
|
+
return
|
|
1465
|
+
repeat, = repeat
|
|
1466
|
+
if function.name == 'repeat' and len(arguments) >= 2:
|
|
1467
|
+
if number := get_number(repeat, negative=False, integer=True):
|
|
1468
|
+
if number.value >= 1:
|
|
1469
|
+
number = number.value
|
|
1470
|
+
elif get_keyword(repeat) in ('auto-fill', 'auto-fit'):
|
|
1471
|
+
# auto-repeat
|
|
1472
|
+
if includes_auto_repeat:
|
|
1543
1473
|
return
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1474
|
+
number = repeat.value
|
|
1475
|
+
includes_auto_repeat = True
|
|
1476
|
+
else:
|
|
1477
|
+
return
|
|
1478
|
+
names_and_sizes = []
|
|
1479
|
+
repeat_last_is_line_name = False
|
|
1480
|
+
for arg in tracks:
|
|
1481
|
+
line_names = _line_names(arg)
|
|
1482
|
+
if line_names is not None:
|
|
1483
|
+
if repeat_last_is_line_name:
|
|
1484
|
+
return
|
|
1485
|
+
names_and_sizes.append(line_names)
|
|
1486
|
+
repeat_last_is_line_name = True
|
|
1487
|
+
continue
|
|
1488
|
+
# fixed-repeat
|
|
1489
|
+
fixed_size = _fixed_size(arg)
|
|
1490
|
+
if fixed_size:
|
|
1491
|
+
if not repeat_last_is_line_name:
|
|
1492
|
+
names_and_sizes.append(())
|
|
1493
|
+
repeat_last_is_line_name = False
|
|
1494
|
+
names_and_sizes.append(fixed_size)
|
|
1495
|
+
continue
|
|
1496
|
+
# track-repeat
|
|
1497
|
+
track_size = _track_size(arg)
|
|
1498
|
+
if track_size:
|
|
1499
|
+
includes_track = True
|
|
1500
|
+
if not repeat_last_is_line_name:
|
|
1501
|
+
names_and_sizes.append(())
|
|
1502
|
+
repeat_last_is_line_name = False
|
|
1503
|
+
names_and_sizes.append(track_size)
|
|
1504
|
+
continue
|
|
1505
|
+
return
|
|
1506
|
+
if not last_is_line_name:
|
|
1507
|
+
return_tokens.append(())
|
|
1508
|
+
last_is_line_name = False
|
|
1509
|
+
if not repeat_last_is_line_name:
|
|
1510
|
+
names_and_sizes.append(())
|
|
1511
|
+
return_tokens.append(('repeat()', number, tuple(names_and_sizes)))
|
|
1512
|
+
continue
|
|
1552
1513
|
return
|
|
1553
1514
|
if includes_auto_repeat and includes_track:
|
|
1554
1515
|
return
|
|
@@ -1627,8 +1588,9 @@ def grid_line(tokens):
|
|
|
1627
1588
|
return keyword
|
|
1628
1589
|
elif keyword != 'span':
|
|
1629
1590
|
return (None, None, token.value)
|
|
1630
|
-
elif
|
|
1631
|
-
|
|
1591
|
+
elif number := get_number(token, integer=True):
|
|
1592
|
+
if number.value != 0:
|
|
1593
|
+
return (None, number.value, None)
|
|
1632
1594
|
return
|
|
1633
1595
|
number = ident = span = None
|
|
1634
1596
|
for token in tokens:
|
|
@@ -1642,13 +1604,13 @@ def grid_line(tokens):
|
|
|
1642
1604
|
elif keyword and ident is None:
|
|
1643
1605
|
ident = token.value
|
|
1644
1606
|
continue
|
|
1645
|
-
elif
|
|
1646
|
-
if number is None:
|
|
1647
|
-
number =
|
|
1607
|
+
elif item := get_number(token, integer=True):
|
|
1608
|
+
if item.value != 0 and number is None:
|
|
1609
|
+
number = item.value
|
|
1648
1610
|
continue
|
|
1649
1611
|
return
|
|
1650
1612
|
if span:
|
|
1651
|
-
if number and number < 0:
|
|
1613
|
+
if isinstance(number, int) and number < 0:
|
|
1652
1614
|
return
|
|
1653
1615
|
elif ident or number:
|
|
1654
1616
|
return (span, number, ident)
|
|
@@ -1805,8 +1767,8 @@ def align_content(tokens):
|
|
|
1805
1767
|
@property()
|
|
1806
1768
|
@single_token
|
|
1807
1769
|
def order(token):
|
|
1808
|
-
if
|
|
1809
|
-
return
|
|
1770
|
+
if number := get_number(token, integer=True):
|
|
1771
|
+
return number.value
|
|
1810
1772
|
|
|
1811
1773
|
|
|
1812
1774
|
@property(unstable=True)
|
|
@@ -1886,12 +1848,11 @@ def anchor(token):
|
|
|
1886
1848
|
"""Validation for ``anchor``."""
|
|
1887
1849
|
if get_keyword(token) == 'none':
|
|
1888
1850
|
return 'none'
|
|
1889
|
-
function =
|
|
1890
|
-
if function:
|
|
1891
|
-
name,
|
|
1892
|
-
prototype = (name, [arg.type for arg in args])
|
|
1851
|
+
function = Function(token)
|
|
1852
|
+
if arguments := function.split_space():
|
|
1853
|
+
prototype = (function.name, [argument.type for argument in arguments])
|
|
1893
1854
|
if prototype == ('attr', ['ident']):
|
|
1894
|
-
return ('attr()',
|
|
1855
|
+
return ('attr()', arguments[0].value)
|
|
1895
1856
|
|
|
1896
1857
|
|
|
1897
1858
|
@property(proprietary=True, wants_base_url=True)
|
|
@@ -1903,12 +1864,11 @@ def link(token, base_url):
|
|
|
1903
1864
|
parsed_url = get_url(token, base_url)
|
|
1904
1865
|
if parsed_url:
|
|
1905
1866
|
return parsed_url
|
|
1906
|
-
function =
|
|
1907
|
-
if function:
|
|
1908
|
-
name,
|
|
1909
|
-
prototype = (name, [arg.type for arg in args])
|
|
1867
|
+
function = Function(token)
|
|
1868
|
+
if arguments := function.split_space():
|
|
1869
|
+
prototype = (function.name, [argument.type for argument in arguments])
|
|
1910
1870
|
if prototype == ('attr', ['ident']):
|
|
1911
|
-
return ('attr()',
|
|
1871
|
+
return ('attr()', arguments[0].value)
|
|
1912
1872
|
|
|
1913
1873
|
|
|
1914
1874
|
@property()
|
|
@@ -1920,8 +1880,7 @@ def tab_size(token):
|
|
|
1920
1880
|
|
|
1921
1881
|
"""
|
|
1922
1882
|
if token.type == 'number' and token.int_value is not None:
|
|
1923
|
-
value
|
|
1924
|
-
if value >= 0:
|
|
1883
|
+
if value := token.int_value:
|
|
1925
1884
|
return value
|
|
1926
1885
|
return get_length(token, negative=False)
|
|
1927
1886
|
|
|
@@ -1930,8 +1889,7 @@ def tab_size(token):
|
|
|
1930
1889
|
@single_token
|
|
1931
1890
|
def hyphens(token):
|
|
1932
1891
|
"""Validation for ``hyphens``."""
|
|
1933
|
-
keyword
|
|
1934
|
-
if keyword in ('none', 'manual', 'auto'):
|
|
1892
|
+
if (keyword := get_keyword(token)) in ('none', 'manual', 'auto'):
|
|
1935
1893
|
return keyword
|
|
1936
1894
|
|
|
1937
1895
|
|
|
@@ -1939,8 +1897,7 @@ def hyphens(token):
|
|
|
1939
1897
|
@single_token
|
|
1940
1898
|
def hyphenate_character(token):
|
|
1941
1899
|
"""Validation for ``hyphenate-character``."""
|
|
1942
|
-
|
|
1943
|
-
if keyword == 'auto':
|
|
1900
|
+
if get_keyword(token) == 'auto':
|
|
1944
1901
|
return '‐'
|
|
1945
1902
|
elif token.type == 'string':
|
|
1946
1903
|
return token.value
|
|
@@ -1961,36 +1918,33 @@ def hyphenate_limit_chars(tokens):
|
|
|
1961
1918
|
keyword = get_keyword(token)
|
|
1962
1919
|
if keyword == 'auto':
|
|
1963
1920
|
return (5, 2, 2)
|
|
1964
|
-
elif
|
|
1965
|
-
return (
|
|
1921
|
+
elif number := get_number(token, integer=True):
|
|
1922
|
+
return (number.value, 2, 2)
|
|
1966
1923
|
elif len(tokens) == 2:
|
|
1967
1924
|
total, left = tokens
|
|
1968
1925
|
total_keyword = get_keyword(total)
|
|
1969
1926
|
left_keyword = get_keyword(left)
|
|
1970
|
-
if
|
|
1971
|
-
if
|
|
1972
|
-
return (
|
|
1927
|
+
if total_number := get_number(total, integer=True):
|
|
1928
|
+
if left_number := get_number(left, integer=True):
|
|
1929
|
+
return (total_number.value, left_number.value, left_number.value)
|
|
1973
1930
|
elif left_keyword == 'auto':
|
|
1974
|
-
return (
|
|
1931
|
+
return (total_number.value, 2, 2)
|
|
1975
1932
|
elif total_keyword == 'auto':
|
|
1976
|
-
if
|
|
1977
|
-
return (5,
|
|
1933
|
+
if left_number := get_number(left, integer=True):
|
|
1934
|
+
return (5, left_number.value, left_number.value)
|
|
1978
1935
|
elif left_keyword == 'auto':
|
|
1979
1936
|
return (5, 2, 2)
|
|
1980
1937
|
elif len(tokens) == 3:
|
|
1981
1938
|
total, left, right = tokens
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
left = left.int_value if left.type == 'number' else 2
|
|
1992
|
-
right = right.int_value if right.type == 'number' else 2
|
|
1993
|
-
return (total, left, right)
|
|
1939
|
+
result = []
|
|
1940
|
+
for token in total, left, right:
|
|
1941
|
+
if get_keyword(token) == 'auto':
|
|
1942
|
+
result.append(5 if token is total else 2)
|
|
1943
|
+
elif number := get_number(token, integer=True):
|
|
1944
|
+
result.append(number.value)
|
|
1945
|
+
else:
|
|
1946
|
+
return
|
|
1947
|
+
return tuple(result)
|
|
1994
1948
|
|
|
1995
1949
|
|
|
1996
1950
|
@property(proprietary=True)
|
|
@@ -1999,12 +1953,11 @@ def lang(token):
|
|
|
1999
1953
|
"""Validation for ``lang``."""
|
|
2000
1954
|
if get_keyword(token) == 'none':
|
|
2001
1955
|
return 'none'
|
|
2002
|
-
function =
|
|
2003
|
-
if function:
|
|
2004
|
-
name,
|
|
2005
|
-
prototype = (name, [arg.type for arg in args])
|
|
1956
|
+
function = Function(token)
|
|
1957
|
+
if arguments := function.split_space():
|
|
1958
|
+
prototype = (function.name, [argument.type for argument in arguments])
|
|
2006
1959
|
if prototype == ('attr', ['ident']):
|
|
2007
|
-
return ('attr()',
|
|
1960
|
+
return ('attr()', arguments[0].value)
|
|
2008
1961
|
elif token.type == 'string':
|
|
2009
1962
|
return ('string', token.value)
|
|
2010
1963
|
|
|
@@ -2012,8 +1965,7 @@ def lang(token):
|
|
|
2012
1965
|
@property(unstable=True, wants_base_url=True)
|
|
2013
1966
|
def bookmark_label(tokens, base_url):
|
|
2014
1967
|
"""Validation for ``bookmark-label``."""
|
|
2015
|
-
parsed_tokens = tuple(
|
|
2016
|
-
get_content_list_token(token, base_url) for token in tokens)
|
|
1968
|
+
parsed_tokens = tuple(get_content_list_token(token, base_url) for token in tokens)
|
|
2017
1969
|
if None not in parsed_tokens:
|
|
2018
1970
|
return parsed_tokens
|
|
2019
1971
|
|
|
@@ -2022,10 +1974,9 @@ def bookmark_label(tokens, base_url):
|
|
|
2022
1974
|
@single_token
|
|
2023
1975
|
def bookmark_level(token):
|
|
2024
1976
|
"""Validation for ``bookmark-level``."""
|
|
2025
|
-
if
|
|
2026
|
-
value
|
|
2027
|
-
|
|
2028
|
-
return value
|
|
1977
|
+
if number := get_number(token, negative=False, integer=True):
|
|
1978
|
+
if number.value >= 1:
|
|
1979
|
+
return number.value
|
|
2029
1980
|
elif get_keyword(token) == 'none':
|
|
2030
1981
|
return 'none'
|
|
2031
1982
|
|
|
@@ -2076,53 +2027,55 @@ def transform(tokens):
|
|
|
2076
2027
|
else:
|
|
2077
2028
|
transforms = []
|
|
2078
2029
|
for token in tokens:
|
|
2079
|
-
function =
|
|
2080
|
-
|
|
2030
|
+
function = Function(token)
|
|
2031
|
+
arguments = function.split_comma()
|
|
2032
|
+
if arguments is None:
|
|
2081
2033
|
return
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
if len(
|
|
2085
|
-
angle = get_angle(
|
|
2086
|
-
length = get_length(
|
|
2087
|
-
if name == 'rotate' and angle is not None:
|
|
2088
|
-
transforms.append((name, angle))
|
|
2089
|
-
elif name in ('skewx', 'skew') and angle is not None:
|
|
2034
|
+
|
|
2035
|
+
all_numbers = {argument.type for argument in arguments} == {'number'}
|
|
2036
|
+
if len(arguments) == 1:
|
|
2037
|
+
angle = get_angle(arguments[0])
|
|
2038
|
+
length = get_length(arguments[0], percentage=True)
|
|
2039
|
+
if function.name == 'rotate' and angle is not None:
|
|
2040
|
+
transforms.append((function.name, angle))
|
|
2041
|
+
elif function.name in ('skewx', 'skew') and angle is not None:
|
|
2090
2042
|
transforms.append(('skew', (angle, 0)))
|
|
2091
|
-
elif name == 'skewy' and angle is not None:
|
|
2043
|
+
elif function.name == 'skewy' and angle is not None:
|
|
2092
2044
|
transforms.append(('skew', (0, angle)))
|
|
2093
|
-
elif name in ('translatex', 'translate') and length:
|
|
2045
|
+
elif function.name in ('translatex', 'translate') and length:
|
|
2094
2046
|
transforms.append(('translate', (length, ZERO_PIXELS)))
|
|
2095
|
-
elif name == 'translatey' and length:
|
|
2047
|
+
elif function.name == 'translatey' and length:
|
|
2096
2048
|
transforms.append(('translate', (ZERO_PIXELS, length)))
|
|
2097
|
-
elif name == 'scalex' and
|
|
2098
|
-
transforms.append(('scale', (
|
|
2099
|
-
elif name == 'scaley' and
|
|
2100
|
-
transforms.append(('scale', (1,
|
|
2101
|
-
elif name == 'scale' and
|
|
2102
|
-
transforms.append(('scale', (
|
|
2049
|
+
elif function.name == 'scalex' and all_numbers:
|
|
2050
|
+
transforms.append(('scale', (arguments[0].value, 1)))
|
|
2051
|
+
elif function.name == 'scaley' and all_numbers:
|
|
2052
|
+
transforms.append(('scale', (1, arguments[0].value)))
|
|
2053
|
+
elif function.name == 'scale' and all_numbers:
|
|
2054
|
+
transforms.append(('scale', (arguments[0].value,) * 2))
|
|
2103
2055
|
else:
|
|
2104
2056
|
return
|
|
2105
|
-
elif len(
|
|
2106
|
-
if name == 'scale' and
|
|
2107
|
-
|
|
2108
|
-
|
|
2057
|
+
elif len(arguments) == 2:
|
|
2058
|
+
if function.name == 'scale' and all_numbers:
|
|
2059
|
+
values = tuple(argument.value for argument in arguments)
|
|
2060
|
+
transforms.append((function.name, values))
|
|
2061
|
+
elif function.name == 'translate':
|
|
2109
2062
|
lengths = tuple(
|
|
2110
|
-
get_length(token, percentage=True) for token in
|
|
2063
|
+
get_length(token, percentage=True) for token in arguments)
|
|
2111
2064
|
if all(lengths):
|
|
2112
|
-
transforms.append((name, lengths))
|
|
2065
|
+
transforms.append((function.name, lengths))
|
|
2113
2066
|
else:
|
|
2114
2067
|
return
|
|
2115
|
-
elif name == 'skew':
|
|
2116
|
-
angles = tuple(get_angle(token) for token in
|
|
2068
|
+
elif function.name == 'skew':
|
|
2069
|
+
angles = tuple(get_angle(token) for token in arguments)
|
|
2117
2070
|
if all(angle is not None for angle in angles):
|
|
2118
|
-
transforms.append((name, angles))
|
|
2071
|
+
transforms.append((function.name, angles))
|
|
2119
2072
|
else:
|
|
2120
2073
|
return
|
|
2121
2074
|
else:
|
|
2122
2075
|
return
|
|
2123
|
-
elif len(
|
|
2124
|
-
|
|
2125
|
-
|
|
2076
|
+
elif len(arguments) == 6 and function.name == 'matrix' and all_numbers:
|
|
2077
|
+
transforms.append(
|
|
2078
|
+
(function.name, tuple(argument.value for argument in arguments)))
|
|
2126
2079
|
else:
|
|
2127
2080
|
return
|
|
2128
2081
|
return tuple(transforms)
|
|
@@ -2132,6 +2085,37 @@ def transform(tokens):
|
|
|
2132
2085
|
@single_token
|
|
2133
2086
|
def appearance(token):
|
|
2134
2087
|
"""``appearance`` property validation."""
|
|
2135
|
-
keyword
|
|
2136
|
-
if keyword in ('none', 'auto'):
|
|
2088
|
+
if (keyword := get_keyword(token)) in ('none', 'auto'):
|
|
2137
2089
|
return keyword
|
|
2090
|
+
|
|
2091
|
+
|
|
2092
|
+
@property()
|
|
2093
|
+
def color_scheme(tokens):
|
|
2094
|
+
"""``color-scheme`` property validation."""
|
|
2095
|
+
if len(tokens) == 1:
|
|
2096
|
+
keyword = get_single_keyword(tokens)
|
|
2097
|
+
if keyword == 'normal':
|
|
2098
|
+
return keyword
|
|
2099
|
+
elif keyword != 'only':
|
|
2100
|
+
return (keyword,)
|
|
2101
|
+
else:
|
|
2102
|
+
return
|
|
2103
|
+
else:
|
|
2104
|
+
keywords = []
|
|
2105
|
+
only = False
|
|
2106
|
+
for i, token in enumerate(tokens):
|
|
2107
|
+
keyword = get_keyword(token)
|
|
2108
|
+
if keyword == 'only':
|
|
2109
|
+
if only or i not in (0, len(tokens) - 1):
|
|
2110
|
+
return
|
|
2111
|
+
else:
|
|
2112
|
+
only = True
|
|
2113
|
+
elif keyword == 'normal':
|
|
2114
|
+
return
|
|
2115
|
+
elif keyword:
|
|
2116
|
+
keywords.append(keyword)
|
|
2117
|
+
else:
|
|
2118
|
+
return
|
|
2119
|
+
if only:
|
|
2120
|
+
keywords.append('only')
|
|
2121
|
+
return tuple(keywords)
|