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
@@ -229,24 +229,22 @@ class LayoutContext:
229
229
  self.font_config = font_config
230
230
  self.counter_style = counter_style
231
231
  self.target_collector = target_collector
232
- self._excluded_shapes_lists = []
232
+ self._excluded_shapes_root_boxes = []
233
+ self._excluded_shapes = {}
233
234
  self.footnotes = []
234
235
  self.page_footnotes = {}
235
236
  self.current_page_footnotes = []
236
237
  self.reported_footnotes = []
237
238
  self.current_footnote_area = None # Not initialized yet
238
- self.excluded_shapes = None # Not initialized yet
239
239
  self.page_bottom = None
240
- self.string_set = defaultdict(lambda: defaultdict(lambda: []))
241
- self.running_elements = defaultdict(lambda: defaultdict(lambda: []))
240
+ self.string_set = defaultdict(lambda: defaultdict(list))
241
+ self.running_elements = defaultdict(lambda: defaultdict(list))
242
242
  self.current_page = None
243
243
  self.forced_break = False
244
244
  self.broken_out_of_flow = {}
245
245
  self.in_column = False
246
246
 
247
247
  # Cache
248
- self.strut_layouts = {}
249
- self.font_features = {}
250
248
  self.tables = {}
251
249
  self.dictionaries = {}
252
250
 
@@ -259,34 +257,39 @@ class LayoutContext:
259
257
  # The 1e-9 value comes from PEP 485.
260
258
  return position_y > bottom * (1 + 1e-9)
261
259
 
262
- def create_block_formatting_context(self):
263
- self.excluded_shapes = []
264
- self._excluded_shapes_lists.append(self.excluded_shapes)
260
+ @property
261
+ def excluded_shapes(self):
262
+ return self._excluded_shapes[self._excluded_shapes_root_boxes[-1]]
265
263
 
266
- def finish_block_formatting_context(self, root_box):
264
+ @excluded_shapes.setter
265
+ def excluded_shapes(self, excluded_shapes):
266
+ self._excluded_shapes[self._excluded_shapes_root_boxes[-1]] = excluded_shapes
267
+
268
+ def create_block_formatting_context(self, root_box=None, new_list=None):
269
+ assert root_box not in self._excluded_shapes_root_boxes
270
+ self._excluded_shapes_root_boxes.append(root_box)
271
+ if root_box not in self._excluded_shapes:
272
+ self._excluded_shapes[root_box] = [] if new_list is None else new_list
273
+
274
+ def finish_block_formatting_context(self, root_box=None):
267
275
  # See https://www.w3.org/TR/CSS2/visudet.html#root-height
268
- if root_box.style['height'] == 'auto' and self.excluded_shapes:
276
+ if root_box and root_box.style['height'] == 'auto' and self.excluded_shapes:
269
277
  box_bottom = root_box.content_box_y() + root_box.height
270
278
  max_shape_bottom = max([
271
279
  shape.position_y + shape.margin_height()
272
280
  for shape in self.excluded_shapes] + [box_bottom])
273
281
  root_box.height += max_shape_bottom - box_bottom
274
- self._excluded_shapes_lists.pop()
275
- if self._excluded_shapes_lists:
276
- self.excluded_shapes = self._excluded_shapes_lists[-1]
277
- else:
278
- self.excluded_shapes = None
282
+ self._excluded_shapes.pop(self._excluded_shapes_root_boxes.pop())
279
283
 
280
- def create_flex_formatting_context(self):
281
- self.excluded_shapes = FakeList()
282
- self._excluded_shapes_lists.append(self.excluded_shapes)
284
+ def create_flex_formatting_context(self, root_box):
285
+ self.create_block_formatting_context(root_box, FakeList())
283
286
 
284
287
  def finish_flex_formatting_context(self, root_box):
285
- self._excluded_shapes_lists.pop()
286
- if self._excluded_shapes_lists:
287
- self.excluded_shapes = self._excluded_shapes_lists[-1]
288
- else:
289
- self.excluded_shapes = None
288
+ self.finish_block_formatting_context(root_box)
289
+
290
+ def add_broken_out_of_flow(self, new_box, box, containing_block, resume_at):
291
+ self.broken_out_of_flow[new_box] = (
292
+ box, containing_block, self._excluded_shapes_root_boxes[-1], resume_at)
290
293
 
291
294
  def get_string_set_for(self, page, name, keyword='first'):
292
295
  """Resolve value of string function."""
@@ -389,10 +392,13 @@ class LayoutContext:
389
392
  if not self.in_column:
390
393
  self.page_bottom -= footnote_area.margin_height()
391
394
  last_child = footnote_area.children[-1]
