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
weasyprint/__init__.py CHANGED
@@ -15,7 +15,7 @@ import cssselect2
15
15
  import tinycss2
16
16
  import tinyhtml5
17
17
 
18
- VERSION = __version__ = '65.1'
18
+ VERSION = __version__ = '67.0'
19
19
 
20
20
  #: Default values for command-line and Python API options. See
21
21
  #: :func:`__main__.main` to learn more about specific options for
@@ -39,6 +39,8 @@ VERSION = __version__ = '65.1'
39
39
  #: A PDF version number.
40
40
  #: :param bool pdf_forms:
41
41
  #: Whether PDF forms have to be included.
42
+ #: :param bool pdf_tags:
43
+ #: Whether PDF should be tagged for accessibility.
42
44
  #: :param bool uncompressed_pdf:
43
45
  #: Whether PDF content should be compressed.
44
46
  #: :param bool custom_metadata:
@@ -71,6 +73,7 @@ DEFAULT_OPTIONS = {
71
73
  'pdf_variant': None,
72
74
  'pdf_version': None,
73
75
  'pdf_forms': None,
76
+ 'pdf_tags': False,
74
77
  'uncompressed_pdf': False,
75
78
  'custom_metadata': False,
76
79
  'presentational_hints': False,
@@ -195,7 +198,8 @@ class HTML:
195
198
  def _ph_stylesheets(self):
196
199
  return [HTML5_PH_STYLESHEET]
197
200
 
198
- def render(self, font_config=None, counter_style=None, **options):
201
+ def render(self, font_config=None, counter_style=None, color_profiles=None,
202
+ **options):
199
203
  """Lay out and paginate the document, but do not (yet) export it.
200
204
 
201
205
  This returns a :class:`document.Document` object which provides
@@ -219,10 +223,11 @@ class HTML:
219
223
  new_options = DEFAULT_OPTIONS.copy()
220
224
  new_options.update(options)
221
225
  options = new_options
222
- return Document._render(self, font_config, counter_style, options)
226
+ return Document._render(
227
+ self, font_config, counter_style, color_profiles, options)
223
228
 
224
229
  def write_pdf(self, target=None, zoom=1, finisher=None,
225
- font_config=None, counter_style=None, **options):
230
+ font_config=None, counter_style=None, color_profiles=None, **options):
226
231
  """Render the document to a PDF file.
227
232
 
228
233
  This is a shortcut for calling :meth:`render`, then
@@ -262,7 +267,7 @@ class HTML:
262
267
  new_options.update(options)
263
268
  options = new_options
264
269
  return (
265
- self.render(font_config, counter_style, **options)
270
+ self.render(font_config, counter_style, color_profiles, **options)
266
271
  .write_pdf(target, zoom, finisher, **options))
267
272
 
268
273
 
@@ -285,7 +290,8 @@ class CSS:
285
290
  string=None, encoding=None, base_url=None,
286
291
  url_fetcher=default_url_fetcher, _check_mime_type=False,
287
292
  media_type='print', font_config=None, counter_style=None,
288
- matcher=None, page_rules=None):
293
+ color_profiles=None, matcher=None, page_rules=None, layers=None,
294
+ layer=None):
289
295
  PROGRESS_LOGGER.info(
290
296
  'Step 2 - Fetching and parsing CSS - %s',
291
297
  filename or url or getattr(file_obj, 'name', 'CSS string'))
@@ -306,10 +312,13 @@ class CSS:
306
312
  self.base_url = base_url
307
313
  self.matcher = matcher or cssselect2.Matcher()
308
314
  self.page_rules = [] if page_rules is None else page_rules
315
+ self.layers = [] if layers is None else layers
309
316
  counter_style = {} if counter_style is None else counter_style
317
+ color_profiles = {} if color_profiles is None else color_profiles
310
318
  preprocess_stylesheet(
311
319
  media_type, base_url, stylesheet, url_fetcher, self.matcher,
312
- self.page_rules, font_config, counter_style)
320
+ self.page_rules, self.layers, font_config, counter_style, color_profiles,
321
+ layer=layer)
313
322
 
314
323
 
315
324
  class Attachment:
