weasyprint 67.0__py3-none-any.whl → 68.1__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/urls.py CHANGED
@@ -6,11 +6,14 @@ import os.path
6
6
  import re
7
7
  import sys
8
8
  import traceback
9
+ import warnings
9
10
  import zlib
11
+ from email.message import EmailMessage
10
12
  from gzip import GzipFile
13
+ from io import BytesIO, StringIO
11
14
  from pathlib import Path
15
+ from urllib import request
12
16
  from urllib.parse import quote, unquote, urljoin, urlsplit
13
- from urllib.request import Request, pathname2url, url2pathname, urlopen
14
17
 
15
18
  from . import __version__
16
19
  from .logger import LOGGER
@@ -55,8 +58,7 @@ def iri_to_uri(url):
55
58
  # Data URIs can be huge, but don’t need this anyway.
56
59
  return url
57
60
  # Use UTF-8 as per RFC 3987 (IRI), except for file://
58
- url = url.encode(
59
- FILESYSTEM_ENCODING if url.startswith('file:') else 'utf-8')
61
+ url = url.encode(FILESYSTEM_ENCODING if url.startswith('file:') else 'utf-8')
60
62
  # This is a full URI, not just a component. Only %-encode characters
61
63
  # that are not allowed at all in URIs. Everthing else is "safe":
62
64
  # * Reserved characters: /:?#[]@!$&'()*+,;=
@@ -85,7 +87,7 @@ def path2url(path):
85
87
  # Otherwise relative URIs are resolved from the parent directory.
86
88
  path += os.path.sep
87
89
  wants_trailing_slash = True
88
- path = pathname2url(path)
90
+ path = request.pathname2url(path)
89
91
  # On Windows pathname2url cuts off trailing slash
90
92
  if wants_trailing_slash and not path.endswith('/'):
91
93
  path += '/' # pragma: no cover
@@ -191,114 +193,305 @@ def default_url_fetcher(url, timeout=10, ssl_context=None, http_headers=None,
191
193
  allowed_protocols=None):
192
194
  """Fetch an external resource such as an image or stylesheet.
193
195
 
194
- Another callable with the same signature can be given as the
195
- ``url_fetcher`` argument to :class:`HTML` or :class:`CSS`.
196
- (See :ref:`URL Fetchers`.)
197
-
198
- :param str url:
199
- The URL of the resource to fetch.
200
- :param int timeout:
201
- The number of seconds before HTTP requests are dropped.
202
- :param ssl.SSLContext ssl_context:
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.
208
- :raises: An exception indicating failure, e.g. :obj:`ValueError` on
209
- syntactically invalid URL.
210
- :returns: A :obj:`dict` with the following keys:
211
-
212
- * One of ``string`` (a :obj:`bytestring <bytes>`) or ``file_obj``
213
- (a :term:`file object`).
214
- * Optionally: ``mime_type``, a MIME type extracted e.g. from a
215
- *Content-Type* header. If not provided, the type is guessed from the
216
- file extension in the URL.
217
- * Optionally: ``encoding``, a character encoding extracted e.g. from a
218
- *charset* parameter in a *Content-Type* header
219
- * Optionally: ``redirected_url``, the actual URL of the resource
220
- if there were e.g. HTTP redirects.
221
- * Optionally: ``filename``, the filename of the resource. Usually
222
- derived from the *filename* parameter in a *Content-Disposition*
223
- header.
224
- * Optionally: ``path``, the path of the resource if it is stored on the
225
- local filesystem.
226
-
227
- If a ``file_obj`` key is given, it is the caller’s responsibility
228
- to call ``file_obj.close()``. The default function used internally to
229
- fetch data in WeasyPrint tries to close the file object after
230
- retreiving; but if this URL fetcher is used elsewhere, the file object
231
- has to be closed manually.
196
+ This function is deprecated, use ``URLFetcher`` instead.
232
197
 
233
198
  """
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:
199
+ warnings.warn(
200
+ "default_url_fetcher is deprecated and will be removed in WeasyPrint 69.0, "
201
+ "please use URLFetcher instead. For security reasons, HTTP redirects are not "
202
+ "supported anymore with default_url_fetcher, but are with URLFetcher.\n\nSee "
203
+ "https://doc.courtbouillon.org/weasyprint/stable/first_steps.html#url-fetchers",
204
+ category=DeprecationWarning)
205
+ fetcher = URLFetcher(
206
+ timeout, ssl_context, http_headers, allowed_protocols, allow_redirects=False)
207
+ return fetcher.fetch(url)
208
+
209
+
210
+ @contextlib.contextmanager
211
+ def select_source(guess=None, filename=None, url=None, file_obj=None, string=None,
212
+ base_url=None, url_fetcher=None, check_css_mime_type=False):
213
+ """If only one input is given, return it.
214
+
215
+ Yield a file object, the base url, the protocol encoding and the protocol mime-type.
216
+
217
+ """
218
+ if base_url is not None:
219
+ base_url = ensure_url(base_url)
220
+ if url_fetcher is None:
221
+ url_fetcher = URLFetcher()
222
+
223
+ selected_params = [
224
+ param for param in (guess, filename, url, file_obj, string) if
225
+ param is not None]
226
+ if len(selected_params) != 1:
227
+ source = ', '.join(selected_params) or 'nothing'
228
+ raise TypeError(f'Expected exactly one source, got {source}')
229
+ elif guess is not None:
230
+ kwargs = {
231
+ 'base_url': base_url,
232
+ 'url_fetcher': url_fetcher,
233
+ 'check_css_mime_type': check_css_mime_type,
234
+ }
235
+ if hasattr(guess, 'read'):
236
+ kwargs['file_obj'] = guess
237
+ elif isinstance(guess, Path):
238
+ kwargs['filename'] = guess
239
+ elif url_is_absolute(guess):
240
+ kwargs['url'] = guess
241
+ else:
242
+ kwargs['filename'] = guess
243
+ result = select_source(**kwargs)
244
+ with result as result:
245
+ yield result
246
+ elif filename is not None:
247
+ if base_url is None:
248
+ base_url = path2url(filename)
249
+ with open(filename, 'rb') as file_obj:
250
+ yield file_obj, base_url, None, None
251
+ elif url is not None:
252
+ with fetch(url_fetcher, url) as response:
253
+ if check_css_mime_type and response.content_type != 'text/css':
254
+ LOGGER.error(
255
+ f'Unsupported stylesheet type {response.content_type} '
256
+ f'for {response.url}')
257
+ yield StringIO(''), base_url, None, None
258
+ else:
259
+ if base_url is None:
260
+ base_url = response.url
261
+ yield response, base_url, response.charset, response.content_type
262
+ elif file_obj is not None:
263
+ if base_url is None:
264
+ # filesystem file-like objects have a 'name' attribute.
265
+ name = getattr(file_obj, 'name', None)
266
+ # Some streams have a .name like '<stdin>', not a filename.
267
+ if name and not name.startswith('<'):
268
+ base_url = ensure_url(name)
269
+ yield file_obj, base_url, None, None
270
+ else:
271
+ if isinstance(string, str):
272
+ yield StringIO(string), base_url, None, None
273
+ else:
274
+ yield BytesIO(string), base_url, None, None
275
+
276
+
277
+ class URLFetchingError(IOError):
278
+ """Some error happened when fetching an URL."""
279
+
280
+
281
+ class FatalURLFetchingError(BaseException):
282
+ """Some error happened when fetching an URL and must stop the rendering."""
283
+
284
+
285
+ class URLFetcher(request.OpenerDirector):
286
+ """Fetcher of external resources such as images or stylesheets.
287
+
288
+ :param int timeout: The number of seconds before HTTP requests are dropped.
289
+ :param ssl.SSLContext ssl_context: An SSL context used for HTTPS requests.
290
+ :param dict http_headers: Additional HTTP headers used for HTTP requests.
291
+ :type allowed_protocols: :term:`sequence`
292
+ :param allowed_protocols: A set of authorized protocols, :obj:`None` means all.
293
+ :param bool allow_redirects: Whether HTTP redirects must be followed.
294
+ :param bool fail_on_errors: Whether HTTP errors should stop the rendering.
295
+
296
+ Another class inheriting from this class, with a ``fetch`` method that has a
297
+ compatible signature, can be given as the ``url_fetcher`` argument to
298
+ :class:`weasyprint.HTML` or :class:`weasyprint.CSS`.
299
+
300
+ See :ref:`URL Fetchers` for more information and examples.
301
+
302
+ """
303
+
304
+ def __init__(self, timeout=10, ssl_context=None, http_headers=None,
305
+ allowed_protocols=None, allow_redirects=True, fail_on_errors=False,
306
+ **kwargs):
307
+ super().__init__()
308
+ handlers = [
309
+ request.ProxyHandler(), request.UnknownHandler(), request.HTTPHandler(),
310
+ request.HTTPDefaultErrorHandler(), request.FTPHandler(),
311
+ request.FileHandler(), request.HTTPErrorProcessor(), request.DataHandler(),
312
+ request.HTTPSHandler(context=ssl_context)]
313
+ if allow_redirects:
314
+ handlers.append(request.HTTPRedirectHandler())
315
+ for handler in handlers:
316
+ self.add_handler(handler)
317
+
318
+ self._timeout = timeout
319
+ self._http_headers = {**HTTP_HEADERS, **(http_headers or {})}
320
+ self._allowed_protocols = allowed_protocols
321
+ self._fail_on_errors = fail_on_errors
322
+
323
+ def fetch(self, url, headers=None):
324
+ """Fetch a given URL.
325
+
326
+ :returns: A :obj:`URLFetcherResponse` instance.
327
+ :raises: An exception indicating failure, e.g. :obj:`ValueError` on
328
+ syntactically invalid URL. All exceptions are catched internally by
329
+ WeasyPrint, except when they inherit from :obj:`FatalURLFetchingError`.
330
+
331
+ """
332
+ # Discard URLs with no or invalid protocol.
333
+ if not (match := UNICODE_SCHEME_RE.match(url)): # pragma: no cover
334
+ raise ValueError(f'Not an absolute URI: {url}')
335
+ scheme = match[1].lower()
336
+
337
+ # Discard URLs with forbidden protocol.
338
+ if self._allowed_protocols is not None:
339
+ if scheme not in self._allowed_protocols:
237
340
  raise ValueError(f'URI uses disallowed protocol: {url}')
238
341
 
239
- # See https://bugs.python.org/issue34702
240
- if url.lower().startswith('file://'):
342
+ # Remove query and fragment parts from file URLs.
343
+ # See https://bugs.python.org/issue34702.
344
+ if scheme == 'file':
241
345
  url = url.split('?')[0]
242
- path = url2pathname(url.removeprefix('file:'))
243
- else:
244
- path = None
245
346
 
347
+ # Transform Unicode IRI to ASCII URI.
246
348
  url = iri_to_uri(url)
247
- if http_headers is not None:
248
- http_headers = {**HTTP_HEADERS, **http_headers}
349
+
350
+ # Open URL.
351
+ headers = {**self._http_headers, **(headers or {})}
352
+ http_request = request.Request(url, headers=headers)
353
+ response = super().open(http_request, timeout=self._timeout)
354
+
355
+ # Decompress response.
356
+ body = response
357
+ if 'Content-Encoding' in response.headers:
358
+ content_encoding = response.headers['Content-Encoding']
359
+ del response.headers['Content-Encoding']
360
+ if content_encoding == 'gzip':
361
+ body = StreamingGzipFile(fileobj=response)
362
+ elif content_encoding == 'deflate':
363
+ data = response.read()
364
+ try:
365
+ body = zlib.decompress(data)
366
+ except zlib.error:
367
+ # Try without zlib header or checksum.
368
+ body = zlib.decompress(data, -15)
369
+
370
+ return URLFetcherResponse(response.url, body, response.headers, response.status)
371
+
372
+ def open(self, url, data=None, timeout=None):
373
+ if isinstance(url, request.Request):
374
+ return self.fetch(url.full_url, url.headers)
375
+ return self.fetch(url)
376
+
377
+ def __call__(self, url):
378
+ return self.fetch(url)
379
+
380
+
381
+ class URLFetcherResponse:
382
+ """The HTTP response of an URL fetcher.
383
+
384
+ :param str url: The URL of the HTTP response.
385
+ :type body: :class:`str`, :class:`bytes` or :term:`file object`
386
+ :param body: The body of the HTTP response.
387
+ :type headers: dict or email.message.EmailMessage
388
+ :param headers: The headers of the HTTP response.
389
+ :param int status: The status of the HTTP response.
390
+
391
+ Has the same interface as :class:`urllib.response.addinfourl`.
392
+
393
+ If a :term:`file object` is given for the body, it is the caller’s responsibility to
394
+ call ``close()`` on it. The default function used internally to fetch data in
395
+ WeasyPrint tries to close the file object after retreiving; but if this URL fetcher
396
+ is used elsewhere, the file object has to be closed manually.
397
+
398
+ """
399
+ def __init__(self, url, body=None, headers=None, status=200, **kwargs):
400
+ self.url = url
401
+ self.status = status
402
+
403
+ if isinstance(headers, EmailMessage):
404
+ self.headers = headers
249
405
  else:
250
- http_headers = HTTP_HEADERS
251
- response = urlopen(
252
- Request(url, headers=http_headers), timeout=timeout,
253
- context=ssl_context)
254
- result = {
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,
260
- }
261
- content_encoding = response.headers.get('Content-Encoding')
262
- if content_encoding == 'gzip':
263
- result['file_obj'] = StreamingGzipFile(fileobj=response)
264
- elif content_encoding == 'deflate':
265
- data = response.read()
266
- try:
267
- result['string'] = zlib.decompress(data)
268
- except zlib.error:
269
- # Try without zlib header or checksum
270
- result['string'] = zlib.decompress(data, -15)
406
+ self.headers = EmailMessage()
407
+ for key, value in (headers or {}).items():
408
+ try:
409
+ self.headers[key] = value
410
+ except ValueError:
411
+ pass # Ignore forbidden duplicated headers.
412
+
413
+ if hasattr(body, 'read'):
414
+ self._file_obj = body
415
+ elif isinstance(body, str):
416
+ self.headers.set_param('charset', 'utf-8')
417
+ self._file_obj = BytesIO(body.encode('utf-8'))
271
418
  else:
272
- result['file_obj'] = response
273
- return result
274
- else: # pragma: no cover
275
- raise ValueError(f'Not an absolute URI: {url}')
419
+ self._file_obj = BytesIO(body)
276
420
 
421
+ def read(self, *args, **kwargs):
422
+ return self._file_obj.read(*args, **kwargs)
277
423
 
278
- class URLFetchingError(IOError):
279
- """Some error happened when fetching an URL."""
424
+ def close(self):
425
+ try:
426
+ self._file_obj.close()
427
+ except Exception: # pragma: no cover
428
+ # May already be closed or something.
429
+ # This is just cleanup anyway: log but make it non-fatal.
430
+ LOGGER.warning(
431
+ 'Error when closing stream for %s:\n%s',
432
+ self.url, traceback.format_exc())
433
+
434
+ @property
435
+ def path(self):
436
+ if self.url.startswith('file:'):
437
+ return request.url2pathname(self.url.split('?')[0].removeprefix('file:'))
438
+
439
+ @property
440
+ def content_type(self):
441
+ return self.headers.get_content_type()
442
+
443
+ @property
444
+ def charset(self):
445
+ return self.headers.get_param('charset')
446
+
447
+ def geturl(self):
448
+ return self.url
449
+
450
+ def info(self):
451
+ return self.headers
452
+
453
+ @property
454
+ def code(self):
455
+ return self.status
456
+
457
+ def getcode(self):
458
+ return self.status
280
459
 
281
460
 
282
461
  @contextlib.contextmanager
283
462
  def fetch(url_fetcher, url):
284
- """Call an url_fetcher, fill in optional data, and clean up."""
463
+ """Fetch an ``url`` with ```url_fetcher``, fill in optional data, and clean up.
464
+
465
+ Fatal errors must raise a ``FatalURLFetchingError`` that stops the rendering. All
466
+ other exceptions are catched and raise an ``URLFetchingError``, that is usually
467
+ catched by the code that fetches the resource and emits a warning.
468
+
469
+ """
285
470
  try:
286
- result = url_fetcher(url)
471
+ resource = url_fetcher(url)
287
472
  except Exception as exception:
473
+ if getattr(url_fetcher, '_fail_on_errors', False):
474
+ raise FatalURLFetchingError(f'Error fetching "{url}"') from exception
288
475
  raise URLFetchingError(f'{type(exception).__name__}: {exception}')
