weasyprint 65.1__py3-none-any.whl → 66.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 (45) hide show
  1. weasyprint/__init__.py +4 -1
  2. weasyprint/__main__.py +2 -0
  3. weasyprint/css/__init__.py +12 -4
  4. weasyprint/css/computed_values.py +8 -2
  5. weasyprint/css/html5_ua.css +2 -7
  6. weasyprint/css/html5_ua_form.css +1 -1
  7. weasyprint/css/utils.py +1 -1
  8. weasyprint/document.py +2 -10
  9. weasyprint/draw/__init__.py +51 -57
  10. weasyprint/draw/border.py +120 -66
  11. weasyprint/draw/text.py +1 -2
  12. weasyprint/formatting_structure/boxes.py +3 -2
  13. weasyprint/formatting_structure/build.py +32 -42
  14. weasyprint/images.py +8 -15
  15. weasyprint/layout/__init__.py +5 -2
  16. weasyprint/layout/absolute.py +4 -1
  17. weasyprint/layout/block.py +60 -29
  18. weasyprint/layout/column.py +1 -0
  19. weasyprint/layout/flex.py +41 -21
  20. weasyprint/layout/float.py +8 -1
  21. weasyprint/layout/grid.py +1 -1
  22. weasyprint/layout/inline.py +7 -8
  23. weasyprint/layout/page.py +23 -1
  24. weasyprint/layout/preferred.py +59 -32
  25. weasyprint/layout/table.py +8 -4
  26. weasyprint/pdf/__init__.py +13 -6
  27. weasyprint/pdf/anchors.py +2 -2
  28. weasyprint/pdf/pdfua.py +7 -115
  29. weasyprint/pdf/stream.py +40 -49
  30. weasyprint/pdf/tags.py +305 -0
  31. weasyprint/stacking.py +14 -15
  32. weasyprint/svg/__init__.py +22 -11
  33. weasyprint/svg/bounding_box.py +4 -2
  34. weasyprint/svg/defs.py +4 -9
  35. weasyprint/svg/utils.py +9 -5
  36. weasyprint/text/fonts.py +1 -1
  37. weasyprint/text/line_break.py +45 -26
  38. weasyprint/urls.py +21 -10
  39. {weasyprint-65.1.dist-info → weasyprint-66.0.dist-info}/METADATA +1 -1
  40. weasyprint-66.0.dist-info/RECORD +74 -0
  41. weasyprint/draw/stack.py +0 -13
  42. weasyprint-65.1.dist-info/RECORD +0 -74
  43. {weasyprint-65.1.dist-info → weasyprint-66.0.dist-info}/WHEEL +0 -0
  44. {weasyprint-65.1.dist-info → weasyprint-66.0.dist-info}/entry_points.txt +0 -0
  45. {weasyprint-65.1.dist-info → weasyprint-66.0.dist-info}/licenses/LICENSE +0 -0
weasyprint/stacking.py CHANGED
@@ -45,7 +45,7 @@ class StackingContext:
45
45
  child_contexts = [cls.from_box(child, page) for child in page.children]
46
46
  # Children are sub-contexts, remove them from the "normal" tree.
47
47
  page = page.copy_with_children([])
48
- return cls(page, child_contexts, [], [], [], page)
48
+ return cls(page, child_contexts, [], [], {}, page)
49
49
 
50
50
  @classmethod
51
51
  def from_box(cls, box, page, child_contexts=None):
@@ -60,7 +60,7 @@ class StackingContext:
60
60
  # context, not this new one."
61
61
  blocks = []
62
62
  floats = []
63
- blocks_and_cells = []
63
+ blocks_and_cells = {}
64
64
  box = _dispatch_children(
65
65
  box, page, child_contexts, blocks, floats, blocks_and_cells)
66
66
  return cls(box, children, blocks, floats, blocks_and_cells, page)