392
- overflow = (
393
- last_child.position_y + last_child.margin_height() >
395
+ last_child_bottom = (
396
+ last_child.position_y + last_child.margin_height() -
397
+ last_child.margin_bottom)
398
+ footnote_area_bottom = (
394
399
  footnote_area.position_y + footnote_area.margin_height() -
395
400
  footnote_area.margin_bottom)
401
+ overflow = last_child_bottom > footnote_area_bottom
396
402
  return overflow
397
403
  else:
398
404
  self.current_footnote_area.height = 0
@@ -67,7 +67,9 @@ def absolute_width(box, context, cb_x, cb_y, cb_width, cb_height):
67
67
  available_width = cb_width - (
68
68
  paddings_borders + box.margin_left + box.margin_right)
69
69
  box.width = shrink_to_fit(context, box, available_width)
70
- if not ltr:
70
+ if box.is_outside_marker:
71
+ translate_box_width = ltr
72
+ elif not ltr:
71
73
  translate_box_width = True
72
74
  translate_x = default_translate_x + available_width
73
75
  elif box.left != 'auto' and box.right != 'auto' and box.width != 'auto':
@@ -200,7 +202,8 @@ def absolute_block(context, box, containing_block, fixed_boxes, bottom_space,
200
202
  new_box, resume_at, _, _, _, _ = block_container_layout(
201
203
  context, box, bottom_space, skip_stack, page_is_empty=True,
202
204
  absolute_boxes=absolute_boxes, fixed_boxes=fixed_boxes,
203
- adjoining_margins=None, discard=False, max_lines=None)
205
+ adjoining_margins=None, first_letter_style=None, first_line_style=None,
206
+ discard=False, max_lines=None)
204
207
  elif isinstance(box, (boxes.FlexContainerBox)):
205
208
  new_box, resume_at, _, _, _ = flex_layout(
206
209
  context, box, bottom_space, skip_stack, containing_block,
@@ -235,8 +238,7 @@ def absolute_layout(context, placeholder, containing_block, fixed_boxes,
235
238
  context, box, containing_block, fixed_boxes, bottom_space, skip_stack)
236
239
  placeholder.set_laid_out_box(new_box)
237
240
  if resume_at:
238
- context.broken_out_of_flow[placeholder] = (
239
- box, containing_block, resume_at)
241
+ context.add_broken_out_of_flow(placeholder, box, containing_block, resume_at)
240
242
 
241
243
 
242
244
  def absolute_box_layout(context, box, containing_block, fixed_boxes,
@@ -257,7 +259,6 @@ def absolute_box_layout(context, box, containing_block, fixed_boxes,
257
259
  resolve_percentages(box, (cb_width, cb_height))
258
260
  resolve_position_percentages(box, (cb_width, cb_height))
259
261
 
260
- context.create_block_formatting_context()
261
262
  if isinstance(box, boxes.BlockReplacedBox):
262
263
  new_box = absolute_replaced(
263
264
  context, box, cb_x, cb_y, cb_width, cb_height)
@@ -267,7 +268,7 @@ def absolute_box_layout(context, box, containing_block, fixed_boxes,
267
268
  new_box, resume_at = absolute_block(
268
269
  context, box, containing_block, fixed_boxes, bottom_space,
269
270
  skip_stack, cb_x, cb_y, cb_width, cb_height)
270
- context.finish_block_formatting_context(new_box)
271
+
271
272
  return new_box, resume_at
272
273
 
273
274
 
@@ -3,13 +3,13 @@
3
3
  from collections import namedtuple
4
4
  from itertools import cycle
5
5
 
6
- from tinycss2.color4 import parse_color
6
+ from tinycss2.color5 import parse_color
7
7
 
8
8
  from ..formatting_structure import boxes
9
9
  from . import replaced
10
10
  from .percent import percentage, resolve_radii_percentages
11
11
 
12
- Background = namedtuple('Background', 'color, layers, image_rendering')
12
+ Background = namedtuple('Background', 'color, layers, style')
13
13
  BackgroundLayer = namedtuple(
14
14
  'BackgroundLayer',
15
15
  'image, size, position, repeat, unbounded, '
@@ -88,7 +88,7 @@ def layout_box_backgrounds(page, box, get_image_from_uri, layout_children=True,
88
88
  style['background_origin'],
89
89
  style['background_position'],
90
90
  style['background_attachment']]))]
91
- box.background = Background(color, layers, style['image_rendering'])
91
+ box.background = Background(color, layers, style)
92
92
 
93
93
 
94
94
  def layout_background_layer(box, page, resolution, image, size, clip, repeat,
@@ -177,15 +177,15 @@ def layout_background_layer(box, page, resolution, image, size, clip, repeat,
177
177
  size_width, size_height = size
178
178
  image_width, image_height = replaced.default_image_sizing(
179
179
  intrinsic_width, intrinsic_height, ratio,
180
- percentage(size_width, positioning_width),
181
- percentage(size_height, positioning_height),
180
+ percentage(size_width, box.style, positioning_width),
181
+ percentage(size_height, box.style, positioning_height),
182
182
  positioning_width, positioning_height)
183
183
 
184
184
  origin_x, position_x, origin_y, position_y = position
185
185
  ref_x = positioning_width - image_width
186
186
  ref_y = positioning_height - image_height
187
- position_x = percentage(position_x, ref_x)
188
- position_y = percentage(position_y, ref_y)
187
+ position_x = percentage(position_x, box.style, ref_x)
188
+ position_y = percentage(position_y, box.style, ref_y)
189
189
  if origin_x == 'right':
190
190
  position_x = ref_x - position_x
191
191
  if origin_y == 'bottom':