289
- result.setdefault('redirected_url', url)
290
- result.setdefault('mime_type', None)
291
- if 'file_obj' in result:
292
- try:
293
- yield result
294
- finally:
295
- try:
296
- result['file_obj'].close()
297
- except Exception: # pragma: no cover
298
- # May already be closed or something.
299
- # This is just cleanup anyway: log but make it non-fatal.
300
- LOGGER.warning(
301
- 'Error when closing stream for %s:\n%s',
302
- url, traceback.format_exc())
303
- else:
304
- yield result
476
+
477
+ if isinstance(resource, dict):
478
+ warnings.warn(
479
+ "Returning dicts in URL fetchers is deprecated and will be removed "
480
+ "in WeasyPrint 69.0, please return URLFetcherResponse instead.",
481
+ category=DeprecationWarning)
482
+ if 'url' not in resource:
483
+ resource['url'] = resource.get('redirected_url', url)
484
+ resource['body'] = resource.get('file_obj', resource.get('string'))
485
+ content_type = resource.get('mime_type', 'application/octet-stream')
486
+ if charset := resource.get('encoding'):
487
+ content_type += f'; charset={charset}'
488
+ resource['headers'] = {'Content-Type': content_type}
489
+ resource = URLFetcherResponse(**resource)
490
+
491
+ assert isinstance(resource, URLFetcherResponse), (
492
+ 'URL fetcher must return either a dict or a URLFetcherResponse instance')
493
+
494
+ try:
495
+ yield resource
496
+ finally:
497
+ resource.close()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: weasyprint
3
- Version: 67.0
3
+ Version: 68.1
4
4
  Summary: The Awesome Document Factory
5
5
  Keywords: html,css,pdf,converter
6
6
  Author-email: Simon Sapin <simon.sapin@exyr.org>
@@ -37,6 +37,7 @@ Requires-Dist: sphinx ; extra == "doc"
37
37
  Requires-Dist: furo ; extra == "doc"
38
38
  Requires-Dist: pytest ; extra == "test"
39
39
  Requires-Dist: ruff ; extra == "test"
40
+ Requires-Dist: Pillow >=12.1.0 ; extra == "test"
40
41
  Project-URL: Changelog, https://github.com/Kozea/WeasyPrint/releases
41
42
  Project-URL: Code, https://github.com/Kozea/WeasyPrint
42
43
  Project-URL: Documentation, https://doc.courtbouillon.org/weasyprint/
@@ -1,77 +1,77 @@
1
- weasyprint/__init__.py,sha256=n_NTbjsAu2kFJgcsSMiEd0R22wSLE6fQwoWQJ4SbxMY,17861
2
- weasyprint/__main__.py,sha256=7K-DxthsEP_bMFjOx6BfS52b2ZwkgmFXaQR-lNQvjOc,7828
1
+ weasyprint/__init__.py,sha256=Z8RQi3arBn0J5Wlpm56inGnfXYuen9NRT2WyNqxS5q8,14949
2
+ weasyprint/__main__.py,sha256=L6pqmXo68pHGPkXth8obc9DlUHYdc3YXwDG5bCky4Pc,9374
3
3
  weasyprint/anchors.py,sha256=fytc05l3TO4AWuyPJx532RBEbZe8L7UlnbYYJpB-bGA,6472
4
- weasyprint/document.py,sha256=VV3QDVnAsFfBbLZsrEdAdrz28CW-tJ90jk-l7U0HYOo,17108
4
+ weasyprint/document.py,sha256=W_pmP05WT_bucoU7eTwkYuYMGG6h-19ZpdVZZcbxpAc,13681
5
5
  weasyprint/html.py,sha256=om7dvhx12ecunTaVclmWejkr5nPQF1BZmeH7jqQ_4GM,11590
6
- weasyprint/images.py,sha256=2hb1oagE5mes9HXtjbGlvVXtbmYWClnA26FvJnk3UgQ,36393
6
+ weasyprint/images.py,sha256=nh1RXq7-8M2dzuOX7gLRwmHDm7LVwulTGcgOsgB_Fr8,36372
7
7
  weasyprint/logger.py,sha256=z1q548fX5shfAyLoMLeM9ozWGKgoBTKQsdlTtfRE_9U,1824
8
8
  weasyprint/matrix.py,sha256=v1BPtyn_-S_4TrAUgzOOR-viUXgdqsABKRndCEprkPc,1909
