Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package python-weasyprint for 
openSUSE:Factory checked in at 2026-03-14 22:22:28
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-weasyprint (Old)
 and      /work/SRC/openSUSE:Factory/.python-weasyprint.new.8177 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-weasyprint"

Sat Mar 14 22:22:28 2026 rev:22 rq:1338811 version:68.1

Changes:
--------
--- /work/SRC/openSUSE:Factory/python-weasyprint/python-weasyprint.changes      
2026-01-21 14:15:19.233782124 +0100
+++ 
/work/SRC/openSUSE:Factory/.python-weasyprint.new.8177/python-weasyprint.changes
    2026-03-14 22:23:38.116212798 +0100
@@ -1,0 +2,15 @@
+Fri Mar 13 20:41:23 UTC 2026 - Dirk Müller <[email protected]>
+
+- update to 68.1:
+  * #2662: Don’t crash when SVG clip paths are not in defs tags
+  * #2665: Fix position of box bounding box
+  * #2663: Fix transparency with Acrobat and Edge
+  * #2666: Don’t rely on random default font to define test page
+    size
+  * #2670: Fix pattern detection of URL schemes
+  * #2671: Improve API compatibility between URLFetcherResponse
+    and addinfourl
+  * #2672: Fix charset for old URL fetcher requests
+  * #2675, #2673: Fix calc for many properties
+
+-------------------------------------------------------------------

Old:
----
  weasyprint-68.0.tar.gz

New:
----
  weasyprint-68.1.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python-weasyprint.spec ++++++
--- /var/tmp/diff_new_pack.AiOAUL/_old  2026-03-14 22:23:39.124254557 +0100
+++ /var/tmp/diff_new_pack.AiOAUL/_new  2026-03-14 22:23:39.124254557 +0100
@@ -35,7 +35,7 @@
 
 %{?sle15_python_module_pythons}
 Name:           python-weasyprint
-Version:        68.0
+Version:        68.1
 Release:        0
 Summary:        Python module to convert web documents to PDF
 License:        BSD-3-Clause

++++++ weasyprint-68.0.tar.gz -> weasyprint-68.1.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/weasyprint-68.0/PKG-INFO new/weasyprint-68.1/PKG-INFO
--- old/weasyprint-68.0/PKG-INFO        1970-01-01 01:00:00.000000000 +0100
+++ new/weasyprint-68.1/PKG-INFO        1970-01-01 01:00:00.000000000 +0100
@@ -1,6 +1,6 @@
 Metadata-Version: 2.4
 Name: weasyprint
-Version: 68.0
+Version: 68.1
 Summary: The Awesome Document Factory
 Keywords: html,css,pdf,converter
 Author-email: Simon Sapin <[email protected]>
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/weasyprint-68.0/docs/changelog.rst 
new/weasyprint-68.1/docs/changelog.rst
--- old/weasyprint-68.0/docs/changelog.rst      2026-01-19 15:54:27.767018000 
+0100
+++ new/weasyprint-68.1/docs/changelog.rst      2026-02-06 16:03:54.102264200 
+0100
@@ -2,6 +2,60 @@
 =========
 
 
+Version 68.1
+------------
+
+Released on 2026-02-06.
+
+Bug fixes:
+
+* `#2662 <https://github.com/Kozea/WeasyPrint/issues/2662>`_:
+  Don’t crash when SVG clip paths are not in defs tags
+* `#2665 <https://github.com/Kozea/WeasyPrint/issues/2665>`_:
+  Fix position of box bounding box
+* `#2663 <https://github.com/Kozea/WeasyPrint/issues/2663>`_:
+  Fix transparency with Acrobat and Edge
+* `#2666 <https://github.com/Kozea/WeasyPrint/issues/2666>`_:
+  Don’t rely on random default font to define test page size
+* `#2670 <https://github.com/Kozea/WeasyPrint/issues/2670>`_:
+  Fix pattern detection of URL schemes
+* `#2671 <https://github.com/Kozea/WeasyPrint/pull/2671>`_:
+  Improve API compatibility between URLFetcherResponse and addinfourl
+* `#2672 <https://github.com/Kozea/WeasyPrint/issues/2672>`_:
+  Fix charset for old URL fetcher requests
+* `#2675 <https://github.com/Kozea/WeasyPrint/pull/2675>`_,
+  `#2673 <https://github.com/Kozea/WeasyPrint/issues/2673>`_:
+  Fix calc for many properties
+
+Contributors:
+
+* Guillaume Ayoub
+
+Backers and sponsors:
+
+* Spacinov
+* Syslifters
+* Kobalt
+* Simon Sapin
+* Grip Angebotssoftware
+* Manuel Barkhau
+* Simonsoft
+* KontextWork
+* Menutech
+* TrainingSparkle
+* Healthchecks.io
+* Method B
+* FieldHub
+* Hammerbacher
+* Yanal-Yves Fargialla
+* Morntag
+* Piloterr
+* Xavid
+* Charlie S.
+* Prothesis Dental Solutions
+* Kai DeLorenzo
+
+
 Version 68.0
 ------------
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/weasyprint-68.0/tests/css/test_math.py 
new/weasyprint-68.1/tests/css/test_math.py
--- old/weasyprint-68.0/tests/css/test_math.py  2026-01-19 15:54:27.768018000 
+0100
+++ new/weasyprint-68.1/tests/css/test_math.py  2026-02-06 16:03:54.104264300 
+0100
@@ -94,6 +94,7 @@
     'calc(100px, 100px)',
     'calc(100px * 100px)',
     'calc(100 * 100)',
+    'calc(calc(100vw))',
     'calc(0.1)',
     'calc(-1)',
     'min()',
@@ -106,6 +107,7 @@
     'max("10px")',
     'max(10, 50px)',
     'calc(max(100, 5px) * 10px)',
+    'calc(100* - max(56px, 1rem)',
     'clamp()',
     'clamp(10px)',
     'clamp(10px, 50px)',
@@ -236,6 +238,37 @@
     assert len(math_logs) == len(logs)
 
 
[email protected]('display', [
+    'block', 'inline', 'flex', 'grid',
+    'list', 'list-item',
+    'table', 'table-row-group', 'table-cell',
+    'inline-block', 'inline-table', 'inline-flex', 'inline-grid',
+])
+def test_math_functions_display_size(display):
+    # Regression test for #2673.
+    render_pages(f'''
+    <div style="display: {display};
+     min-width: calc(50% + 1em); max-width: calc(50% + 1em); width: calc(50% + 
1em);
+     min-height: calc(50% + 1em); max-height: calc(50% + 1em); height: 
calc(50% + 1em)
+    ">
+      <div style="
+       min-width: calc(50% + 1em); max-width: calc(50% + 1em); width: calc(50% 
+ 1em);
+       min-height: calc(50% + 1em); max-height: calc(50% + 1em); height: 
calc(50% + 1em)
+      "></div>
+    </div>
+    ''')
+
+
+@assert_no_logs
+def test_math_functions_hyphenate():
+    render_pages('''
+      <div lang="en"
+        style="hyphens: auto; hyphenate-limit-zone: calc(1em + 100%); width: 
2em">
+        absolute
+      </div>
+    ''')
+
+
 @assert_no_logs
 def test_math_functions_gradient():
     render_pages('''
@@ -262,3 +295,81 @@
         rgba(10, 20, calc(30), calc(80%)) 10%,
         hsl(calc(10 + 10), 20%, 20%) 80%"></div>
     ''')
+
+
+@assert_no_logs
+def test_math_image_min_content_calc():
+    render_pages('''
+      <table>
+        <td>
+          <img src="pattern.png" style="
+            height: calc(10% + 1em);
+            width: calc(10% + 1em);
+            max-height: calc(10% + 1em);
+            max-width: calc(10% + 1em);
+            min-height: calc(10% + 1em);
+            min-width: calc(10% + 1em);
+          ">
+    ''')
+
+
+@assert_no_logs
+def test_math_image_min_content_auto_width_calc():
+    render_pages('''
+      <table>
+        <td>
+          <img src="pattern.png" style="
+            height: calc(10% + 1em);
+            max-height: calc(10% + 1em);
+            max-width: calc(10% + 1em);
+            min-height: calc(10% + 1em);
+            min-width: calc(10% + 1em);
+          ">
+    ''')
+
+
+@assert_no_logs
+def test_math_image_min_content_auto_width_height_calc():
+    render_pages('''
+      <table>
+        <td>
+          <img src="pattern.png" style="
+            max-height: calc(10% + 1em);
+            max-width: calc(10% + 1em);
+            min-height: calc(10% + 1em);
+            min-width: calc(10% + 1em);
+          ">
+    ''')
+
+
+@assert_no_logs
+def test_math_table_margin():
+    render_pages('<table style="margin: calc(1em + 10%)">')
+
+
+@assert_no_logs
+def test_math_grid_padding():
+    render_pages('''
+      <article style="display: grid">
+        <div style="box-sizing: border-box; border: 1px solid;
+                    padding: calc(2px + 10%); width: 7px">a</div>
+      </article>
+    ''')
+
+
+@assert_no_logs
+def test_math_table_column():
+    render_pages('''
+      <table style="width: 200px">
+        <colgroup style="width: calc(1em + 10%)">
+          <col />
+        </colgroup>
+        <col style="width: calc(1em + 10%)" />
+        <tbody>
+          <tr>
+            <td>a</td>
+            <td>a</td>
+          </tr>
+        </tbody>
+      </table>
+    ''')
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/weasyprint-68.0/tests/draw/svg/test_clip.py 
new/weasyprint-68.1/tests/draw/svg/test_clip.py
--- old/weasyprint-68.0/tests/draw/svg/test_clip.py     2026-01-19 
15:54:27.768018000 +0100
+++ new/weasyprint-68.1/tests/draw/svg/test_clip.py     2026-02-06 
16:03:54.104264300 +0100
@@ -96,3 +96,31 @@
         </g>
       </svg>
     ''')
+
+
+@assert_no_logs
+def test_clip_path_outside_defs(assert_pixels):
+    # Regression test for #2662.
+    assert_pixels('''
+        _________
+        _________
+        __RRRRR__
+        __RBBBR__
+        __RBBBR__
+        __RBBBR__
+        __RRRRR__
+        _________
+        _________
+    ''', '''
+      <style>
+        @page { size: 9px }
+        svg { display: block }
+      </style>
+      <svg width="9px" height="9px" xmlns="http://www.w3.org/2000/svg";>
+        <clipPath id="clip">
+          <rect x="2" y="2" width="5" height="5" />
+        </clipPath>
+        <rect x="2" y="2" width="5" height="5" stroke-width="2"
+              stroke="red" fill="blue" clip-path="url(#clip)" />
+      </svg>
+    ''')
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/weasyprint-68.0/tests/draw/svg/test_text.py 
new/weasyprint-68.1/tests/draw/svg/test_text.py
--- old/weasyprint-68.0/tests/draw/svg/test_text.py     2026-01-19 
15:54:27.769018000 +0100
+++ new/weasyprint-68.1/tests/draw/svg/test_text.py     2026-02-06 
16:03:54.105264200 +0100
@@ -30,7 +30,7 @@
         _BBBBBBBBBBBB_______
     ''', '''
       <style>
-        @page { font-size: 1px; size: 20em 8ex }
+        @page { font-size: 1px; size: 20em 4em }
         svg { display: block }
       </style>
       <svg width="20px" height="4px" xmlns="http://www.w3.org/2000/svg";>
@@ -687,3 +687,26 @@
       </svg>
     ''',
     )
+
+
+@assert_no_logs
+def test_text_fill_opacity(assert_pixels):
+    # Regression text for #2665.
+    assert_pixels('''
+        ______
+        _ssss_
+        _ssss_
+        _ssss_
+        _ssss_
+        ______
+    ''', '''
+      <style>
+        @page { size: 6px 6px }
+        svg { display: block }
+      </style>
+      <svg width="6px" height="6px" xmlns="http://www.w3.org/2000/svg";>
+        <text x="1" y="4" font="4px weasyprint" fill="red" opacity="0.5">
+          A
+        </text>
+      </svg>
+    ''')
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/weasyprint-68.0/tests/draw/test_text.py 
new/weasyprint-68.1/tests/draw/test_text.py
--- old/weasyprint-68.0/tests/draw/test_text.py 2026-01-19 15:54:27.769018000 
+0100
+++ new/weasyprint-68.1/tests/draw/test_text.py 2026-02-06 16:03:54.106264000 
+0100
@@ -667,6 +667,34 @@
       <div>abc</div>''')
 
 
+def test_text_underline_offset_calc(assert_pixels):
+    assert_pixels('''
+        _____________
+        _zzzzzzzzzzz_
+        _zRRRRRRRRRz_
+        _zRRRRRRRRRz_
+        _zzzzzzzzzzz_
+        _zzzzzzzzzzz_
+        _zBBBBBBBBBz_
+        _zzzzzzzzzzz_
+        _____________
+    ''', '''
+      <style>
+        @page {
+          size: 13px 9px;
+          margin: 2px;
+        }
+        body {
+          color: red;
+          font-family: weasyprint;
+          font-size: 3px;
+          text-decoration: underline blue;
+          text-underline-offset: calc(0.5em + 20%);
+        }
+      </style>
+      <div>abc</div>''')
+
+
 def test_text_underline_thickness(assert_pixels):
     assert_pixels('''
         _____________
@@ -720,6 +748,34 @@
           text-underline-offset: 2px;
         }
       </style>