@@ -99,22 +99,21 @@ def _dispatch(box, page, child_contexts, blocks, floats, blocks_and_cells):
99
99
  else:
100
100
  if isinstance(box, boxes.BlockLevelBox):
101
101
  blocks_index = len(blocks)
102
- blocks_and_cells_index = len(blocks_and_cells)
102
+ box_blocks_and_cells = {}
103
+ box = _dispatch_children(
104
+ box, page, child_contexts, blocks, floats, box_blocks_and_cells)
105
+ blocks.insert(blocks_index, box)
106
+ blocks_and_cells[box] = box_blocks_and_cells
103
107
  elif isinstance(box, boxes.TableCellBox):
104
- blocks_index = None
105
- blocks_and_cells_index = len(blocks_and_cells)
108
+ box_blocks_and_cells = {}
109
+ box = _dispatch_children(
110
+ box, page, child_contexts, blocks, floats, box_blocks_and_cells)
111
+ blocks_and_cells[box] = box_blocks_and_cells
106
112
  else:
107
113
  blocks_index = None
108
- blocks_and_cells_index = None
109
-
110
- box = _dispatch_children(
111
- box, page, child_contexts, blocks, floats, blocks_and_cells)
112
-
113
- # Insert at the positions before dispatch the children.
114
- if blocks_index is not None:
115
- blocks.insert(blocks_index, box)
116
- if blocks_and_cells_index is not None:
117
- blocks_and_cells.insert(blocks_and_cells_index, box)
114
+ box_blocks_and_cells = None
115
+ box = _dispatch_children(
116
+ box, page, child_contexts, blocks, floats, blocks_and_cells)
118
117
 
119
118
  return box
120
119
 
@@ -92,6 +92,7 @@ class Node:
92
92
  self._wrapper = wrapper
93
93
  self._etree_node = wrapper.etree_element
94
94
  self._style = style
95
+ self._children = None
95
96
 
96
97
  self.attrib = wrapper.etree_element.attrib.copy()
97
98
 
@@ -178,10 +179,14 @@ class Node:
178
179
 
179
180
  def __iter__(self):
180
181
  """Yield node children, handling cascade."""
181
- for wrapper in self._wrapper:
182
- child = Node(wrapper, self._style)
183
- self.cascade(child)
184
- yield child
182
+ if self._children is None:
183
+ children = []
184
+ for wrapper in self._wrapper:
185
+ child = Node(wrapper, self._style)
186
+ self.cascade(child)
187
+ children.append(child)
188
+ self._children = children
189
+ return iter(self._children)
185
190
 
186
191
  def get_viewbox(self):
187
192
  """Get node viewBox as a tuple of floats."""
@@ -410,16 +415,13 @@ class SVG:
410
415
  apply_filters(self, node, filter_, font_size)
411
416
 
412
417
  # Apply transform attribute
413
- self.transform(node.get('transform'), font_size)
418
+ self.transform(node, font_size)
414
419
 
415
420
  # Create substream for opacity
416
421
  opacity = alpha_value(node.get('opacity', 1))
417
422
  if fill_stroke and 0 <= opacity < 1:
418
423
  original_streams.append(self.stream)
419
- box = self.calculate_bounding_box(node, font_size)
420
- if not is_valid_bounding_box(box):
421
- box = (0, 0, self.inner_width, self.inner_height)
422
- self.stream = self.stream.add_group(*box)
424
+ self.stream = self.stream.add_group(0, 0, 0, 0) # BBox set after drawing
423
425
 
424
426
  # Clip
425
427
  clip_path = parse_url(node.get('clip-path')).fragment
@@ -523,6 +525,12 @@ class SVG:
523
525
 
524
526
  # Apply opacity stream and restore original stream
525
527
  if fill_stroke and 0 <= opacity < 1:
528
+ box = self.calculate_bounding_box(node, font_size)
529
+ if not is_valid_bounding_box(box):
530
+ box = (0, 0, self.inner_width, self.inner_height)
531
+ x, y, width, height = box
532
+ self.stream.extra['BBox'][:] = x, y, x + width, y + height
533
+
526
534
  group_id = self.stream.id
527
535
  self.stream = original_streams.pop()
528
536
  self.stream.set_alpha(opacity, stroke=True, fill=True)
@@ -760,12 +768,15 @@ class SVG:
760
768
  else:
761
769
  self.stream.end()
762
770
 
763
- def transform(self, transform_string, font_size):
771
+ def transform(self, node, font_size):
764
772
  """Apply a transformation string to the node."""
773
+ transform_origin = node.get('transform-origin')
774
+ transform_string = node.get('transform')
765
775
  if not transform_string:
766
776
  return
767
777
 
768
- matrix = transform(transform_string, font_size, self.inner_diagonal)
778
+ matrix = transform(
779
+ transform_string, transform_origin, font_size, self.inner_diagonal)
769
780
  if matrix.determinant:
770
781
  self.stream.transform(*matrix.values)
771
782
 
@@ -206,7 +206,7 @@ def bounding_box_path(svg, node, font_size):
206
206
 
207
207
  def bounding_box_text(svg, node, font_size):
208
208
  """Bounding box for text node."""
209
- return node.get('text_bounding_box')
209
+ return getattr(node, 'text_bounding_box', None)
210
210
 
211
211
 
212
212
  def bounding_box_g(svg, node, font_size):
@@ -229,7 +229,9 @@ def bounding_box_use(svg, node, font_size):
229
229
  if (tree := get_use_tree(svg, node, font_size)) is None:
230
230
  return EMPTY_BOUNDING_BOX
231
231
  else:
232
- return bounding_box(svg, tree, font_size, True)
232
+ x, y = svg.point(node.get('x'), node.get('y'), font_size)
233
+ box = bounding_box(svg, tree, font_size, True)
234
+ return box[0] + x, box[1] + y, box[2], box[3]
233
235
 
234
236
 
235
237
  def _bounding_box_elliptical_arc(x1, y1, rx, ry, phi, large, sweep, x, y):
weasyprint/svg/defs.py CHANGED
@@ -44,12 +44,6 @@ def get_use_tree(svg, node, font_size):
44
44
 
45
45
  def use(svg, node, font_size):
46
46
  """Draw use tags."""
47
- x, y = svg.point(node.get('x'), node.get('y'), font_size)
48
-
49
- for attribute in ('x', 'y', 'viewBox', 'mask'):
50
- if attribute in node.attrib:
51
- del node.attrib[attribute]
52
-
53
47
  if (tree := get_use_tree(svg, node, font_size)) is None:
54
48
  return
55
49
 
@@ -69,6 +63,7 @@ def use(svg, node, font_size):
69
63
 
70
64
  node.cascade(tree)
71
65
  node.override_iter(iter((tree,)))
66
+ x, y = svg.point(node.get('x'), node.get('y'), font_size)
72
67
  svg.stream.transform(e=x, f=y)
73
68
 
74
69
 
@@ -137,7 +132,7 @@ def draw_gradient(svg, node, gradient, font_size, opacity, stroke):
137
132
 
138
133
  if 'gradientTransform' in gradient.attrib:
139
134
  transform_matrix = transform(
140
- gradient.get('gradientTransform'), font_size,
135
+ gradient.get('gradientTransform'), '0 0', font_size,
141
136
  svg.normalized_diagonal)
142
137
  matrix = transform_matrix @ matrix
143
138
 
@@ -407,7 +402,7 @@ def spread_radial_gradient(spread, positions, colors, fx, fy, fr, cx, cy, r,
407
402
  average_positions = [position, ratio, ratio, next_position]
408
403
  zero_color = gradient_average_color(
409
404
  average_colors, average_positions)
410
- colors = [zero_color] + original_colors[-(i - 1):] + colors
405
+ colors = [zero_color, *original_colors[-(i - 1):], *colors]
411
406
  new_positions = [
412
407
  position - 1 - full_repeat for position
413
408
  in original_positions[-(i - 1):]]
@@ -450,7 +445,7 @@ def draw_pattern(svg, node, pattern, font_size, opacity, stroke):
450
445
 
451
446
  if 'patternTransform' in pattern.attrib:
452
447
  transform_matrix = transform(
453
- pattern.get('patternTransform'), font_size, svg.inner_diagonal)
448
+ pattern.get('patternTransform'), '0 0', font_size, svg.inner_diagonal)
454
449
  matrix = transform_matrix @ matrix
455
450
 
456
451
  matrix = matrix @ svg.stream.ctm
weasyprint/svg/utils.py CHANGED
@@ -149,13 +149,17 @@ def color(string):
149
149
  return parse_color(string or '') or parse_color('black')
150
150
 
151
151
 
152
- def transform(transform_string, font_size, normalized_diagonal):
152
+ def transform(transform_string, transform_origin, font_size, normalized_diagonal):
153
153
  """Get a matrix corresponding to the transform string."""
154
154
  # TODO: merge with gather_anchors and css.validation.properties.transform
155
- transformations = re.findall(
156
- r'(\w+) ?\( ?(.*?) ?\)', normalize(transform_string))
157
- matrix = Matrix()
158
155
 
156
+ origin_x, origin_y = 0, 0
157
+ size_strings = normalize(transform_origin).split()
158
+ if len(size_strings) == 2:
159
+ origin_x, origin_y = size(size_strings[0]), size(size_strings[1])
160
+ matrix = Matrix(e=origin_x, f=origin_y)
161
+
162
+ transformations = re.findall(r'(\w+) ?\( ?(.*?) ?\)', normalize(transform_string))
159
163
  for transformation_type, transformation in transformations:
160
164
  values = [
161
165
  size(value, font_size, normalized_diagonal)
@@ -196,4 +200,4 @@ def transform(transform_string, font_size, normalized_diagonal):
196
200
  if transformation_type in ('scaleY', 'scale'):
197
201
  matrix = Matrix(d=values.pop(0)) @ matrix
198
202
 
199
- return matrix
203
+ return Matrix(e=-origin_x, f=-origin_y) @ matrix
weasyprint/text/fonts.py CHANGED
@@ -374,7 +374,7 @@ def get_pango_font_key(pango_font):
374
374
  # TODO: This value is stable for a given Pango font in a given Pango map, but can’t
375
375
  # be cached with just the Pango font as a key because two Pango fonts could point to
376
376
  # the same address for two different Pango maps. We should cache it in the
377
- # FontConfiguration object. See https://github.com/Kozea/WeasyPrint/issues/2144
377
+ # FontConfiguration object. See issue #2144.
378
378
  description = ffi.gc(
379
379
  pango.pango_font_describe(pango_font), pango.pango_font_description_free)
380
380
  font_size = pango.pango_font_description_get_size(description) * FROM_UNITS
@@ -377,27 +377,45 @@ def split_first_line(text, style, context, max_width, justification_spacing,
377
377
  soft_hyphen = '\xad'
378
378
 
379
379
  auto_hyphenation = manual_hyphenation = False
380
+
380
381
  if hyphens != 'none':
381
382
  manual_hyphenation = soft_hyphen in first_line_text + second_line_text
383
+
382
384
  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
385
+ # Get text until next line break opportunity.
386
+ next_text = second_line_text
387
+ if (next_break_point := get_next_break_point_from_text(second_line_text, lang)):
388
+ next_text = next_text[:next_break_point]
389
+
390
+ # Try all words included in this text.
391
+ next_text_index = 0
392
+ while next_text:
393
+ next_word_boundaries = get_next_word_boundaries(next_text, lang)
394
+ if next_word_boundaries:
395
+ # We have a word to hyphenate.
396
+ start_word, stop_word = next_word_boundaries
397
+ next_word = next_text[start_word:stop_word]
398
+ if stop_word - start_word >= total:
399
+ # This word is long enough.
400
+ first_line_width, _ = line_size(first_line, style)
401
+ space = max_width - first_line_width
402
+ if style['hyphenate_limit_zone'].unit == '%':
403
+ limit_zone = (
404
+ max_width * style['hyphenate_limit_zone'].value / 100)
405
+ else:
406
+ limit_zone = style['hyphenate_limit_zone'].value
407
+ if space > limit_zone or space < 0:
408
+ # Available space is worth the try, or the line is even too long
409
+ # to fit: try to hyphenate.
410
+ auto_hyphenation = True
411
+ next_text_index += start_word
412
+ break
413
+
414
+ # This word doesn’t work, try next one.
415
+ next_text = next_text[stop_word:]
416
+ next_text_index += stop_word
417
+ else:
418
+ break
401
419
 
402
420
  # Automatic hyphenation opportunities within a word must be ignored if the
403
421
  # word contains a conditional hyphen, in favor of the conditional
@@ -413,24 +431,21 @@ def split_first_line(text, style, context, max_width, justification_spacing,
413
431
  match.start() for match in re.finditer(soft_hyphen, second_line_text)]
414
432
  soft_hyphen_indexes.reverse()
415
433
  dictionary_iterations = [second_line_text[:i+1] for i in soft_hyphen_indexes]
416
- start_word = 0
417
434
  elif auto_hyphenation:
418
435
  dictionary_key = (lang, left, right, total)
419
436
  dictionary = context.dictionaries.get(dictionary_key)
420
437
  if dictionary is None:
421
438
  dictionary = pyphen.Pyphen(lang=lang, left=left, right=right)
422
439
  context.dictionaries[dictionary_key] = dictionary
440
+ previous_words = second_line_text[:next_text_index]
423
441
  dictionary_iterations = [
424
- start for start, end in dictionary.iterate(next_word)]
442
+ previous_words + start for start, end in dictionary.iterate(next_word)]
425
443
  else:
426
444
  dictionary_iterations = []
427
445
 
428
446
  if dictionary_iterations:
429
447
  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)
448
+ new_first_line_text = first_line_text + first_word_part
434
449
  hyphenated_first_line_text = (
435
450
  new_first_line_text + style['hyphenate_character'])