9
9
  weasyprint/stacking.py,sha256=6c6eZ_BxtcYvlEbH6JQdqaKwYBkuaqUwGGSs3OfkpS8,5697
10
- weasyprint/urls.py,sha256=eGYReMlXG7vHXPts1xXscszdoJOSQRpV18H7g4NmtKY,11055
11
- weasyprint/css/__init__.py,sha256=Rw24jzwWQNidyuy_VHLU3YFuthZ1VNwsnoM8zwr4XAQ,77767
10
+ weasyprint/urls.py,sha256=6ZsVvV5TKHH1lyXvAkeV0qIgMavWKWKVjLvS7aMagRE,18287
11
+ weasyprint/css/__init__.py,sha256=rCwc157BawGI6j1z_ymYmjT-_-0jEUDVw1DYdJPW9OM,77514
12
12
  weasyprint/css/computed_values.py,sha256=WPySY5DFXusCxa7wC5mUv4uzwBi2c7ACMLI3_iHqDnI,24187
13
13
  weasyprint/css/counters.py,sha256=RAUuZGOLZj5oKrZexbkDUYPncTFWzwtT870xTKYkQmI,11374
14
- weasyprint/css/functions.py,sha256=IabFkGPcrWOsAmIIY_wkn1EC7Hpwgx0I0VNlpCJIAsE,6562
14
+ weasyprint/css/functions.py,sha256=PXPZX0EJKI1FL_ydPGLueICMTHPxwiZwQr74vWpEaMY,6779
15
15
  weasyprint/css/html5_ph.css,sha256=l8t4ZN3KoevKx0UEfNw3_vgVjArcII6y5DZXZolWaw0,4629
16
- weasyprint/css/html5_ua.css,sha256=mPHfk2DltgEiIAZr9G1b3kxcr2PHnLbtBkfQSGJIQj8,18359
16
+ weasyprint/css/html5_ua.css,sha256=0HGk0se0ej4-hEVK6u4E2JVW6u-x0ZE7K3gb0TgozmY,18399
17
17
  weasyprint/css/html5_ua_form.css,sha256=O0L78oQN8ZuQcXlfe7XVTy5kM3nVF6-rfO3xALbYTy8,310
18
18
  weasyprint/css/media_queries.py,sha256=Mh1JlSupMNjVJ4CKW3Yd7zR4W6NGNXp21y_4hajDDUY,1179
19
19
  weasyprint/css/properties.py,sha256=64TpsS87--Xj_al7x0vREkbCDpyYQqHJx9H7vYxjKjY,11727
20
20
  weasyprint/css/targets.py,sha256=5Ofw1RrmPsfQjDuZ1FCgktspGUai3wJmNa03MbT2sOI,8853
21
- weasyprint/css/tokens.py,sha256=TsDJIpUkUvTbd6jqqEhvn0momX5nLdSkLTvQGRW5iBA,23403
21
+ weasyprint/css/tokens.py,sha256=r1A3lUA0xEQ8KnqILdhi_vCOZv_sQ-Y9ZdQRZcPH-JM,23546
22
22
  weasyprint/css/units.py,sha256=loUL8k7y4OYxbNrowbUgsRuqKj-uFKfhZmKUjc6t3GQ,2901
23
23
  weasyprint/css/validation/__init__.py,sha256=bt0Rqn5Jt2KLr6jFXau8QnosNIkz83DxSlca1t064hU,8469
24
24
  weasyprint/css/validation/descriptors.py,sha256=7LN_0ed9wZWgYT3_4UrEE-R6mzpINGs-VM9lglV49P8,11839
25
25
  weasyprint/css/validation/expanders.py,sha256=hqVJKiK5G-C8oNsCMTJ_X964qFip2j5hO4r912dnJX8,39233
26
- weasyprint/css/validation/properties.py,sha256=EHwBGdXp3Qh6gb1nRlXrr6nwIWiC9eSw_UUSl5C2kcs,66824
26
+ weasyprint/css/validation/properties.py,sha256=DYjW8TZl_y_cedVkleY6kTVw6G3nUlDspE0QMjqOsU4,66866
27
27
  weasyprint/draw/__init__.py,sha256=l4q_L6k93tIobB8hHui_PSElstO5MI5FTHCBTISb_ew,22715
28
28
  weasyprint/draw/border.py,sha256=5s-FZyw3MN085cmmAJHp8Yms5gM90j-XzInpwldgPFs,30373
29
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
30
+ weasyprint/draw/text.py,sha256=vHzgHNGiTB8TGJw1GOOAVCzj4ZOnREQ2enbRJL0kVzM,12444
31
+ weasyprint/formatting_structure/boxes.py,sha256=QDX3FRmSY7pG6QjWTuhANlSMrePNRPm1iX5-CJ8tU98,27199
32
+ weasyprint/formatting_structure/build.py,sha256=j_pKsHxsNt-ErNj-TT12QczQa6RszPOWjDztqzH5yMo,58491
33
+ weasyprint/layout/__init__.py,sha256=mUlsoqBIamjdIqtINS9W0njkrgn8guyYORu3UCedon0,16767
34
34
  weasyprint/layout/absolute.py,sha256=JA4mjOweZt2h-Hrsa91nHnvzZDnF-gneMpZfcXiTugc,14005
35
35
  weasyprint/layout/background.py,sha256=IfcmSZ-E4_NES-RSeKi5DGbmrloGdu9zVz5zZ87m9gU,10023
36
- weasyprint/layout/block.py,sha256=czlH2egB6a92UeZGrdggiWOXPj50K5IR9UzJ7-y96GY,48691
36
+ weasyprint/layout/block.py,sha256=I5E7-aTPbkEqIHLcm8PJNXCfq32XbDmu6a9jv8czUBA,48990
37
37
  weasyprint/layout/column.py,sha256=g64aPoNZYpPlrma4F1nyrW7_ji5DBYoamA92YrbFHZM,17209
38
38
  weasyprint/layout/flex.py,sha256=KZNFElr2QDT5FYI6JlZGpXxJHYFvMMN8QGylLx98PUs,44148
39
39
  weasyprint/layout/float.py,sha256=5iBDHg1KSexArW1SfbnMShuILzkkEH8DW7n1bdk5OU4,9488
40
- weasyprint/layout/grid.py,sha256=HTJwzvJo38yrAzuxlYE9ioA5n2V5_jp1ZEO5vu877_c,63931
40
+ weasyprint/layout/grid.py,sha256=fqSt4_71FblPJxwG5irqvm3CKqbp1brNJWYNJ2WujJs,64522
41
41
  weasyprint/layout/inline.py,sha256=Iw3h9_N6a0rly9VGZ35LD0Pp8RDDGK7cYVKjZy17BZk,50447
42
42
  weasyprint/layout/leader.py,sha256=wklI0aLyTx0VJhqU7D_FxtJpfe7dXswcN-VApAusM-Q,2825
43
43
  weasyprint/layout/min_max.py,sha256=JdXJG9ISO_RsfeHua_-3g477a16I-NrnYuwH_tQwq4o,1527
44
- weasyprint/layout/page.py,sha256=lMhopv3aZgJTfbyymg6zuMIpfPDxJUpPBxsZaXwzIl8,41566
44
+ weasyprint/layout/page.py,sha256=m4z24NqI6bc08j1GwXsRCpyMkKxC93XE6--QMHUaKxM,41565
45
45
  weasyprint/layout/percent.py,sha256=F2kNcNz7b6to70GwiFFu-oWWpYs011TM021COH7feEc,5829
46
- weasyprint/layout/preferred.py,sha256=PPLWMQYyL3f8rNnkQtw9PAJjca7eFJUksgPd_iVGjWU,33206
46
+ weasyprint/layout/preferred.py,sha256=ZZAc7tbn5Rdl9DjgXgnT4J9sBBljNXvY5qj47HjUXGM,35288
47
47
  weasyprint/layout/replaced.py,sha256=7-foaAPIAVC329QEnUN_1u2U6VTrEbQ0Qx254BlrLCo,11357
