Repository: Kozea/WeasyPrint (python)

Issue:
Support `unicode-range`

Pull request description:
Handle @font-face unicode-range
Fix #378.

Patch:
diff --git a/weasyprint/css/validation/descriptors.py b/weasyprint/css/validation/descriptors.py
index 8f2cadca..06db3be4 100644
--- a/weasyprint/css/validation/descriptors.py
+++ b/weasyprint/css/validation/descriptors.py
@@ -208,6 +208,15 @@ def font_variant(tokens):
     return values
 
 
+@descriptor('font-face')
+@comma_separated_list
+@single_token
+def unicode_range(token):
+    """``unicode_range`` descriptor validation."""
+    if token.type == 'unicode-range':
+        return token
+
+
 @descriptor('counter-style')
 def system(tokens):
     """``system`` descriptor validation."""
diff --git a/weasyprint/text/fonts.py b/weasyprint/text/fonts.py
index efa16435..61c46ef0 100644
--- a/weasyprint/text/fonts.py
+++ b/weasyprint/text/fonts.py
@@ -208,14 +208,23 @@ class FontConfiguration:
             match = SubElement(root, 'match', target='font')
             test = SubElement(match, 'test', name='file', compare='eq')
             SubElement(test, 'string').text = str(font_path)
-            edit = SubElement(match, 'edit', name='fontfeatures', mode=mode)
             descriptors = {
                 rules[0][0].replace('-', '_'): rules[0][1] for rules in
                 rule_descriptors.get('font_variant', [])}
             settings = rule_descriptors.get('font_feature_settings', 'normal')
             features = font_features(font_feature_settings=settings, **descriptors)
-            for key, value in features.items():
-                SubElement(edit, 'string').text = f'{key} {value}'
+            if features:
+                edit = SubElement(match, 'edit', name='fontfeatures', mode=mode)
+                for key, value in features.items():
+                    SubElement(edit, 'string').text = f'{key} {value}'
+            if unicode_ranges := rule_descriptors.get('unicode_range'):
+                edit = SubElement(match, 'edit', name='charset', mode=mode)
+                plus = SubElement(edit, 'plus')
+                for unicode_range in unicode_ranges:
+                    charset = SubElement(plus, 'charset')
+                    range_ = SubElement(charset, 'range')
+                    for value in (unicode_range.start, unicode_range.end):
+                        SubElement(range_, 'int').text = f'0x{value:x}'
             header = (
                 b'<?xml version="1.0"?>',
                 b'<!DOCTYPE fontconfig SYSTEM "urn:fontconfig:fonts.dtd">')


Test patch:
diff --git a/tests/draw/test_text.py b/tests/draw/test_text.py
index c1be0036..9fab6373 100644
--- a/tests/draw/test_text.py
+++ b/tests/draw/test_text.py
@@ -1230,3 +1230,29 @@ def test_font_variant_caps_titling(assert_pixels):
       </style>
       <p>Pp</p>
     ''' % SANS_FONTS)
+
+
+def test_unicode_range(assert_pixels):
+    assert_pixels('''
+        __________
+        _RRRRRR___
+        _RRRRRRzz_
+        __________
+    ''', '''
+      <style>
+        @font-face {
+          font-family: uni;
+          src: url(weasyprint.otf);
+          unicode-range: u+41, u+043-045, u+005?;
+        }
+        @page {
+          size: 10px 4px;
+        }
+        body {
+          color: red;
+          font-family: uni;
+          font-size: 2px;
+          line-height: 0;
+          margin: 2px 1px;
+        }
+      </style>ADZB''')