436
451
  new_layout = create_layout(
@@ -526,12 +541,16 @@ def get_next_break_point(log_attrs):
526
541
  return i
527
542
 
528
543
 
529
- def can_break_text(text, lang):
544
+ def get_next_break_point_from_text(text, lang):
530
545
  if not text or len(text) < 2:
531
546
  return None
532
547
  log_attrs = get_log_attrs(text, lang)
533
548
  length = len(text) + 1
534
- return get_next_break_point(log_attrs[1:length-1]) is not None
549
+ return get_next_break_point(log_attrs[1:length-1])
550
+
551
+
552
+ def can_break_text(text, lang):
553
+ return get_next_break_point_from_text(text, lang) is not None
535
554
 
536
555
 
537
556
  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
@@ -177,7 +177,7 @@ def ensure_url(string):
177
177
  return string if url_is_absolute(string) else path2url(string)
178
178
 
179
179
 
180
- def default_url_fetcher(url, timeout=10, ssl_context=None):
180
+ def default_url_fetcher(url, timeout=10, ssl_context=None, http_headers=None):
181
181
  """Fetch an external resource such as an image or stylesheet.
182
182
 
183
183
  Another callable with the same signature can be given as the
@@ -190,6 +190,8 @@ def default_url_fetcher(url, timeout=10, ssl_context=None):
190
190
  The number of seconds before HTTP requests are dropped.
191
191
  :param ssl.SSLContext ssl_context:
192
192
  An SSL context used for HTTP requests.
193
+ :param dict http_headers:
194
+ Additional HTTP headers used for HTTP requests.
193
195
  :raises: An exception indicating failure, e.g. :obj:`ValueError` on
194
196
  syntactically invalid URL.
195
197
  :returns: A :obj:`dict` with the following keys:
@@ -205,7 +207,9 @@ def default_url_fetcher(url, timeout=10, ssl_context=None):
205
207
  if there were e.g. HTTP redirects.
206
208
  * Optionally: ``filename``, the filename of the resource. Usually
207
209
  derived from the *filename* parameter in a *Content-Disposition*
208
- header
210
+ header.
211
+ * Optionally: ``path``, the path of the resource if it is stored on the
212
+ local filesystem.
209
213
 
210
214
  If a ``file_obj`` key is given, it is the caller’s responsibility
211
215
  to call ``file_obj.close()``. The default function used internally to
@@ -218,19 +222,26 @@ def default_url_fetcher(url, timeout=10, ssl_context=None):
218
222
  # See https://bugs.python.org/issue34702
219
223
  if url.startswith('file://'):
220
224
  url = url.split('?')[0]
225
+ path = url2pathname(url.removeprefix('file:'))
226
+ else:
227
+ path = None
221
228
 
222
229
  url = iri_to_uri(url)
230
+ if http_headers is not None:
231
+ http_headers = {**HTTP_HEADERS, **http_headers}
232
+ else:
233
+ http_headers = HTTP_HEADERS
223
234
  response = urlopen(
224
- Request(url, headers=HTTP_HEADERS), timeout=timeout,
235
+ Request(url, headers=http_headers), timeout=timeout,
225
236
  context=ssl_context)
226
- response_info = response.info()
227
237
  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(),
238
+ 'redirected_url': response.url,
239
+ 'mime_type': response.headers.get_content_type(),
240
+ 'encoding': response.headers.get_param('charset'),
241
+ 'filename': response.headers.get_filename(),
242
+ 'path': path,
232
243
  }
233
- content_encoding = response_info.get('Content-Encoding')
244
+ content_encoding = response.headers.get('Content-Encoding')
234
245
  if content_encoding == 'gzip':
235
246
  result['file_obj'] = StreamingGzipFile(fileobj=response)
236
247
  elif content_encoding == 'deflate':
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: weasyprint
3
- Version: 65.1
3
+ Version: 66.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>
@@ -0,0 +1,74 @@
1
+ weasyprint/__init__.py,sha256=bgv7c6-Fvtfjj71VjvhDjQ-xlF4z7QnolEHsvldo_Cs,17512
2
+ weasyprint/__main__.py,sha256=we7G-HJTb4IxouME9I-cXUj1vSWPqIx0fK68WHdaKaM,7208
3
+ weasyprint/anchors.py,sha256=yXEZD0uFsCAdDjC07cHgWqRuGOSsOsmmmk4omZZecAo,6428
4
+ weasyprint/document.py,sha256=EShIUTYQ9MyxFA1nuUE0HGq1gP6bursU6y9acpkT4x0,16757
5
+ weasyprint/html.py,sha256=om7dvhx12ecunTaVclmWejkr5nPQF1BZmeH7jqQ_4GM,11590
6
+ weasyprint/images.py,sha256=G35CNQ0dIjMPRQaqdqD6tDC9z3ObkfHOfoWvTJOsViI,35245
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=v-t4AnyOU0gCwxEAq0PwG_RFmPAXlw2pY5t1W7ZiUEA,10429
11
+ weasyprint/css/__init__.py,sha256=uBz22V4bydfyuYjQ-YD3jvuKfVmK9INtykAjEOIgOK4,51918
12
+ weasyprint/css/computed_values.py,sha256=V0wPsAVHUcRPG6bEM3O4FrNa8PbbU_LGM8cRELUOIj4,27569
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=FM3nd6XbWFG0ZRMBh1bpiWbaTxXGPEhf_KqZzTWCBV4,18330
16
+ weasyprint/css/html5_ua_form.css,sha256=rNxw41kQVpd8sLkWDSW0WrOIqyDYtaviKWphpuPp2Jw,289
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=2KxWyzYFLnyexj2BQn2-yDfGG6TXRe-yWjQTLVnIAIg,25118
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=Dh-TUi6QGCaCJUlJAlEuTTkd6mNlDEybdtD---923wc,22793
26
+ weasyprint/draw/border.py,sha256=IsyeyC3BKooXQUZNtXaD3u_YiAkdLTbn2zuBg-5-qAQ,30441
27
+ weasyprint/draw/color.py,sha256=ZjqiMDSNIFOLsIz2qP30HrFMbVqeEmYAKxcU6tl4GBs,1449
28
+ weasyprint/draw/text.py,sha256=qZIJY7lFA10GFIAqZxda2Z54l9ZOOBoJA9ww6lPtpZo,11393
29
+ weasyprint/formatting_structure/boxes.py,sha256=X960KZFuNXWSqfD1hqO8JOu52cXGZ19ZHYY-n2t2N1A,26340
30
+ weasyprint/formatting_structure/build.py,sha256=4ICJuiDv8t2htwGyPb9aGHuAPK0u_3EVBTUxyW4Q9Xg,55933
31
+ weasyprint/layout/__init__.py,sha256=3XZZ7fm3t9Fwek1qFGDcZprbUdSXNZdvcy_0y_kfzWY,16444
32
+ weasyprint/layout/absolute.py,sha256=C-52iDnb6H5oGBpdxU6sKxvDvE-pBiBrHc8Vjh-Jb20,14056
33
+ weasyprint/layout/background.py,sha256=0ZRFZGAJnvF9eULIMyt8oy6lDqE-hKSbcyomQPx8HsQ,10008
34
+ weasyprint/layout/block.py,sha256=QBR2saIiQO68XBtUFtmRD2Q0R1OoUaUYhNPw0o0qpPU,47123
35
+ weasyprint/layout/column.py,sha256=8ka0QJuvR-M7QoEEyILpJRMYtIF-M-f8g888ZUxiGrY,17373
36
+ weasyprint/layout/flex.py,sha256=ptW2MHvKD-2vIoGGF_Tqy4eAZ1Au1GSdQDp6bU_yucc,43759
37
+ weasyprint/layout/float.py,sha256=FeM2MW7FzjB7wzWNzeNdSIRjG0g8n7b6SMVgmuU5s7o,9543
38
+ weasyprint/layout/grid.py,sha256=XsG_Z4LOM-34mTRD3AqEEfB-_uqTPbf94WED1zS2JdY,54729
39
+ weasyprint/layout/inline.py,sha256=YAl6rsXep7WyGDqAf-uIdGRcBA3Mt-ps_Qqfv4gSNnc,48139
40
+ weasyprint/layout/leader.py,sha256=wklI0aLyTx0VJhqU7D_FxtJpfe7dXswcN-VApAusM-Q,2825
41
+ weasyprint/layout/min_max.py,sha256=JdXJG9ISO_RsfeHua_-3g477a16I-NrnYuwH_tQwq4o,1527
42
+ weasyprint/layout/page.py,sha256=7lHmlu4VVXIPMYThqSDaM19iHF4CAZ4OMX_9NPFnf1Q,41020
43
+ weasyprint/layout/percent.py,sha256=2XzT_Y-fu7OVt2Ut1f9T1Tt9S4ftRr4x7wL3agvEJus,5626
44
+ weasyprint/layout/preferred.py,sha256=rS-zyPY-RoSfbv16_LJb7y61adlZED2MA6eNNSG1U3I,32379
45
+ weasyprint/layout/replaced.py,sha256=ucAd6VMKIEryjnwK8ciKbUoE2yK29-ggdYlGW3KPDXk,11178
46
+ weasyprint/layout/table.py,sha256=jdZK3ZIcOpeHwLXuEf9fwrM0nY8sdlLj7laqOK7C43g,47624
47
+ weasyprint/pdf/__init__.py,sha256=yQyaA_v0Qb4d4iXacGQGKtO2qsrMXj0iX2klTSBHggI,12203
48
+ weasyprint/pdf/anchors.py,sha256=x-7q87XceZo9SwuAm5o6EO3xGkvUpneY9acduFJ7B6Q,17398
49
+ weasyprint/pdf/debug.py,sha256=reLw6U6hK94FOVNYW8psdt_SFN11iIe1rhYkr6sURF4,1407
50
+ weasyprint/pdf/fonts.py,sha256=D_T0JWFnofSDqdU29noTg8IbyenN9SlaULxSTPlg1JQ,24777
51
+ weasyprint/pdf/metadata.py,sha256=r5ATj8Lv_6Ib-RbA2zgazyo6yJwF-LoqoNAWUchV4qE,4168
52
+ weasyprint/pdf/pdfa.py,sha256=97J7nlKHmP5vdSBz0X0cDnGnqAPZ5qqoQ7ArZsdWRT8,3626
53
+ weasyprint/pdf/pdfua.py,sha256=96poiMjhQHd4MjAD0L5C8eio4yyYWjsbo57Ts_fhMEw,521
54
+ weasyprint/pdf/sRGB2014.icc,sha256=OEuDLeNBIGZ0O1KnXukGtvufuNngnpNvwsQyI4Fcbgo,3024
55
+ weasyprint/pdf/stream.py,sha256=iHRWylT7xYOTt3JIWcXzFXDcOV2ZZwfC0e26KNILoJI,10794
56
+ weasyprint/pdf/tags.py,sha256=P4FkR9VOc0w92C9Rp6ik5Mksbrct9vb3jKrWM55S2VA,11678
57
+ weasyprint/svg/__init__.py,sha256=EdHjWxbsgvmruBhyRwug2uoHBLHvldQxWvjfq9N7mdA,30453
58
+ weasyprint/svg/bounding_box.py,sha256=auXs-vD2nvOx3cplHLGXFzy7X_f_IY4hg_IzKlUTXjM,13129
59
+ weasyprint/svg/css.py,sha256=AUsIim2rOmRGLgFuiWm4EzXwnrRlThczfM17Uq2MRUg,3832
60
+ weasyprint/svg/defs.py,sha256=poSH9LPdRTdqv0H-Ns7W58V4sRzULZKIjK_XCnhrxY0,20862
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=mqrwh-gkCgEv0diyTk6o3hJbJG9gGS1wIE0dv5JQuSg,7226
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=LO0B5cbu2jj-5PyxGvT_OxgVEmNnoc5z3SA4ISHRkUo,17425
69
+ weasyprint/text/line_break.py,sha256=Ogc-3aTFnLJOBMZHrhDMFM07ISPHPuiQ0HytSB2_D00,25485
70
+ weasyprint-66.0.dist-info/entry_points.txt,sha256=wgDp3XXzFywdYgI5vUWMp1zAwx1sZXXH0FTUQbFOq6A,55
71
+ weasyprint-66.0.dist-info/licenses/LICENSE,sha256=v9FOzPphAFdUYOaFVWsYM5nUvTNZBOPJUhsBFtIcVNo,1534
72
+ weasyprint-66.0.dist-info/WHEEL,sha256=G2gURzTEtmeR8nrdXUJfNiB3VYVxigPQ-bEQujpNiNs,82
73
+ weasyprint-66.0.dist-info/METADATA,sha256=7D2zTWZQ6isQiaHnc9c2witt6xFVk5ExlkyEUOnCH3o,3707
74
+ weasyprint-66.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,,