+      <div>abc</div>''')
+
+
+def test_text_underline_thickness_calc(assert_pixels):
+    assert_pixels('''
+        _____________
+        _zzzzzzzzzzz_
+        _zRRRRRRRRRz_
+        _zRRRRRRRRRz_
+        _zzzzzzzzzzz_
+        _zzzzzzzzzzz_
+        _zBBBBBBBBBz_
+        _zBBBBBBBBBz_
+        _zzzzzzzzzzz_
+    ''', '''
+      <style>
+        @page {
+          size: 13px 9px;
+          margin: 2px;
+        }
+        body {
+          color: red;
+          font-family: weasyprint;
+          font-size: 3px;
+          text-decoration: underline blue calc(0.5em + 50%);
+          text-underline-offset: 2px;
+        }
+      </style>
       <div>abc</div>''')
 
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/weasyprint-68.0/tests/test_api.py 
new/weasyprint-68.1/tests/test_api.py
--- old/weasyprint-68.0/tests/test_api.py       2026-01-19 15:54:27.780017900 
+0100
+++ new/weasyprint-68.1/tests/test_api.py       2026-02-06 16:03:54.117264300 
+0100
@@ -10,6 +10,7 @@
 import unicodedata
 import wsgiref.simple_server
 import zlib
+from base64 import b64encode
 from functools import partial
 from pathlib import Path
 from urllib.parse import urljoin, uses_relative
@@ -704,6 +705,17 @@
     _run(command, f'<img 
src="{path2url(resource_path("pattern.png"))}">'.encode())
 
 
+@assert_no_logs
[email protected]('command', [
+    '- -',
+    '--allowed-protocols data - -',
+    '--allowed-protocols File,Data - -',
+])
+def test_allowed_protocols_data(command):
+    data = b64encode(resource_path('pattern.png').read_bytes()).decode()
+    _run(command, f'<img src="data:image/png;base64,{data}">'.encode())
+
+
 @pytest.mark.parametrize('command', [
     '--allowed-protocols http - -',
     '--allowed-protocols http,https - -',
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/weasyprint-68.0/weasyprint/__init__.py 
new/weasyprint-68.1/weasyprint/__init__.py
--- old/weasyprint-68.0/weasyprint/__init__.py  2026-01-19 15:54:27.780017900 
+0100
+++ new/weasyprint-68.1/weasyprint/__init__.py  2026-02-06 16:03:54.118264200 
+0100
@@ -14,7 +14,7 @@
 import tinycss2
 import tinyhtml5
 
-VERSION = __version__ = '68.0'
+VERSION = __version__ = '68.1'
 
 #: Default values for command-line and Python API rendering options. See
 #: :func:`__main__.main` to learn more about specific options for
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/weasyprint-68.0/weasyprint/css/__init__.py 
new/weasyprint-68.1/weasyprint/css/__init__.py
--- old/weasyprint-68.0/weasyprint/css/__init__.py      2026-01-19 
15:54:27.781017800 +0100
+++ new/weasyprint-68.1/weasyprint/css/__init__.py      2026-02-06 
16:03:54.118264200 +0100
@@ -779,7 +779,6 @@
         return
 
     args = []
-    original_token = token
     function = Function(token)
     if function.name is None:
         return
@@ -795,7 +794,7 @@
     if function.name == 'calc':
         result = _resolve_calc_sum(computed, args[0], property_name, refer_to)
         if result is None:
-            return original_token
+            return
         else:
             return tokenize(result)
 
@@ -1194,10 +1193,10 @@
                 try:
                     token = resolve_math(function, self, key)
                 except PercentageInMath:
-                    token = None
-                if token is None:
                     solved_tokens.append(function)
                 else:
+                    if token is None:
+                        raise Exception
                     solved_tokens.append(token)
                 original_key = key.replace('_', '-')
                 value = validate_non_shorthand(solved_tokens, 
original_key)[0][1]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/weasyprint-68.0/weasyprint/draw/text.py 
new/weasyprint-68.1/weasyprint/draw/text.py
--- old/weasyprint-68.0/weasyprint/draw/text.py 2026-01-19 15:54:27.782018000 
+0100
+++ new/weasyprint-68.1/weasyprint/draw/text.py 2026-02-06 16:03:54.120264300 
+0100
@@ -17,6 +17,8 @@
 
 def draw_text(stream, textbox, offset_x, text_overflow, block_ellipsis):
     """Draw a textbox to a pydyf stream."""
+    from ..layout.percent import percentage
+
     # Pango crashes with font-size: 0.
     assert textbox.style['font_size']
 
@@ -30,11 +32,10 @@
     if 'underline' in text_decoration_values or 'overline' in 
text_decoration_values:
         if textbox.style['text_decoration_thickness'] in ('auto', 'from-font'):
             thickness = textbox.pango_layout.underline_thickness
-        elif textbox.style['text_decoration_thickness'].unit == '%':
-            ratio = textbox.style['text_decoration_thickness'].value / 100
-            thickness = textbox.style['font_size'] * ratio
         else:
-            thickness = textbox.style['text_decoration_thickness'].value
+            thickness = percentage(
+                textbox.style['text_decoration_thickness'], textbox.style,
+                textbox.style['font_size'])
     if 'overline' in text_decoration_values:
         offset_y = (
             textbox.baseline - textbox.pango_layout.ascent + thickness / 2)
@@ -44,11 +45,10 @@
     if 'underline' in text_decoration_values:
         if textbox.style['text_underline_offset'] == 'auto':
             underline_offset = - textbox.pango_layout.underline_position
-        elif textbox.style['text_underline_offset'].unit == '%':
-            ratio = textbox.style['text_underline_offset'].value / 100
-            underline_offset = textbox.style['font_size'] * ratio
         else:
-            underline_offset = textbox.style['text_underline_offset'].value
+            underline_offset = percentage(
+                textbox.style['text_underline_offset'], textbox.style,
+                textbox.style['font_size'])
         offset_y = textbox.baseline + underline_offset + thickness / 2
         draw_text_decoration(
             stream, textbox, offset_x, offset_y, thickness,
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/weasyprint-68.0/weasyprint/layout/block.py 
new/weasyprint-68.1/weasyprint/layout/block.py
--- old/weasyprint-68.0/weasyprint/layout/block.py      2026-01-19 
15:54:27.783017900 +0100
+++ new/weasyprint-68.1/weasyprint/layout/block.py      2026-02-06 
16:03:54.121264200 +0100
@@ -301,7 +301,7 @@
     return stop, resume_at, new_child, out_of_flow_resume_at
 
 
-def _break_line(context, box, line, new_children, next_lines, page_is_empty, 
index,
+def _break_line(context, box, line, new_children, needed, page_is_empty, index,
                 skip_stack, resume_at, absolute_boxes, fixed_boxes):
     """Break line where allowed by orphans and widows.
 
@@ -316,7 +316,6 @@
         return True, False, resume_at
     # How many lines we need on the next page to satisfy widows
     # -1 for the current line.
-    needed = max(box.style['widows'] - 1 - next_lines, 0)
     if needed > over_orphans and not page_is_empty:
         # Total number of lines < orphans + widows
         remove_placeholders(context, line.children, absolute_boxes, 
fixed_boxes)
@@ -377,15 +376,21 @@
             # If we couldn’t break the line before but can break now, first 
try to
             # report footnotes and see if we don’t overflow.
             could_break_before = can_break_now = True
-            next_lines = len(tuple(lines_iterator))
+            needed = box.style['widows'] - 1
+            for _ in lines_iterator:
+                needed -= 1
+                # Don’t iterate over all lines as it can be long.
+                if needed == -1:
+                    break
             if len(new_children) + 1 < box.style['orphans']:
                 can_break_now = False
-            elif next_lines < box.style['widows']:
+            elif needed >= 0:
                 can_break_now = False
             if len(new_children) < box.style['orphans']:
                 could_break_before = False
-            elif next_lines + 1 < box.style['widows']:
+            elif needed > 0:
                 could_break_before = False
+            needed = max(0, needed)
             report = not context.in_column and can_break_now and not 
could_break_before
             reported_footnotes = 0
             while report and context.current_page_footnotes:
@@ -397,9 +402,8 @@
                     break
             else:
                 abort, stop, resume_at = _break_line(
-                    context, box, line, new_children, next_lines,
-                    page_is_empty, index, skip_stack, resume_at, 
absolute_boxes,
-                    fixed_boxes)
+                    context, box, line, new_children, needed, page_is_empty, 
index,
+                    skip_stack, resume_at, absolute_boxes, fixed_boxes)
 
             # Revert reported footnotes, as they’ve been reported starting 
from the last
             # one.
@@ -414,8 +418,7 @@
         # "When an unforced page break occurs here, both the adjoining
         #  ‘margin-top’ and ‘margin-bottom’ are set to zero."
         # See issue #115.
-        elif page_is_empty and context.overflows_page(
-                bottom_space, new_position_y):
+        elif page_is_empty and context.overflows_page(bottom_space, 
new_position_y):
             # Remove the top border when a page is empty and the box is
             # too high to be drawn in one page
             new_position_y -= box.margin_top
@@ -433,8 +436,7 @@
                 overflow = (
                     overflow or
                     context.reported_footnotes or
-                    context.overflows_page(
-                        bottom_space, new_position_y + offset_y))
+                    context.overflows_page(bottom_space, new_position_y + 
offset_y))
                 if overflow:
                     context.report_footnote(footnote)
                     # If we've put other content on this page, then we may want
@@ -443,11 +445,15 @@
                     # even try.
                     if new_children or not page_is_empty:
                         if footnote.style['footnote_policy'] == 'line':
-                            next_lines = len(tuple(lines_iterator))
+                            if needed := box.style['widows'] - 1:
+                                for _ in lines_iterator:
+                                    needed -= 1
+                                    # Don’t iterate over all lines as it can 
be long.
+                                    if needed == 0:
+                                        break
                             abort, stop, resume_at = _break_line(
-                                context, box, line, new_children,
-                                next_lines, page_is_empty, index,
-                                skip_stack, resume_at, absolute_boxes,
+                                context, box, line, new_children, needed, 
page_is_empty,
+                                index, skip_stack, resume_at, absolute_boxes,
                                 fixed_boxes)
                             break_linebox = True
                             break
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/weasyprint-68.0/weasyprint/layout/preferred.py 
new/weasyprint-68.1/weasyprint/layout/preferred.py
--- old/weasyprint-68.0/weasyprint/layout/preferred.py  2026-01-19 
15:54:27.784017800 +0100
+++ new/weasyprint-68.1/weasyprint/layout/preferred.py  2026-02-06 
16:03:54.122264100 +0100
@@ -109,7 +109,7 @@
 
         for value in ('padding_left', 'padding_right'):
             style_value = box.style[value]
-            if style_value != 'auto':
+            if style_value != 'auto' and not check_math(style_value):
                 if style_value.unit.lower() == 'px':
                     width -= style_value.value
                 else:
@@ -175,7 +175,7 @@
         (['margin_right', 'padding_right'] if right else [])
     ):
         style_value = box.style[value]
-        if style_value != 'auto':
+        if style_value != 'auto' and not check_math(style_value):
             if style_value.unit.lower() == 'px':
                 width += style_value.value
             else:
@@ -263,7 +263,7 @@
 def column_group_content_width(context, box):
     """Return the *-content width for a ``TableColumnGroupBox``."""
     width = box.style['width']
-    if width == 'auto' or width.unit == '%':
+    if width == 'auto' or check_math(width) or width.unit == '%':
         width = 0
     else:
         assert width.unit.lower() == 'px'
@@ -597,21 +597,22 @@
     # Define constrainedness
     constrainedness = [False for i in range(grid_width)]
     for i in range(grid_width):
-        if (column_groups[i] and column_groups[i].style['width'] != 'auto' and
-                column_groups[i].style['width'].unit != '%'):
-            constrainedness[i] = True
-            continue
-        if (columns[i] and columns[i].style['width'] != 'auto' and
-                columns[i].style['width'].unit != '%'):
-            constrainedness[i] = True
-            continue
-        for cell in zipped_grid[i]:
-            if (cell and cell.colspan == 1 and
-                    cell.style['width'] != 'auto' and
-                    not check_math(cell.style['width']) and
-                    cell.style['width'].unit != '%'):
+        if column_groups[i]:
+            width = column_groups[i].style['width']
+            if width != 'auto' and not check_math(width) and width.unit != '%':
                 constrainedness[i] = True
-                break
+                continue
+        if columns[i]:
+            width = columns[i].style['width']
+            if width != 'auto' and not check_math(width) and width.unit != '%':
+                constrainedness[i] = True
+                continue
+        for cell in zipped_grid[i]:
+            if cell and cell.colspan == 1:
+                width = cell.style['width']
+                if width != 'auto' and not check_math(width) and width.unit != 
'%':
+                    constrainedness[i] = True
+                    break
 
     intrinsic_percentages = [
         min(percentage, 100 - sum(intrinsic_percentages[:i]))
@@ -679,7 +680,8 @@
             sum(max_content_widths), large_percentage_contribution,
             *small_percentage_contributions]))
 
-    if table.style['width'] != 'auto' and table.style['width'].unit.lower() == 
'px':
+    width = table.style['width']
+    if width != 'auto' and not check_math(width) and width.unit.lower() == 
'px':
         # "percentages on the following properties are treated instead as
         # though they were the following: width: auto"
         # https://dbaron.org/css/intrinsic/#outer-intrinsic
@@ -714,12 +716,16 @@
     width = box.style['width']
     if width == 'auto':
         height = box.style['height']
-        if height == 'auto' or height.unit == '%':
+        if height == 'auto' or check_math(height) or height.unit == '%':
             height = 'auto'
         else:
             assert height.unit.lower() == 'px'
             height = height.value
-        if box.style['max_width'] != 'auto' and box.style['max_width'].unit == 
'%':
+        unknown_max_width = (
+            box.style['max_width'] != 'auto' and
+            not check_math(box.style['max_width']) and
+            box.style['max_width'].unit == '%')
+        if unknown_max_width:
             # See https://drafts.csswg.org/css-sizing/#intrinsic-contribution
             width = 0
         else:
@@ -730,7 +736,7 @@
             width, _ = default_image_sizing(
                 intrinsic_width, intrinsic_height, intrinsic_ratio, 'auto',
                 height, default_width=0, default_height=0)
-    elif box.style['width'].unit == '%':
+    elif check_math(box.style['width']) or box.style['width'].unit == '%':
         # See https://drafts.csswg.org/css-sizing/#intrinsic-contribution
         width = 0
     else:
@@ -744,7 +750,7 @@
     width = box.style['width']
     if width == 'auto':
         height = box.style['height']
-        if height == 'auto' or height.unit == '%':
+        if height == 'auto' or check_math(height) or height.unit == '%':
             height = 'auto'
         else:
             assert height.unit.lower() == 'px'
@@ -756,7 +762,7 @@
         width, _ = default_image_sizing(
             intrinsic_width, intrinsic_height, intrinsic_ratio, 'auto', height,
             default_width=300, default_height=150)
-    elif box.style['width'].unit == '%':
+    elif check_math(box.style['width']) or box.style['width'].unit == '%':
         # See https://drafts.csswg.org/css-sizing/#intrinsic-contribution
         width = 0
     else:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/weasyprint-68.0/weasyprint/pdf/stream.py 
new/weasyprint-68.1/weasyprint/pdf/stream.py
--- old/weasyprint-68.0/weasyprint/pdf/stream.py        2026-01-19 
15:54:27.785017700 +0100
+++ new/weasyprint-68.1/weasyprint/pdf/stream.py        2026-02-06 
16:03:54.123264300 +0100
@@ -201,6 +201,8 @@
             'Group': pydyf.Dictionary({
                 'Type': '/Group',
                 'S': '/Transparency',
+                'I': 'true',
+                'CS': '/DeviceRGB',
             }),
         })
         group = self.clone(resources=resources, extra=extra)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/weasyprint-68.0/weasyprint/svg/__init__.py 
new/weasyprint-68.1/weasyprint/svg/__init__.py
--- old/weasyprint-68.0/weasyprint/svg/__init__.py      2026-01-19 
15:54:27.785017700 +0100
+++ new/weasyprint-68.1/weasyprint/svg/__init__.py      2026-02-06 
16:03:54.123264300 +0100
@@ -9,7 +9,7 @@
 
 from ..urls import get_url_attribute
 from .css import parse_declarations, parse_stylesheets
-from .defs import apply_filters, clip_path, draw_gradient_or_pattern, 
paint_mask, use
+from .defs import apply_filters, draw_gradient_or_pattern, paint_mask, use
 from .images import image, svg
 from .path import path
 from .shapes import circle, ellipse, line, polygon, polyline, rect
@@ -24,7 +24,6 @@
 TAGS = {
     'a': text,
     'circle': circle,
-    'clipPath': clip_path,
     'ellipse': ellipse,
     'image': image,
     'line': line,
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/weasyprint-68.0/weasyprint/svg/defs.py 
new/weasyprint-68.1/weasyprint/svg/defs.py
--- old/weasyprint-68.0/weasyprint/svg/defs.py  2026-01-19 15:54:27.785017700 
+0100
+++ new/weasyprint-68.1/weasyprint/svg/defs.py  2026-02-06 16:03:54.123264300 
+0100
@@ -517,9 +517,3 @@
     svg.stream = svg.stream.set_alpha_state(x, y, width, height)
     svg.draw_node(mask, font_size)
     svg.stream = svg_stream
-
-
-def clip_path(svg, node, font_size):
-    """Store a clip path definition."""
-    if 'id' in node.attrib:
-        svg.paths[node.attrib['id']] = node
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/weasyprint-68.0/weasyprint/svg/text.py 
new/weasyprint-68.1/weasyprint/svg/text.py
--- old/weasyprint-68.0/weasyprint/svg/text.py  2026-01-19 15:54:27.785017700 
+0100
+++ new/weasyprint-68.1/weasyprint/svg/text.py  2026-02-06 16:03:54.124264200 
+0100
@@ -141,7 +141,7 @@
             svg.cursor_d_position[1] = 0
         svg.cursor_d_position[0] += dx or 0
         svg.cursor_d_position[1] += dy or 0
-        layout, _, _, width, height, _ = split_first_line(
+        layout, _, _, width, height, baseline = split_first_line(
             letter, style, svg.context, inf, 0)
         x = svg.cursor_position[0] if x is None else x
         y = svg.cursor_position[1] if y is None else y
@@ -154,8 +154,9 @@
         y_position = y + svg.cursor_d_position[1] + y_align
         angle = last_r if r is None else r
         points = (
-            (x_position, y_position),
-            (x_position + width, y_position - height))
+            (x_position, y_position - baseline),
+            (x_position + width, y_position - baseline + height))
+        # TODO: Use ink extents instead of logical from line_break.line_size().
         node.text_bounding_box = extend_bounding_box(
             node.text_bounding_box, points)
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/weasyprint-68.0/weasyprint/text/line_break.py 
new/weasyprint-68.1/weasyprint/text/line_break.py
--- old/weasyprint-68.0/weasyprint/text/line_break.py   2026-01-19 
15:54:27.786018000 +0100
+++ new/weasyprint-68.1/weasyprint/text/line_break.py   2026-02-06 
16:03:54.124264200 +0100
@@ -256,6 +256,8 @@
     ``baseline``: baseline in pixels of the first line
 
     """
+    from ..layout.percent import percentage
+
     # See https://www.w3.org/TR/css-text-3/#white-space-property
     text_wrap = style['white_space'] in ('normal', 'pre-wrap', 'pre-line')
     space_collapse = style['white_space'] in ('normal', 'nowrap', 'pre-line')
@@ -392,11 +394,8 @@
                     # This word is long enough.
                     first_line_width, _ = line_size(first_line, style)
                     space = max_width - first_line_width
-                    if style['hyphenate_limit_zone'].unit == '%':
-                        limit_zone = (
-                            max_width * style['hyphenate_limit_zone'].value / 
100)
-                    else:
-                        limit_zone = style['hyphenate_limit_zone'].value
+                    limit_zone = percentage(
+                        style['hyphenate_limit_zone'], style, max_width)
                     if space > limit_zone or space < 0:
                         # Available space is worth the try, or the line is 
even too long
                         # to fit: try to hyphenate.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/weasyprint-68.0/weasyprint/urls.py 
new/weasyprint-68.1/weasyprint/urls.py
--- old/weasyprint-68.0/weasyprint/urls.py      2026-01-19 15:54:27.786018000 
+0100
+++ new/weasyprint-68.1/weasyprint/urls.py      2026-02-06 16:03:54.124264200 
+0100
@@ -330,17 +330,18 @@
 
         """
         # Discard URLs with no or invalid protocol.
-        if not UNICODE_SCHEME_RE.match(url):  # pragma: no cover
+        if not (match := UNICODE_SCHEME_RE.match(url)):  # pragma: no cover
             raise ValueError(f'Not an absolute URI: {url}')
+        scheme = match[1].lower()
 
         # Discard URLs with forbidden protocol.
         if self._allowed_protocols is not None:
-            if url.split('://', 1)[0].lower() not in self._allowed_protocols:
+            if scheme not in self._allowed_protocols:
                 raise ValueError(f'URI uses disallowed protocol: {url}')
 
         # Remove query and fragment parts from file URLs.
         # See https://bugs.python.org/issue34702.
-        if url.lower().startswith('file://'):
+        if scheme == 'file':
             url = url.split('?')[0]
 
         # Transform Unicode IRI to ASCII URI.
@@ -385,7 +386,7 @@
     :param body: The body of the HTTP response.
     :type headers: dict or email.message.EmailMessage
     :param headers: The headers of the HTTP response.
-    :param str status: The status of the HTTP response.
+    :param int status: The status of the HTTP response.
 
     Has the same interface as :class:`urllib.response.addinfourl`.
 
@@ -395,7 +396,7 @@
     is used elsewhere, the file object has to be closed manually.
 
     """
-    def __init__(self, url, body=None, headers=None, status='200 OK', 
**kwargs):
+    def __init__(self, url, body=None, headers=None, status=200, **kwargs):
         self.url = url
         self.status = status
 
@@ -404,7 +405,10 @@
         else:
             self.headers = EmailMessage()
             for key, value in (headers or {}).items():
-                self.headers[key] = value
+                try:
+                    self.headers[key] = value
+                except ValueError:
+                    pass  # Ignore forbidden duplicated headers.
 
         if hasattr(body, 'read'):
             self._file_obj = body
@@ -440,6 +444,19 @@
     def charset(self):
         return self.headers.get_param('charset')
 
+    def geturl(self):
+        return self.url
+
+    def info(self):
+        return self.headers
+
+    @property
+    def code(self):
+        return self.status
+
+    def getcode(self):
+        return self.status
+
 
 @contextlib.contextmanager
 def fetch(url_fetcher, url):
@@ -467,7 +484,7 @@
         resource['body'] = resource.get('file_obj', resource.get('string'))
         content_type = resource.get('mime_type', 'application/octet-stream')
         if charset := resource.get('encoding'):
-            content_type += f';{charset}'
+            content_type += f'; charset={charset}'
         resource['headers'] = {'Content-Type': content_type}
         resource = URLFetcherResponse(**resource)
 

Reply via email to