48
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
49
+ weasyprint/pdf/__init__.py,sha256=5OG1Tgdyk1XPhcren50OY2S9ZLT4SseIajlEhDl7PSw,14303
50
+ weasyprint/pdf/anchors.py,sha256=IG7mimMqf0j2lurfX5DF3UkUZ4HdMiTMqpupue6BEvI,17164
51
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
52
+ weasyprint/pdf/fonts.py,sha256=UMGV2P4-y3mh1p3YFzVaoKekdx2B5nExsZvIBZg9kTM,28159
53
+ weasyprint/pdf/metadata.py,sha256=enBUUChb5vTHXZH7B2v9Cl_WO7hXL0TrS3XcfSv4x9M,9207
54
+ weasyprint/pdf/pdfa.py,sha256=JDKtJ3-v-i2CMWScTbesZi3YQ8C-5PYOheYAYM5Y1iw,4239
55
+ weasyprint/pdf/pdfua.py,sha256=RrV7qky5Djc_tP5ipdDUDC5BqOi6T_LDZpVy8cyXRGc,486
56
+ weasyprint/pdf/pdfx.py,sha256=1DB9afper1oZpmoktSxBvRTpUuxHj7TBy3dhXdD2qGc,2629
57
57
  weasyprint/pdf/sRGB2014.icc,sha256=OEuDLeNBIGZ0O1KnXukGtvufuNngnpNvwsQyI4Fcbgo,3024
58
58
  weasyprint/pdf/stream.py,sha256=7L7EcGhxwDp06VSqgVWVP2ZgpIdJ35LPLE6IAN4sQzQ,11615
59
59
  weasyprint/pdf/tags.py,sha256=914AozTYgFOMpFSP2UtEZCVzsPIKueHWjI9DA77w28M,11789
60
- weasyprint/svg/__init__.py,sha256=6mGg2E1uolusHrc7ep4c9Ci3pnw8ECpEWNjNHZw0Ylk,31497
60
+ weasyprint/svg/__init__.py,sha256=96DOr-LUCMiEOL81QbgpnPL96Bw9ItPDMb9AUvxygJU,32181
61
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
62
+ weasyprint/svg/css.py,sha256=MohmaDCX6oDIIxJPrn6bn1fKq4la0oPfxvMYrfL5vbI,4871
63
+ weasyprint/svg/defs.py,sha256=kkuNKYp9ymb1hK-w45_ClV9iGWULyEGmXhazmAUnXF8,20774
64
64
  weasyprint/svg/images.py,sha256=msVOn7_DgKFVB6Pz25SDLXU22-p7H5y0fJGKBpFDjrc,3333
65
65
  weasyprint/svg/path.py,sha256=Z-T6kbUU3pyHhzVV0JSBgO--XaCGXLsH-cS9iAsITMM,10064
66
66
  weasyprint/svg/shapes.py,sha256=NDo0KMnwrm0hj3BOmfrKjRZo4iJF9o-MeUhZ5avANco,3845
67
- weasyprint/svg/text.py,sha256=vJ2FUquzpnGclA_y1dXu4jImvkGR0NvvnyoVLyHZlk0,6663
67
+ weasyprint/svg/text.py,sha256=RST3ss4HEGxrq87lX6z67T9K55nL5qBntz0dDqH0bfs,6772
68
68
  weasyprint/svg/utils.py,sha256=BEJvyOtxo4tAgLL-RORaEMIBAgmIZzwBNp2YuN1u3NM,7289
69
69
  weasyprint/text/constants.py,sha256=gtC92Hbzci0976gVTct3roVKLcQjNWIQM43zuEBqIuY,14189
70
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,,
71
+ weasyprint/text/fonts.py,sha256=P2Zi7cHwoR4WZsOfm3Ki0lQ3aRYu6YWyFsKvvd0DEqE,17568
72
+ weasyprint/text/line_break.py,sha256=02vHDXwy_yuWSlG19CxDi-sC3rnh8wj6SBY63sskuTM,28091
73
+ weasyprint-68.1.dist-info/entry_points.txt,sha256=wgDp3XXzFywdYgI5vUWMp1zAwx1sZXXH0FTUQbFOq6A,55
74
+ weasyprint-68.1.dist-info/licenses/LICENSE,sha256=v9FOzPphAFdUYOaFVWsYM5nUvTNZBOPJUhsBFtIcVNo,1534
75
+ weasyprint-68.1.dist-info/WHEEL,sha256=G2gURzTEtmeR8nrdXUJfNiB3VYVxigPQ-bEQujpNiNs,82
76
+ weasyprint-68.1.dist-info/METADATA,sha256=QsG5YIcitase__6kIbSxKZ2KsJYdm-LaIxguuOv9BVE,3709
77
+ weasyprint-68.1.dist-info/RECORD,,