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.
Files changed (67) hide show
  1. weasyprint/__init__.py +17 -7
  2. weasyprint/__main__.py +21 -10
  3. weasyprint/anchors.py +4 -4
  4. weasyprint/css/__init__.py +732 -67
  5. weasyprint/css/computed_values.py +65 -170
  6. weasyprint/css/counters.py +1 -1
  7. weasyprint/css/functions.py +206 -0
  8. weasyprint/css/html5_ua.css +3 -7
  9. weasyprint/css/html5_ua_form.css +2 -2
  10. weasyprint/css/media_queries.py +3 -1
  11. weasyprint/css/properties.py +6 -2
  12. weasyprint/css/{utils.py → tokens.py} +306 -397
  13. weasyprint/css/units.py +91 -0
  14. weasyprint/css/validation/__init__.py +1 -1
  15. weasyprint/css/validation/descriptors.py +47 -19
  16. weasyprint/css/validation/expanders.py +7 -8
  17. weasyprint/css/validation/properties.py +341 -357
  18. weasyprint/document.py +20 -19
  19. weasyprint/draw/__init__.py +56 -63
  20. weasyprint/draw/border.py +121 -69
  21. weasyprint/draw/color.py +1 -1
  22. weasyprint/draw/text.py +60 -41
  23. weasyprint/formatting_structure/boxes.py +24 -5
  24. weasyprint/formatting_structure/build.py +33 -45
  25. weasyprint/images.py +76 -62
  26. weasyprint/layout/__init__.py +32 -26
  27. weasyprint/layout/absolute.py +7 -6
  28. weasyprint/layout/background.py +7 -7
  29. weasyprint/layout/block.py +195 -152
  30. weasyprint/layout/column.py +19 -24
  31. weasyprint/layout/flex.py +54 -26
  32. weasyprint/layout/float.py +12 -7
  33. weasyprint/layout/grid.py +284 -90
  34. weasyprint/layout/inline.py +121 -68
  35. weasyprint/layout/page.py +45 -12
  36. weasyprint/layout/percent.py +14 -10
  37. weasyprint/layout/preferred.py +105 -63
  38. weasyprint/layout/replaced.py +9 -6
  39. weasyprint/layout/table.py +16 -9
  40. weasyprint/pdf/__init__.py +58 -18
  41. weasyprint/pdf/anchors.py +3 -4
  42. weasyprint/pdf/fonts.py +126 -69
  43. weasyprint/pdf/metadata.py +36 -4
  44. weasyprint/pdf/pdfa.py +19 -3
  45. weasyprint/pdf/pdfua.py +7 -115
  46. weasyprint/pdf/pdfx.py +83 -0
  47. weasyprint/pdf/stream.py +57 -49
  48. weasyprint/pdf/tags.py +307 -0
  49. weasyprint/stacking.py +14 -15
  50. weasyprint/svg/__init__.py +59 -32
  51. weasyprint/svg/bounding_box.py +4 -2
  52. weasyprint/svg/defs.py +4 -9
  53. weasyprint/svg/images.py +11 -3
  54. weasyprint/svg/text.py +11 -2
  55. weasyprint/svg/utils.py +15 -8
  56. weasyprint/text/constants.py +1 -1
  57. weasyprint/text/ffi.py +4 -3
  58. weasyprint/text/fonts.py +13 -5
  59. weasyprint/text/line_break.py +146 -43
  60. weasyprint/urls.py +41 -13
  61. {weasyprint-65.1.dist-info → weasyprint-67.0.dist-info}/METADATA +5 -6
  62. weasyprint-67.0.dist-info/RECORD +77 -0
  63. weasyprint/draw/stack.py +0 -13
  64. weasyprint-65.1.dist-info/RECORD +0 -74
  65. {weasyprint-65.1.dist-info → weasyprint-67.0.dist-info}/WHEEL +0 -0
  66. {weasyprint-65.1.dist-info → weasyprint-67.0.dist-info}/entry_points.txt +0 -0
  67. {weasyprint-65.1.dist-info → weasyprint-67.0.dist-info}/licenses/LICENSE +0 -0
@@ -6,7 +6,7 @@ from math import inf
6
6
  import pyphen
7
7
 
8
8
  from .constants import LST_TO_ISO, PANGO_DIRECTION, PANGO_WRAP_MODE
9
- from .ffi import FROM_UNITS, TO_UNITS, ffi, gobject, pango, pangoft2, unicode_to_char_p
9
+ from .ffi import FROM_UNITS, TO_UNITS, ffi, gobject, pango, unicode_to_char_p
10
10
  from .fonts import font_features, get_font_description
11
11
 
12
12
 
@@ -56,22 +56,16 @@ def first_line_metrics(first_line, text, layout, resume_at, space_collapse,
56
56
 
57
57
  class Layout:
58
58
  """Object holding PangoLayout-related cdata pointers."""
59
- def __init__(self, context, style, justification_spacing=0,
60
- max_width=None):
59
+ def __init__(self, style, justification_spacing=0, max_width=None):
61
60
  self.justification_spacing = justification_spacing
62
- self.setup(context, style)
61
+ self.setup(style)
63
62
  self.max_width = max_width
64
63
 
65
- def setup(self, context, style):
66
- self.context = context
64
+ def setup(self, style):
67
65
  self.style = style
68
66
  self.first_line_direction = 0
69
67
 
70
- if context is None:
71
- font_map = ffi.gc(
72
- pangoft2.pango_ft2_font_map_new(), gobject.g_object_unref)
73
- else:
74
- font_map = context.font_config.font_map
68
+ font_map = style.font_config.font_map
75
69
  pango_context = ffi.gc(
76
70
  pango.pango_font_map_create_context(font_map),
77
71
  gobject.g_object_unref)
@@ -127,11 +121,11 @@ class Layout:
127
121
  style['font_variant_position'], style['font_variant_caps'],
128
122
  style['font_variant_numeric'], style['font_variant_alternates'],
129
123
  style['font_variant_east_asian'], style['font_feature_settings'])
130
- if features and context:
124
+ if features:
131
125
  features = ','.join(
132
126
  f'{key} {value}' for key, value in features.items()).encode()
133
127
  # In the meantime, keep a cache to avoid leaking too many of them.
134
- attr = context.font_features.setdefault(
128
+ attr = style.font_config.font_features.setdefault(
135
129
  features, pango.pango_attr_font_features_new(features))
136
130
  attr_list = pango.pango_attr_list_new()
137
131
  pango.pango_attr_list_insert(attr_list, attr)
@@ -209,8 +203,7 @@ class Layout:
209
203
 
210
204
  def set_tabs(self):
211
205
  if isinstance(self.style['tab_size'], int):
212
- layout = Layout(
213
- self.context, self.style, self.justification_spacing)
206
+ layout = Layout(self.style, self.justification_spacing)
214
207
  layout.set_text(' ' * self.style['tab_size'])
215
208
  line, _ = layout.get_first_line()
216
209
  width, _ = line_size(line, self.style)
@@ -228,13 +221,13 @@ class Layout:
228
221
  del self.layout, self.language, self.style
229
222
 
230
223
  def reactivate(self, style):
231
- self.setup(self.context, style)
224
+ self.setup(style)
232
225
  self.set_text(self.text, justify=True)
233
226
 
234
227
 
235
228
  def create_layout(text, style, context, max_width, justification_spacing):
236
229
  """Return an opaque Pango layout with default Pango line-breaks."""
237
- layout = Layout(context, style, justification_spacing, max_width)
230
+ layout = Layout(style, justification_spacing, max_width)
238
231
 
239
232
  # Make sure that max_width * Pango.SCALE == max_width * 1024 fits in a
240
233
  # signed integer. Treat bigger values same as None: unconstrained width.
@@ -377,27 +370,45 @@ def split_first_line(text, style, context, max_width, justification_spacing,
377
370
  soft_hyphen = '\xad'
378
371
 
379
372
  auto_hyphenation = manual_hyphenation = False
373
+
380
374
  if hyphens != 'none':
381
375
  manual_hyphenation = soft_hyphen in first_line_text + second_line_text
376
+
382
377
  if hyphens == 'auto' and lang:
383
- next_word_boundaries = get_next_word_boundaries(second_line_text, lang)
384
- if next_word_boundaries:
385
- # We have a word to hyphenate
386
- start_word, stop_word = next_word_boundaries
387
- next_word = second_line_text[start_word:stop_word]
388
- if stop_word - start_word >= total:
389
- # This word is long enough
390
- first_line_width, _ = line_size(first_line, style)
391
- space = max_width - first_line_width
392
- if style['hyphenate_limit_zone'].unit == '%':
393
- limit_zone = (
394
- max_width * style['hyphenate_limit_zone'].value / 100)
395
- else:
396
- limit_zone = style['hyphenate_limit_zone'].value
397
- if space > limit_zone or space < 0:
398
- # Available space is worth the try, or the line is even too
399
- # long to fit: try to hyphenate
400
- auto_hyphenation = True
378
+ # Get text until next line break opportunity.
379
+ next_text = second_line_text
380
+ if (next_break_point := get_next_break_point_from_text(second_line_text, lang)):
381
+ next_text = next_text[:next_break_point]
382
+
383
+ # Try all words included in this text.
384
+ next_text_index = 0
385
+ while next_text:
386
+ next_word_boundaries = get_next_word_boundaries(next_text, lang)
387
+ if next_word_boundaries:
388
+ # We have a word to hyphenate.
389
+ start_word, stop_word = next_word_boundaries
390
+ next_word = next_text[start_word:stop_word]
391
+ if stop_word - start_word >= total:
392
+ # This word is long enough.
393
+ first_line_width, _ = line_size(first_line, style)
394
+ space = max_width - first_line_width
395
+ if style['hyphenate_limit_zone'].unit == '%':
396
+ limit_zone = (
397
+ max_width * style['hyphenate_limit_zone'].value / 100)
398
+ else:
399
+ limit_zone = style['hyphenate_limit_zone'].value
400
+ if space > limit_zone or space < 0:
401
+ # Available space is worth the try, or the line is even too long
402
+ # to fit: try to hyphenate.
403
+ auto_hyphenation = True
404
+ next_text_index += start_word
405
+ break
406
+
407
+ # This word doesn’t work, try next one.
408
+ next_text = next_text[stop_word:]
409
+ next_text_index += stop_word
410
+ else:
411
+ break
401
412
 
402
413
  # Automatic hyphenation opportunities within a word must be ignored if the
403
414
  # word contains a conditional hyphen, in favor of the conditional
@@ -413,24 +424,21 @@ def split_first_line(text, style, context, max_width, justification_spacing,
413
424
  match.start() for match in re.finditer(soft_hyphen, second_line_text)]
414
425
  soft_hyphen_indexes.reverse()
415
426
  dictionary_iterations = [second_line_text[:i+1] for i in soft_hyphen_indexes]
416
- start_word = 0
417
427
  elif auto_hyphenation:
418
428
  dictionary_key = (lang, left, right, total)
419
429
  dictionary = context.dictionaries.get(dictionary_key)
420
430
  if dictionary is None:
421
431
  dictionary = pyphen.Pyphen(lang=lang, left=left, right=right)
422
432
  context.dictionaries[dictionary_key] = dictionary
433
+ previous_words = second_line_text[:next_text_index]
423
434
  dictionary_iterations = [
424
- start for start, end in dictionary.iterate(next_word)]
435
+ previous_words + start for start, end in dictionary.iterate(next_word)]
425
436
  else:
426
437
  dictionary_iterations = []
427
438
 
428
439
  if dictionary_iterations:
429
440
  for first_word_part in dictionary_iterations:
430
- new_first_line_text = (
431
- first_line_text +
432
- second_line_text[:start_word] +
433
- first_word_part)
441
+ new_first_line_text = first_line_text + first_word_part
434
442
  hyphenated_first_line_text = (
435
443
  new_first_line_text + style['hyphenate_character'])
436
444
  new_layout = create_layout(
@@ -501,6 +509,97 @@ def split_first_line(text, style, context, max_width, justification_spacing,
501
509
  hyphenated, style['hyphenate_character'])
502
510
 
503
511
 
512
+ def _font_style_cache_key(style, include_size=False):
513
+ key = str((
514
+ style['font_family'],
515
+ style['font_style'],
516
+ style['font_stretch'],
517
+ style['font_weight'],
518
+ style['font_variant_ligatures'],
519
+ style['font_variant_position'],
520
+ style['font_variant_caps'],
521
+ style['font_variant_numeric'],
522
+ style['font_variant_alternates'],
523
+ style['font_variant_east_asian'],
524
+ style['font_feature_settings'],
525
+ style['font_variation_settings'],
526
+ style['font_language_override'],
527
+ style['lang'],
528
+ ))
529
+ if include_size:
530
+ key += str(style['font_size']) + str(style['line_height'])
531
+ return key
532
+
533
+
534
+ def strut(style):
535
+ """Return a tuple of the used value of ``line-height`` and the baseline.
536
+
537
+ The baseline is given from the top edge of line height.
538
+
539
+ """
540
+ if style['font_size'] == 0:
541
+ return 0, 0
542
+
543
+ key = _font_style_cache_key(style, include_size=True)
544
+ if key in style.font_config.strut_layouts:
545
+ return style.font_config.strut_layouts[key]
546
+
547
+ layout = Layout(style)
548
+ layout.set_text(' ')
549
+ line, _ = layout.get_first_line()
550
+ _, _, _, _, text_height, baseline = first_line_metrics(
551
+ line, '', layout, resume_at=None, space_collapse=False, style=style)
552
+ if style['line_height'] == 'normal':
553
+ result = text_height, baseline
554
+ style.font_config.strut_layouts[key] = result
555
+ return result
556
+ type_, line_height = style['line_height']
557
+ if type_ == 'NUMBER':
558
+ line_height *= style['font_size']
559
+ result = line_height, baseline + (line_height - text_height) / 2
560
+ style.font_config.strut_layouts[key] = result
561
+ return result
562
+
563
+
564
+ def character_ratio(style, unit):
565
+ """Return the font size ratio used by given unit."""
566
+ character = {'ex': 'x', 'cap': 'O', 'ic': '水', 'ch': '0'}.get(unit)
567
+ assert character
568
+
569
+ cache = style.cache.setdefault(unit, {})
570
+ cache_key = _font_style_cache_key(style)
571
+ if cache_key in cache:
572
+ return cache[cache_key]
573
+
574
+ # Avoid recursion for letter-spacing and word-spacing properties
575
+ style = style.copy()
576
+ style['letter_spacing'] = 'normal'
577
+ style['word_spacing'] = 0
578
+ # Random big value
579
+ style['font_size'] = 1000
580
+
581
+ layout = Layout(style)
582
+ layout.set_text(character)
583
+ line, _ = layout.get_first_line()
584
+
585
+ ink_extents = ffi.new('PangoRectangle *')
586
+ logical_extents = ffi.new('PangoRectangle *')
587
+ pango.pango_layout_line_get_extents(line, ink_extents, logical_extents)
588
+ if unit == 'ex':
589
+ measure = -ink_extents.y * FROM_UNITS
590
+ elif character == 'cap':
591
+ measure = logical_extents.height * FROM_UNITS
592
+ else:
593
+ measure = logical_extents.width * FROM_UNITS
594
+ ffi.release(ink_extents)
595
+ ffi.release(logical_extents)
596
+
597
+ # Zero means some kind of failure, fallback is 0.5.
598
+ # We round to try keeping exact values that were altered by Pango.
599
+ cache[cache_key] = round(measure / style['font_size'], 5) or 0.5
600
+ return cache[cache_key]
601
+
602
+
504
603
  def get_log_attrs(text, lang):
505
604
  if lang:
506
605
  lang_p, lang = unicode_to_char_p(lang)
@@ -526,12 +625,16 @@ def get_next_break_point(log_attrs):
526
625
  return i
527
626
 
528
627
 
529
- def can_break_text(text, lang):
628
+ def get_next_break_point_from_text(text, lang):
530
629
  if not text or len(text) < 2:
531
630
  return None
532
631
  log_attrs = get_log_attrs(text, lang)
533
632
  length = len(text) + 1
534
- return get_next_break_point(log_attrs[1:length-1]) is not None
633
+ return get_next_break_point(log_attrs[1:length-1])
634
+
635
+
636
+ def can_break_text(text, lang):
637
+ return get_next_break_point_from_text(text, lang) is not None
535
638
 
536
639
 
537
640
  def get_next_word_boundaries(text, lang):
weasyprint/urls.py CHANGED
@@ -10,7 +10,7 @@ import zlib
10
10
  from gzip import GzipFile
11
11
  from pathlib import Path
12
12
  from urllib.parse import quote, unquote, urljoin, urlsplit
13
- from urllib.request import Request, pathname2url, urlopen
13
+ from urllib.request import Request, pathname2url, url2pathname, urlopen
14
14
 
15
15
  from . import __version__
16
16
  from .logger import LOGGER
@@ -78,7 +78,7 @@ def path2url(path):
78
78
  elif isinstance(path, bytes):
79
79
  path = path.decode(FILESYSTEM_ENCODING)
80
80
  # If a trailing path.sep is given, keep it
81
- wants_trailing_slash = path.endswith(os.path.sep) or path.endswith('/')
81
+ wants_trailing_slash = path.endswith((os.path.sep, '/'))
82
82
  path = os.path.abspath(path)
83
83
  if wants_trailing_slash or os.path.isdir(path):
84
84
  # Make sure directory names have a trailing slash.
@@ -122,6 +122,16 @@ def get_url_attribute(element, attr_name, base_url, allow_relative=False):
122
122
  (element.tag, attr_name, value))
123
123
 
124
124
 
125
+ def get_url_tuple(url, base_url):
126
+ """Get tuple describing internal or external URI."""
127
+ if url.startswith('#'):
128
+ return ('internal', unquote(url[1:]))
129
+ elif url_is_absolute(url):
130
+ return ('external', iri_to_uri(url))
131
+ elif base_url:
132
+ return ('external', iri_to_uri(urljoin(base_url, url)))
133
+
134
+
125
135
  def url_join(base_url, url, allow_relative, context, context_args):
126
136
  """Like urllib.urljoin, but warn if base_url is required but missing."""
127
137
  if url_is_absolute(url):
@@ -177,7 +187,8 @@ def ensure_url(string):
177
187
  return string if url_is_absolute(string) else path2url(string)
178
188
 
179
189
 
180
- def default_url_fetcher(url, timeout=10, ssl_context=None):
190
+ def default_url_fetcher(url, timeout=10, ssl_context=None, http_headers=None,
191
+ allowed_protocols=None):
181
192
  """Fetch an external resource such as an image or stylesheet.
182
193
 
183
194
  Another callable with the same signature can be given as the
@@ -190,6 +201,10 @@ def default_url_fetcher(url, timeout=10, ssl_context=None):
190
201
  The number of seconds before HTTP requests are dropped.
191
202
  :param ssl.SSLContext ssl_context:
192
203
  An SSL context used for HTTP requests.
204
+ :param dict http_headers:
205
+ Additional HTTP headers used for HTTP requests.
206
+ :param set allowed_protocols:
207
+ A set of authorized protocols.
193
208
  :raises: An exception indicating failure, e.g. :obj:`ValueError` on
194
209
  syntactically invalid URL.
195
210
  :returns: A :obj:`dict` with the following keys:
@@ -205,7 +220,9 @@ def default_url_fetcher(url, timeout=10, ssl_context=None):
205
220
  if there were e.g. HTTP redirects.
206
221
  * Optionally: ``filename``, the filename of the resource. Usually
207
222
  derived from the *filename* parameter in a *Content-Disposition*
208
- header
223
+ header.
224
+ * Optionally: ``path``, the path of the resource if it is stored on the
225
+ local filesystem.
209
226
 
210
227
  If a ``file_obj`` key is given, it is the caller’s responsibility
211
228
  to call ``file_obj.close()``. The default function used internally to
@@ -215,22 +232,33 @@ def default_url_fetcher(url, timeout=10, ssl_context=None):
215
232
 
216
233
  """
217
234
  if UNICODE_SCHEME_RE.match(url):
235
+ if allowed_protocols is not None:
236
+ if url.split('://', 1)[0].lower() not in allowed_protocols:
237
+ raise ValueError(f'URI uses disallowed protocol: {url}')
238
+
218
239
  # See https://bugs.python.org/issue34702
219
- if url.startswith('file://'):
240
+ if url.lower().startswith('file://'):
220
241
  url = url.split('?')[0]
242
+ path = url2pathname(url.removeprefix('file:'))
243
+ else:
244
+ path = None
221
245
 
222
246
  url = iri_to_uri(url)
247
+ if http_headers is not None:
248
+ http_headers = {**HTTP_HEADERS, **http_headers}
249
+ else:
250
+ http_headers = HTTP_HEADERS
223
251
  response = urlopen(
224
- Request(url, headers=HTTP_HEADERS), timeout=timeout,
252
+ Request(url, headers=http_headers), timeout=timeout,
225
253
  context=ssl_context)
226
- response_info = response.info()
227
254
  result = {
228
- 'redirected_url': response.geturl(),
229
- 'mime_type': response_info.get_content_type(),
230
- 'encoding': response_info.get_param('charset'),
231
- 'filename': response_info.get_filename(),
255
+ 'redirected_url': response.url,
256
+ 'mime_type': response.headers.get_content_type(),
257
+ 'encoding': response.headers.get_param('charset'),
258
+ 'filename': response.headers.get_filename(),
259
+ 'path': path,
232
260
  }
233
- content_encoding = response_info.get('Content-Encoding')
261
+ content_encoding = response.headers.get('Content-Encoding')
234
262
  if content_encoding == 'gzip':
235
263
  result['file_obj'] = StreamingGzipFile(fileobj=response)
236
264
  elif content_encoding == 'deflate':
@@ -244,7 +272,7 @@ def default_url_fetcher(url, timeout=10, ssl_context=None):
244
272
  result['file_obj'] = response
245
273
  return result
246
274
  else: # pragma: no cover
247
- raise ValueError('Not an absolute URI: %r' % url)
275
+ raise ValueError(f'Not an absolute URI: {url}')
248
276
 
249
277
 
250
278
  class URLFetchingError(IOError):
@@ -1,11 +1,11 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: weasyprint
3
- Version: 65.1
3
+ Version: 67.0
4
4
  Summary: The Awesome Document Factory
5
5
  Keywords: html,css,pdf,converter
6
6
  Author-email: Simon Sapin <simon.sapin@exyr.org>
7
7
  Maintainer-email: CourtBouillon <contact@courtbouillon.org>
8
- Requires-Python: >=3.9
8
+ Requires-Python: >=3.10
9
9
  Description-Content-Type: text/x-rst
10
10
  Classifier: Development Status :: 5 - Production/Stable
11
11
  Classifier: Intended Audience :: Developers
@@ -14,7 +14,6 @@ Classifier: Operating System :: OS Independent
14
14
  Classifier: Programming Language :: Python
15
15
  Classifier: Programming Language :: Python :: 3
16
16
  Classifier: Programming Language :: Python :: 3 :: Only
17
- Classifier: Programming Language :: Python :: 3.9
18
17
  Classifier: Programming Language :: Python :: 3.10
19
18
  Classifier: Programming Language :: Python :: 3.11
20
19
  Classifier: Programming Language :: Python :: 3.12
@@ -29,11 +28,11 @@ License-File: LICENSE
29
28
  Requires-Dist: pydyf >=0.11.0
30
29
  Requires-Dist: cffi >=0.6
31
30
  Requires-Dist: tinyhtml5 >=2.0.0b1
32
- Requires-Dist: tinycss2 >=1.4.0
31
+ Requires-Dist: tinycss2 >=1.5.0
33
32
  Requires-Dist: cssselect2 >=0.8.0
34
33
  Requires-Dist: Pyphen >=0.9.1
35
34
  Requires-Dist: Pillow >=9.1.0
36
- Requires-Dist: fonttools[woff] >=4.0.0
35
+ Requires-Dist: fonttools[woff] >=4.59.2
37
36
  Requires-Dist: sphinx ; extra == "doc"
38
37
  Requires-Dist: furo ; extra == "doc"
39
38
  Requires-Dist: pytest ; extra == "test"
@@ -62,7 +61,7 @@ WebKit or Gecko. The CSS layout engine is written in Python, designed for
62
61
  pagination, and meant to be easy to hack on.
63
62
 
64
63
  * Free software: BSD license
65
- * For Python 3.9+, tested on CPython and PyPy
64
+ * For Python 3.10+, tested on CPython and PyPy
66
65
  * Documentation: https://doc.courtbouillon.org/weasyprint
67
66
  * Examples: https://weasyprint.org/#samples
68
67
  * Changelog: https://github.com/Kozea/WeasyPrint/releases
@@ -0,0 +1,77 @@
1
+ weasyprint/__init__.py,sha256=n_NTbjsAu2kFJgcsSMiEd0R22wSLE6fQwoWQJ4SbxMY,17861
2
+ weasyprint/__main__.py,sha256=7K-DxthsEP_bMFjOx6BfS52b2ZwkgmFXaQR-lNQvjOc,7828
3
+ weasyprint/anchors.py,sha256=fytc05l3TO4AWuyPJx532RBEbZe8L7UlnbYYJpB-bGA,6472
4
+ weasyprint/document.py,sha256=VV3QDVnAsFfBbLZsrEdAdrz28CW-tJ90jk-l7U0HYOo,17108
5
+ weasyprint/html.py,sha256=om7dvhx12ecunTaVclmWejkr5nPQF1BZmeH7jqQ_4GM,11590
6
+ weasyprint/images.py,sha256=2hb1oagE5mes9HXtjbGlvVXtbmYWClnA26FvJnk3UgQ,36393
7
+ weasyprint/logger.py,sha256=z1q548fX5shfAyLoMLeM9ozWGKgoBTKQsdlTtfRE_9U,1824
8
+ weasyprint/matrix.py,sha256=v1BPtyn_-S_4TrAUgzOOR-viUXgdqsABKRndCEprkPc,1909
9
+ weasyprint/stacking.py,sha256=6c6eZ_BxtcYvlEbH6JQdqaKwYBkuaqUwGGSs3OfkpS8,5697
10
+ weasyprint/urls.py,sha256=eGYReMlXG7vHXPts1xXscszdoJOSQRpV18H7g4NmtKY,11055
11
+ weasyprint/css/__init__.py,sha256=Rw24jzwWQNidyuy_VHLU3YFuthZ1VNwsnoM8zwr4XAQ,77767
12
+ weasyprint/css/computed_values.py,sha256=WPySY5DFXusCxa7wC5mUv4uzwBi2c7ACMLI3_iHqDnI,24187
13
+ weasyprint/css/counters.py,sha256=RAUuZGOLZj5oKrZexbkDUYPncTFWzwtT870xTKYkQmI,11374
14
+ weasyprint/css/functions.py,sha256=IabFkGPcrWOsAmIIY_wkn1EC7Hpwgx0I0VNlpCJIAsE,6562
15
+ weasyprint/css/html5_ph.css,sha256=l8t4ZN3KoevKx0UEfNw3_vgVjArcII6y5DZXZolWaw0,4629
16
+ weasyprint/css/html5_ua.css,sha256=mPHfk2DltgEiIAZr9G1b3kxcr2PHnLbtBkfQSGJIQj8,18359
17
+ weasyprint/css/html5_ua_form.css,sha256=O0L78oQN8ZuQcXlfe7XVTy5kM3nVF6-rfO3xALbYTy8,310
18
+ weasyprint/css/media_queries.py,sha256=Mh1JlSupMNjVJ4CKW3Yd7zR4W6NGNXp21y_4hajDDUY,1179
19
+ weasyprint/css/properties.py,sha256=64TpsS87--Xj_al7x0vREkbCDpyYQqHJx9H7vYxjKjY,11727
20
+ weasyprint/css/targets.py,sha256=5Ofw1RrmPsfQjDuZ1FCgktspGUai3wJmNa03MbT2sOI,8853
21
+ weasyprint/css/tokens.py,sha256=TsDJIpUkUvTbd6jqqEhvn0momX5nLdSkLTvQGRW5iBA,23403
22
+ weasyprint/css/units.py,sha256=loUL8k7y4OYxbNrowbUgsRuqKj-uFKfhZmKUjc6t3GQ,2901
23
+ weasyprint/css/validation/__init__.py,sha256=bt0Rqn5Jt2KLr6jFXau8QnosNIkz83DxSlca1t064hU,8469
24
+ weasyprint/css/validation/descriptors.py,sha256=7LN_0ed9wZWgYT3_4UrEE-R6mzpINGs-VM9lglV49P8,11839
25
+ weasyprint/css/validation/expanders.py,sha256=hqVJKiK5G-C8oNsCMTJ_X964qFip2j5hO4r912dnJX8,39233
26
+ weasyprint/css/validation/properties.py,sha256=EHwBGdXp3Qh6gb1nRlXrr6nwIWiC9eSw_UUSl5C2kcs,66824
27
+ weasyprint/draw/__init__.py,sha256=l4q_L6k93tIobB8hHui_PSElstO5MI5FTHCBTISb_ew,22715
28
+ weasyprint/draw/border.py,sha256=5s-FZyw3MN085cmmAJHp8Yms5gM90j-XzInpwldgPFs,30373
29
+ weasyprint/draw/color.py,sha256=xoqq6LmkyN1xdubX0Qm-MKy7xijzT3Zd7kF2MSaqZiQ,1449
30
+ weasyprint/draw/text.py,sha256=ik65U17D6lSxcfFs33yesxzg16eSKitvSjQwk-Ljj4I,12552
31
+ weasyprint/formatting_structure/boxes.py,sha256=qxfIMocW-JOr4UNkwee-kk8gZPJQdrGLskPMBTAdeuY,26974
32
+ weasyprint/formatting_structure/build.py,sha256=gyBoqCi2XyGhKSaiX8eUu5DFj0-E8DjrG1aFkxkvxZI,55931
33
+ weasyprint/layout/__init__.py,sha256=PosEQ0Y6o4FMriCroMc0PIoDRXuZkQiHDs_cLs4YdFA,16741
34
+ weasyprint/layout/absolute.py,sha256=JA4mjOweZt2h-Hrsa91nHnvzZDnF-gneMpZfcXiTugc,14005
35
+ weasyprint/layout/background.py,sha256=IfcmSZ-E4_NES-RSeKi5DGbmrloGdu9zVz5zZ87m9gU,10023
36
+ weasyprint/layout/block.py,sha256=czlH2egB6a92UeZGrdggiWOXPj50K5IR9UzJ7-y96GY,48691
37
+ weasyprint/layout/column.py,sha256=g64aPoNZYpPlrma4F1nyrW7_ji5DBYoamA92YrbFHZM,17209
38
+ weasyprint/layout/flex.py,sha256=KZNFElr2QDT5FYI6JlZGpXxJHYFvMMN8QGylLx98PUs,44148
39
+ weasyprint/layout/float.py,sha256=5iBDHg1KSexArW1SfbnMShuILzkkEH8DW7n1bdk5OU4,9488
40
+ weasyprint/layout/grid.py,sha256=HTJwzvJo38yrAzuxlYE9ioA5n2V5_jp1ZEO5vu877_c,63931
41
+ weasyprint/layout/inline.py,sha256=Iw3h9_N6a0rly9VGZ35LD0Pp8RDDGK7cYVKjZy17BZk,50447
42
+ weasyprint/layout/leader.py,sha256=wklI0aLyTx0VJhqU7D_FxtJpfe7dXswcN-VApAusM-Q,2825
43
+ weasyprint/layout/min_max.py,sha256=JdXJG9ISO_RsfeHua_-3g477a16I-NrnYuwH_tQwq4o,1527
44
+ weasyprint/layout/page.py,sha256=lMhopv3aZgJTfbyymg6zuMIpfPDxJUpPBxsZaXwzIl8,41566
45
+ weasyprint/layout/percent.py,sha256=F2kNcNz7b6to70GwiFFu-oWWpYs011TM021COH7feEc,5829
46
+ weasyprint/layout/preferred.py,sha256=PPLWMQYyL3f8rNnkQtw9PAJjca7eFJUksgPd_iVGjWU,33206
47
+ weasyprint/layout/replaced.py,sha256=7-foaAPIAVC329QEnUN_1u2U6VTrEbQ0Qx254BlrLCo,11357
48
+ weasyprint/layout/table.py,sha256=KyFOg9aiMl_wO31_0kQ7WrCVeHtGJY0Ir-hdub2qSSg,47778
49
+ weasyprint/pdf/__init__.py,sha256=Sm31SSWY2oFd_vH9g3TVshasygT9oDZNAlQa6kzOm0Q,13745
50
+ weasyprint/pdf/anchors.py,sha256=z2xqI_LTHK2IbpODQuYr5V7veaQuR27axy68EnpcLoc,17395
51
+ weasyprint/pdf/debug.py,sha256=reLw6U6hK94FOVNYW8psdt_SFN11iIe1rhYkr6sURF4,1407
52
+ weasyprint/pdf/fonts.py,sha256=EgB2w4VVpdYvk63NeGgx_nYQ4YrPa8DWhDswS_Fy3G8,27820
53
+ weasyprint/pdf/metadata.py,sha256=uObkbOKHMtvUp7iCLfgHhVRDB_lOIkSeTcoBAJ0euAY,5727
54
+ weasyprint/pdf/pdfa.py,sha256=fowoZeENgFFckUSd1jPmg6r_iZdS_q5tLGn34aTyKFs,4274
55
+ weasyprint/pdf/pdfua.py,sha256=96poiMjhQHd4MjAD0L5C8eio4yyYWjsbo57Ts_fhMEw,521
56
+ weasyprint/pdf/pdfx.py,sha256=ouxdFib1a7c4PoO3Mf9CS60lY2R8l-sFXF6zR4SW3F0,2664
57
+ weasyprint/pdf/sRGB2014.icc,sha256=OEuDLeNBIGZ0O1KnXukGtvufuNngnpNvwsQyI4Fcbgo,3024
58
+ weasyprint/pdf/stream.py,sha256=7L7EcGhxwDp06VSqgVWVP2ZgpIdJ35LPLE6IAN4sQzQ,11615
59
+ weasyprint/pdf/tags.py,sha256=914AozTYgFOMpFSP2UtEZCVzsPIKueHWjI9DA77w28M,11789
60
+ weasyprint/svg/__init__.py,sha256=6mGg2E1uolusHrc7ep4c9Ci3pnw8ECpEWNjNHZw0Ylk,31497
61
+ weasyprint/svg/bounding_box.py,sha256=auXs-vD2nvOx3cplHLGXFzy7X_f_IY4hg_IzKlUTXjM,13129
62
+ weasyprint/svg/css.py,sha256=AUsIim2rOmRGLgFuiWm4EzXwnrRlThczfM17Uq2MRUg,3832
63
+ weasyprint/svg/defs.py,sha256=poSH9LPdRTdqv0H-Ns7W58V4sRzULZKIjK_XCnhrxY0,20862
64
+ weasyprint/svg/images.py,sha256=msVOn7_DgKFVB6Pz25SDLXU22-p7H5y0fJGKBpFDjrc,3333
65
+ weasyprint/svg/path.py,sha256=Z-T6kbUU3pyHhzVV0JSBgO--XaCGXLsH-cS9iAsITMM,10064
66
+ weasyprint/svg/shapes.py,sha256=NDo0KMnwrm0hj3BOmfrKjRZo4iJF9o-MeUhZ5avANco,3845
67
+ weasyprint/svg/text.py,sha256=vJ2FUquzpnGclA_y1dXu4jImvkGR0NvvnyoVLyHZlk0,6663
68
+ weasyprint/svg/utils.py,sha256=BEJvyOtxo4tAgLL-RORaEMIBAgmIZzwBNp2YuN1u3NM,7289
69
+ weasyprint/text/constants.py,sha256=gtC92Hbzci0976gVTct3roVKLcQjNWIQM43zuEBqIuY,14189
70
+ weasyprint/text/ffi.py,sha256=13hLmV19RiTspKHna_RlZMar6CpTIHwPDPSGV1FmhNc,18418
71
+ weasyprint/text/fonts.py,sha256=4NzMy3EXw0FunatLMctY24NWj3CdgIsEbZ6NwGNMseU,17656
72
+ weasyprint/text/line_break.py,sha256=0hrm7916GfSFtGf4SLRzB8rp_-iwpi27bVaNI--th0c,28215
73
+ weasyprint-67.0.dist-info/entry_points.txt,sha256=wgDp3XXzFywdYgI5vUWMp1zAwx1sZXXH0FTUQbFOq6A,55
74
+ weasyprint-67.0.dist-info/licenses/LICENSE,sha256=v9FOzPphAFdUYOaFVWsYM5nUvTNZBOPJUhsBFtIcVNo,1534
75
+ weasyprint-67.0.dist-info/WHEEL,sha256=G2gURzTEtmeR8nrdXUJfNiB3VYVxigPQ-bEQujpNiNs,82
76
+ weasyprint-67.0.dist-info/METADATA,sha256=Pnp6hPX5OIgs5PPv8ws3-Y-hl2lKTqwDh3_WD8UCsvM,3660
77
+ weasyprint-67.0.dist-info/RECORD,,
weasyprint/draw/stack.py DELETED
@@ -1,13 +0,0 @@
1
- """Drawing stack context manager."""
2
-
3
- from contextlib import contextmanager
4
-
5
-
6
- @contextmanager
7
- def stacked(stream):
8
- """Save and restore stream context when used with the ``with`` keyword."""
9
- stream.push_state()
10
- try:
11
- yield
12
- finally:
13
- stream.pop_state()
@@ -1,74 +0,0 @@
1
- weasyprint/__init__.py,sha256=MrV26630NkuBXozSOqGdsFZWOFI6arULa1nSj-7EYM8,17409
2
- weasyprint/__main__.py,sha256=WaRdNsuuTBCJlny8AgqF9I_0Mnx5GseQEFA5lXimErw,7114
3
- weasyprint/anchors.py,sha256=yXEZD0uFsCAdDjC07cHgWqRuGOSsOsmmmk4omZZecAo,6428
4
- weasyprint/document.py,sha256=pUwJKcUp3tmgxh155RsQpS3kVUMuXWZfXCOziqQwaNc,17131
5
- weasyprint/html.py,sha256=om7dvhx12ecunTaVclmWejkr5nPQF1BZmeH7jqQ_4GM,11590
6
- weasyprint/images.py,sha256=PI6xXN8z7vB79wK4RB9x3bLDwpPw7dQHZlzV4mLzqjM,35555
7
- weasyprint/logger.py,sha256=z1q548fX5shfAyLoMLeM9ozWGKgoBTKQsdlTtfRE_9U,1824
8
- weasyprint/matrix.py,sha256=v1BPtyn_-S_4TrAUgzOOR-viUXgdqsABKRndCEprkPc,1909
9
- weasyprint/stacking.py,sha256=oFB1-UHHeY8jO9nnS-IU5KK55azQ7V5QjEHYWGSqGW0,5628
10
- weasyprint/urls.py,sha256=yf6pXTic73l_nZiYkX03bfgFl2HdQlH0rCs4bSY5tjQ,9960
11
- weasyprint/css/__init__.py,sha256=6w2UoHQSoZ76eCuferHS3R0M8nAzFgab1NwR1vpy9LA,51599
12
- weasyprint/css/computed_values.py,sha256=O3USpx2ZH7Ff_FN5oIjn40lNMHj3zdThR0r51HRniIQ,27301
13
- weasyprint/css/counters.py,sha256=DHSrGJr2ktpZLCc-JYIiE67ak0TORsKSBKkjju-qwdE,11373
14
- weasyprint/css/html5_ph.css,sha256=l8t4ZN3KoevKx0UEfNw3_vgVjArcII6y5DZXZolWaw0,4629
15
- weasyprint/css/html5_ua.css,sha256=qGUxl1eIu3tQRVu2fVMnC_oo_CbMYoukAC8dSvH1ErM,19111
16
- weasyprint/css/html5_ua_form.css,sha256=0JLdrpmhokuy9IFhK2L1ZwbTa2G3ibXTXcGuCkAw80Y,288
17
- weasyprint/css/media_queries.py,sha256=wHPteZ9Gs2ttuA5kZpMDgRtFHnhYZVwrFXrhKgmR-4g,1072
18
- weasyprint/css/properties.py,sha256=5nywzyQjLOSKKGxsvJsVgoyQfMsLSQarMeso-WRgYlU,11593
19
- weasyprint/css/targets.py,sha256=5Ofw1RrmPsfQjDuZ1FCgktspGUai3wJmNa03MbT2sOI,8853
20
- weasyprint/css/utils.py,sha256=bRZYaMDRGtlHqyQouzZRAA-SfMep6_xGe-nnIbUUG_Y,25105
21
- weasyprint/css/validation/__init__.py,sha256=tuyJXGP6TbUdVi_KtxLj0nFa49vGF5KNZkXI8HxR_cw,8468
22
- weasyprint/css/validation/descriptors.py,sha256=VOLmaKZP76mTVwMCqMzVgfzP1JaI4zYWdEIFLNXCfYQ,11160
23
- weasyprint/css/validation/expanders.py,sha256=9SJiVhoK8HESwwmN2IqXj-bmaC9p4SSD15tqmfMITnE,39263
24
- weasyprint/css/validation/properties.py,sha256=1xoYf0rEs4dKZDeZT7Qh-Z3_GmrBJ1pQ8ZozEIJOgyE,66811
25
- weasyprint/draw/__init__.py,sha256=JGk_UCt045atrZPkLlUDNNlqeV7ngze487-v_wqwG0w,22914
26
- weasyprint/draw/border.py,sha256=MRG9E7Bi_QLGkSVEgEJ7D1QlFbs3atkATAAA2s9a6wY,27910
27
- weasyprint/draw/color.py,sha256=ZjqiMDSNIFOLsIz2qP30HrFMbVqeEmYAKxcU6tl4GBs,1449
28
- weasyprint/draw/stack.py,sha256=o9VB8GtAdLfbrzQpZ1SWiV_hneHAz64egURawBLYbMo,281
29
- weasyprint/draw/text.py,sha256=L_5VqeAzz2PpPCdBhl7Dfza08NteNU0SfbAwITEaroE,11419
30
- weasyprint/formatting_structure/boxes.py,sha256=U3HPSaM9mbzQSbZCo_ZQp8VAS48KVOsQaH0g08iBGpc,26279
31
- weasyprint/formatting_structure/build.py,sha256=c86cB4K8JL2oVMogFOWQn4V_GIhN1yEQL0z_wMqgPjo,56697
32
- weasyprint/layout/__init__.py,sha256=TNzbltiiuLVLLOlFDTLbwVlOoS7OnOzNPmHtWeaLk3M,16292
33
- weasyprint/layout/absolute.py,sha256=Caek_aFJDz5NLYFxTfEYO1-l7ITUMcx6TNDjNpE15-Y,13981
34
- weasyprint/layout/background.py,sha256=0ZRFZGAJnvF9eULIMyt8oy6lDqE-hKSbcyomQPx8HsQ,10008
35
- weasyprint/layout/block.py,sha256=jwQS2jGCoQ0hSvCsWRPFK7Y3wad-Ucgzv0fzf-4DKm0,45388
36
- weasyprint/layout/column.py,sha256=P_863u0_vFYzig9tfBUMI8ZCpXlbx9SLoYekbZ5vg5Y,17299
37
- weasyprint/layout/flex.py,sha256=uAm4DAOvomSg0aX1oEse8-vT3M_fVI-IGFO1yatBH6E,42630
38
- weasyprint/layout/float.py,sha256=xo9CuXfvrC5jE8rnCVT2ShNRAXl7vAISq41oqkvvbK8,9266
39
- weasyprint/layout/grid.py,sha256=hzmsWXWQYw5rdy2XA_O_4Hz7CWCVle0RnnWTkugPEvY,54729
40
- weasyprint/layout/inline.py,sha256=aFxHM0b-fQCSIWyXgf3dLHM90sQq8AOcS1ELx7sDpbc,48217
41
- weasyprint/layout/leader.py,sha256=wklI0aLyTx0VJhqU7D_FxtJpfe7dXswcN-VApAusM-Q,2825
42
- weasyprint/layout/min_max.py,sha256=JdXJG9ISO_RsfeHua_-3g477a16I-NrnYuwH_tQwq4o,1527
43
- weasyprint/layout/page.py,sha256=JLKoYzGmrULONv11KLLrQuRFaTRK7XJZocuUsB0EGKs,39908
44
- weasyprint/layout/percent.py,sha256=2XzT_Y-fu7OVt2Ut1f9T1Tt9S4ftRr4x7wL3agvEJus,5626
45
- weasyprint/layout/preferred.py,sha256=cSOraU8g7i_8dDUifJhvKk0gxHBdPXqGZjjIgs8LVIs,31040
46
- weasyprint/layout/replaced.py,sha256=ucAd6VMKIEryjnwK8ciKbUoE2yK29-ggdYlGW3KPDXk,11178
47
- weasyprint/layout/table.py,sha256=qomzhxW0m4WaZ4Vis4GW7bfNQ6JP7yRSt7aze-dNcbU,47431
48
- weasyprint/pdf/__init__.py,sha256=pEehJ-JDH0ZepUFtj3Fz75BvVGBf7psduIbnph08PRo,12022
49
- weasyprint/pdf/anchors.py,sha256=Slgq3_IBiIBg3J7zzHMajISBb7ph2AVezIs-lW_tB9I,17386
50
- weasyprint/pdf/debug.py,sha256=reLw6U6hK94FOVNYW8psdt_SFN11iIe1rhYkr6sURF4,1407
51
- weasyprint/pdf/fonts.py,sha256=D_T0JWFnofSDqdU29noTg8IbyenN9SlaULxSTPlg1JQ,24777
52
- weasyprint/pdf/metadata.py,sha256=r5ATj8Lv_6Ib-RbA2zgazyo6yJwF-LoqoNAWUchV4qE,4168
53
- weasyprint/pdf/pdfa.py,sha256=97J7nlKHmP5vdSBz0X0cDnGnqAPZ5qqoQ7ArZsdWRT8,3626
54
- weasyprint/pdf/pdfua.py,sha256=mCgXdr1RUe6hffg6Jsu4LzmDtBNyJTtj32wTouBeJy4,5236
55
- weasyprint/pdf/sRGB2014.icc,sha256=OEuDLeNBIGZ0O1KnXukGtvufuNngnpNvwsQyI4Fcbgo,3024
56
- weasyprint/pdf/stream.py,sha256=-s_ZT2xDsKzhjj9cgMnf4OkvVp88EnKBwzAvxKwOwWs,11301
57
- weasyprint/svg/__init__.py,sha256=smY5L9f68vf3BTZL1KTB0fz5JcwFM0kxFgywgRAOxdE,30012
58
- weasyprint/svg/bounding_box.py,sha256=ti9BZ8Pxr4K_RZr41vrcDeBu1Mz9CReh8ECbUzzh_0s,12999
59
- weasyprint/svg/css.py,sha256=AUsIim2rOmRGLgFuiWm4EzXwnrRlThczfM17Uq2MRUg,3832
60
- weasyprint/svg/defs.py,sha256=u_VQ-b6045qCBuLNEe9lwJWHcOsYYO4wBwsSWiXEZm0,20978
61
- weasyprint/svg/images.py,sha256=3A3pulL4cDPQ22uz0QqyQ78qcRIp_sEyAAIxjyj62d0,3059
62
- weasyprint/svg/path.py,sha256=Z-T6kbUU3pyHhzVV0JSBgO--XaCGXLsH-cS9iAsITMM,10064
63
- weasyprint/svg/shapes.py,sha256=NDo0KMnwrm0hj3BOmfrKjRZo4iJF9o-MeUhZ5avANco,3845
64
- weasyprint/svg/text.py,sha256=JVrLSpDtU3P9IgkG50g_lQVi0L5uNbXoEDh6tk3o2z4,6404
65
- weasyprint/svg/utils.py,sha256=RkmPhTAqBfn85YACP3u9m4Rs8hyqZyQLBLME1q8psyw,6969
66
- weasyprint/text/constants.py,sha256=ifPeTG_us_sSgWuM-WTQgDrrAgwwnohYR63HhS_1dIM,14191
67
- weasyprint/text/ffi.py,sha256=0FWxNeYn0Nub-fKHWElFcQc9GmlgiLAKvS82hpDxsAs,18282
68
- weasyprint/text/fonts.py,sha256=Qa7HxrQeJN84XXBKgJitcRdVMRA9vz1QY8Kl3StRCE4,17460
69
- weasyprint/text/line_break.py,sha256=FXlHDk8A4lBB-i3wqgl7RO66P3TPpFzkkFbmu-FHGKg,24735
70
- weasyprint-65.1.dist-info/entry_points.txt,sha256=wgDp3XXzFywdYgI5vUWMp1zAwx1sZXXH0FTUQbFOq6A,55
71
- weasyprint-65.1.dist-info/licenses/LICENSE,sha256=v9FOzPphAFdUYOaFVWsYM5nUvTNZBOPJUhsBFtIcVNo,1534
72
- weasyprint-65.1.dist-info/WHEEL,sha256=G2gURzTEtmeR8nrdXUJfNiB3VYVxigPQ-bEQujpNiNs,82
73
- weasyprint-65.1.dist-info/METADATA,sha256=v9-RDucK9Kk_K1t1UO7i1KRMr7GRF-bbdCdt9XGn-qg,3707
74
- weasyprint-65.1.dist-info/RECORD,,