@@ -426,6 +435,7 @@ def _select_source(guess=None, filename=None, url=None, file_obj=None,
426
435
  assert string is not None
427
436
  yield 'string', string, base_url, None
428
437
 
438
+
429
439
  # Work around circular imports.
430
440
  from .css import preprocess_stylesheet # noqa: I001, E402
431
441
  from .html import ( # noqa: E402
weasyprint/__main__.py CHANGED
@@ -16,16 +16,18 @@ from .urls import default_url_fetcher
16
16
 
17
17
  class PrintInfo(argparse.Action):
18
18
  def __call__(*_, **__):
19
+ # TODO: ignore check at block-level when available.
20
+ # https://github.com/astral-sh/ruff/issues/3711
19
21
  uname = platform.uname()
20
- print('System:', uname.system)
21
- print('Machine:', uname.machine)
22
- print('Version:', uname.version)
23
- print('Release:', uname.release)
24
- print()
25
- print('WeasyPrint version:', __version__)
26
- print('Python version:', sys.version.split()[0])
27
- print('Pydyf version:', pydyf.__version__)
28
- print('Pango version:', pango.pango_version())
22
+ print('System:', uname.system) # noqa: T201
23
+ print('Machine:', uname.machine) # noqa: T201
24
+ print('Version:', uname.version) # noqa: T201
25
+ print('Release:', uname.release) # noqa: T201
26
+ print() # noqa: T201
27
+ print('WeasyPrint version:', __version__) # noqa: T201
28
+ print('Python version:', sys.version.split()[0]) # noqa: T201
29
+ print('Pydyf version:', pydyf.__version__) # noqa: T201
30
+ print('Pango version:', pango.pango_version()) # noqa: T201
29
31
  sys.exit()
30
32
 
31
33
 
@@ -89,6 +91,8 @@ PARSER.add_argument(
89
91
  PARSER.add_argument('--pdf-version', help='PDF version number')
90
92
  PARSER.add_argument(
91
93
  '--pdf-forms', action='store_true', help='include PDF forms')
94
+ PARSER.add_argument(
95
+ '--pdf-tags', action='store_true', help='tag PDF for accessibility')
92
96
  PARSER.add_argument(
93
97
  '--uncompressed-pdf', action='store_true',
94
98
  help='do not compress PDF content, mainly for debugging purpose')
@@ -136,7 +140,10 @@ PARSER.add_argument(
136
140
  help='print system information and exit')
137
141
  PARSER.add_argument(
138
142
  '-t', '--timeout', type=int,
139
- help='Set timeout in seconds for HTTP requests')
143
+ help='set timeout in seconds for HTTP requests')
144
+ PARSER.add_argument(
145
+ '--allowed-protocols', dest='allowed_protocols',
146
+ help='only authorize comma-separated list of protocols for fetching URLs')
140
147
  PARSER.set_defaults(**DEFAULT_OPTIONS)
141
148
 
142
149
 
@@ -167,6 +174,10 @@ def main(argv=None, stdout=None, stdin=None, HTML=HTML): # noqa: N803
167
174
  url_fetcher = default_url_fetcher
168
175
  if args.timeout is not None:
169
176
  url_fetcher = partial(default_url_fetcher, timeout=args.timeout)
177
+ if args.allowed_protocols is not None:
178
+ protocols = {
179
+ protocol.strip().lower() for protocol in args.allowed_protocols.split(',')}
180
+ url_fetcher = partial(url_fetcher, allowed_protocols=protocols)
170
181
 
171
182
  options = {
172
183
  key: value for key, value in vars(args).items() if key in DEFAULT_OPTIONS}
weasyprint/anchors.py CHANGED
@@ -43,8 +43,8 @@ def gather_anchors(box, anchors, links, bookmarks, forms, parent_matrix=None,
43
43
  border_width = box.border_width()
44
44
  border_height = box.border_height()
45
45
  origin_x, origin_y = box.style['transform_origin']
46
- offset_x = percentage(origin_x, border_width)
47
- offset_y = percentage(origin_y, border_height)
46
+ offset_x = percentage(origin_x, box.style, border_width)
47
+ offset_y = percentage(origin_y, box.style, border_height)
48
48
  origin_x = box.border_box_x() + offset_x
49
49
  origin_y = box.border_box_y() + offset_y
50
50
 
@@ -58,8 +58,8 @@ def gather_anchors(box, anchors, links, bookmarks, forms, parent_matrix=None,
58
58
  b = math.sin(args)
59
59
  c = -b
60
60
  elif name == 'translate':
61
- e = percentage(args[0], border_width)
62
- f = percentage(args[1], border_height)
61
+ e = percentage(args[0], box.style, border_width)
62
+ f = percentage(args[1], box.style, border_height)
63
63
  elif name == 'skew':
64
64
  b, c = math.tan(args[1]), math.tan(args[0])
65
65
  else: