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.
- weasyprint/__init__.py +17 -7
- weasyprint/__main__.py +21 -10
- weasyprint/anchors.py +4 -4
- weasyprint/css/__init__.py +732 -67
- weasyprint/css/computed_values.py +65 -170
- weasyprint/css/counters.py +1 -1
- weasyprint/css/functions.py +206 -0
- weasyprint/css/html5_ua.css +3 -7
- weasyprint/css/html5_ua_form.css +2 -2
- weasyprint/css/media_queries.py +3 -1
- weasyprint/css/properties.py +6 -2
- weasyprint/css/{utils.py → tokens.py} +306 -397
- weasyprint/css/units.py +91 -0
- weasyprint/css/validation/__init__.py +1 -1
- weasyprint/css/validation/descriptors.py +47 -19
- weasyprint/css/validation/expanders.py +7 -8
- weasyprint/css/validation/properties.py +341 -357
- weasyprint/document.py +20 -19
- weasyprint/draw/__init__.py +56 -63
- weasyprint/draw/border.py +121 -69
- weasyprint/draw/color.py +1 -1
- weasyprint/draw/text.py +60 -41
- weasyprint/formatting_structure/boxes.py +24 -5
- weasyprint/formatting_structure/build.py +33 -45
- weasyprint/images.py +76 -62
- weasyprint/layout/__init__.py +32 -26
- weasyprint/layout/absolute.py +7 -6
- weasyprint/layout/background.py +7 -7
- weasyprint/layout/block.py +195 -152
- weasyprint/layout/column.py +19 -24
- weasyprint/layout/flex.py +54 -26
- weasyprint/layout/float.py +12 -7
- weasyprint/layout/grid.py +284 -90
- weasyprint/layout/inline.py +121 -68
- weasyprint/layout/page.py +45 -12
- weasyprint/layout/percent.py +14 -10
- weasyprint/layout/preferred.py +105 -63
- weasyprint/layout/replaced.py +9 -6
- weasyprint/layout/table.py +16 -9
- weasyprint/pdf/__init__.py +58 -18
- weasyprint/pdf/anchors.py +3 -4
- weasyprint/pdf/fonts.py +126 -69
- weasyprint/pdf/metadata.py +36 -4
- weasyprint/pdf/pdfa.py +19 -3
- weasyprint/pdf/pdfua.py +7 -115
- weasyprint/pdf/pdfx.py +83 -0
- weasyprint/pdf/stream.py +57 -49
- weasyprint/pdf/tags.py +307 -0
- weasyprint/stacking.py +14 -15
- weasyprint/svg/__init__.py +59 -32
- weasyprint/svg/bounding_box.py +4 -2
- weasyprint/svg/defs.py +4 -9
- weasyprint/svg/images.py +11 -3
- weasyprint/svg/text.py +11 -2
- weasyprint/svg/utils.py +15 -8
- weasyprint/text/constants.py +1 -1
- weasyprint/text/ffi.py +4 -3
- weasyprint/text/fonts.py +13 -5
- weasyprint/text/line_break.py +146 -43
- weasyprint/urls.py +41 -13
- {weasyprint-65.1.dist-info → weasyprint-67.0.dist-info}/METADATA +5 -6
- weasyprint-67.0.dist-info/RECORD +77 -0
- weasyprint/draw/stack.py +0 -13
- weasyprint-65.1.dist-info/RECORD +0 -74
- {weasyprint-65.1.dist-info → weasyprint-67.0.dist-info}/WHEEL +0 -0
- {weasyprint-65.1.dist-info → weasyprint-67.0.dist-info}/entry_points.txt +0 -0
- {weasyprint-65.1.dist-info → weasyprint-67.0.dist-info}/licenses/LICENSE +0 -0
weasyprint/document.py
CHANGED
|
@@ -10,7 +10,7 @@ from .anchors import gather_anchors, make_page_bookmark_tree
|
|
|
10
10
|
from .css import get_all_computed_styles
|
|
11
11
|
from .css.counters import CounterStyle
|
|
12
12
|
from .css.targets import TargetCollector
|
|
13
|
-
from .draw import draw_page
|
|
13
|
+
from .draw import draw_page
|
|
14
14
|
from .formatting_structure.build import build_formatting_structure
|
|
15
15
|
from .html import get_html_metadata
|
|
16
16
|
from .images import get_image_from_uri as original_get_image_from_uri
|
|
@@ -82,7 +82,7 @@ class Page:
|
|
|
82
82
|
|
|
83
83
|
def paint(self, stream, scale=1):
|
|
84
84
|
"""Paint the page into the PDF file."""
|
|
85
|
-
with stacked(
|
|
85
|
+
with stream.stacked():
|
|
86
86
|
stream.transform(a=scale, d=scale)
|
|
87
87
|
draw_page(self._page_box, stream)
|
|
88
88
|
|
|
@@ -92,7 +92,7 @@ class DocumentMetadata:
|
|
|
92
92
|
|
|
93
93
|
New attributes may be added in future versions of WeasyPrint.
|
|
94
94
|
"""
|
|
95
|
-
def __init__(self, title=None, authors=
|
|
95
|
+
def __init__(self, title=None, authors=None, description=None, keywords=None,
|
|
96
96
|
generator=None, created=None, modified=None, attachments=None,
|
|
97
97
|
lang=None, custom=None, generate_rdf_metadata=generate_rdf_metadata):
|
|
98
98
|
#: The title of the document, as a string or :obj:`None`.
|
|
@@ -204,9 +204,11 @@ class Document:
|
|
|
204
204
|
"""
|
|
205
205
|
|
|
206
206
|
@classmethod
|
|
207
|
-
def _build_layout_context(cls, html, font_config, counter_style,
|
|
207
|
+
def _build_layout_context(cls, html, font_config, counter_style, color_profiles,
|
|
208
|
+
options):
|
|
208
209
|
target_collector = TargetCollector()
|
|
209
210
|
page_rules = []
|
|
211
|
+
layers = []
|
|
210
212
|
user_stylesheets = []
|
|
211
213
|
cache = options['cache']
|
|
212
214
|
if cache is None:
|
|
@@ -217,11 +219,12 @@ class Document:
|
|
|
217
219
|
if not hasattr(css, 'matcher'):
|
|
218
220
|
css = CSS(
|
|
219
221
|
guess=css, media_type=html.media_type,
|
|
220
|
-
font_config=font_config, counter_style=counter_style
|
|
222
|
+
font_config=font_config, counter_style=counter_style,
|
|
223
|
+
color_profiles=color_profiles)
|
|
221
224
|
user_stylesheets.append(css)
|
|
222
225
|
style_for = get_all_computed_styles(
|
|
223
|
-
html, user_stylesheets, options['presentational_hints'],
|
|
224
|
-
|
|
226
|
+
html, user_stylesheets, options['presentational_hints'], font_config,
|
|
227
|
+
counter_style, color_profiles, page_rules, layers, target_collector,
|
|
225
228
|
options['pdf_forms'])
|
|
226
229
|
get_image_from_uri = functools.partial(
|
|
227
230
|
original_get_image_from_uri, cache=cache,
|
|
@@ -233,15 +236,18 @@ class Document:
|
|
|
233
236
|
return context
|
|
234
237
|
|
|
235
238
|
@classmethod
|
|
236
|
-
def _render(cls, html, font_config, counter_style, options):
|
|
239
|
+
def _render(cls, html, font_config, counter_style, color_profiles, options):
|
|
237
240
|
if font_config is None:
|
|
238
241
|
font_config = FontConfiguration()
|
|
239
242
|
|
|
240
243
|
if counter_style is None:
|
|
241
244
|
counter_style = CounterStyle()
|
|
242
245
|
|
|
246
|
+
if color_profiles is None:
|
|
247
|
+
color_profiles = {}
|
|
248
|
+
|
|
243
249
|
context = cls._build_layout_context(
|
|
244
|
-
html, font_config, counter_style, options)
|
|
250
|
+
html, font_config, counter_style, color_profiles, options)
|
|
245
251
|
|
|
246
252
|
root_box = build_formatting_structure(
|
|
247
253
|
html.etree_element, context.style_for, context.get_image_from_uri,
|
|
@@ -252,11 +258,11 @@ class Document:
|
|
|
252
258
|
rendering = cls(
|
|
253
259
|
[Page(page_box) for page_box in page_boxes],
|
|
254
260
|
DocumentMetadata(**get_html_metadata(html)),
|
|
255
|
-
html.url_fetcher, font_config)
|
|
261
|
+
html.url_fetcher, font_config, color_profiles)
|
|
256
262
|
rendering._html = html
|
|
257
263
|
return rendering
|
|
258
264
|
|
|
259
|
-
def __init__(self, pages, metadata, url_fetcher, font_config):
|
|
265
|
+
def __init__(self, pages, metadata, url_fetcher, font_config, color_profiles):
|
|
260
266
|
#: A list of :class:`Page` objects.
|
|
261
267
|
self.pages = pages
|
|
262
268
|
#: A :class:`DocumentMetadata` object.
|
|
@@ -276,13 +282,7 @@ class Document:
|
|
|
276
282
|
# fonts that may be used when rendering
|
|
277
283
|
self.font_config = font_config
|
|
278
284
|
|
|
279
|
-
|
|
280
|
-
if etree_element is None:
|
|
281
|
-
etree_element = self._html.etree_element
|
|
282
|
-
structure[etree_element] = {'parent': None}
|
|
283
|
-
for child in etree_element:
|
|
284
|
-
structure[child] = {'parent': etree_element}
|
|
285
|
-
self.build_element_structure(structure, child)
|
|
285
|
+
self.color_profiles = color_profiles
|
|
286
286
|
|
|
287
287
|
def copy(self, pages='all'):
|
|
288
288
|
"""Take a subset of the pages.
|
|
@@ -314,7 +314,8 @@ class Document:
|
|
|
314
314
|
elif not isinstance(pages, list):
|
|
315
315
|
pages = list(pages)
|
|
316
316
|
return type(self)(
|
|
317
|
-
pages, self.metadata, self.url_fetcher, self.font_config
|
|
317
|
+
pages, self.metadata, self.url_fetcher, self.font_config,
|
|
318
|
+
self.color_profiles)
|
|
318
319
|
|
|
319
320
|
def make_bookmark_tree(self, scale=1, transform_pages=False):
|
|
320
321
|
"""Make a tree of all bookmarks in the document.
|
weasyprint/draw/__init__.py
CHANGED
|
@@ -12,7 +12,6 @@ from ..matrix import Matrix
|
|
|
12
12
|
from ..stacking import StackingContext
|
|
13
13
|
from .border import draw_border, draw_line, draw_outline, rounded_box, set_mask_border
|
|
14
14
|
from .color import styled_color
|
|
15
|
-
from .stack import stacked
|
|
16
15
|
from .text import draw_text
|
|
17
16
|
|
|
18
17
|
|
|
@@ -32,11 +31,9 @@ def draw_page(page, stream):
|
|
|
32
31
|
def draw_stacking_context(stream, stacking_context):
|
|
33
32
|
"""Draw a ``stacking_context`` on ``stream``."""
|
|
34
33
|
# See https://www.w3.org/TR/CSS2/zindex.html.
|
|
35
|
-
with stacked(
|
|
34
|
+
with stream.stacked():
|
|
36
35
|
box = stacking_context.box
|
|
37
36
|
|
|
38
|
-
stream.begin_marked_content(box, mcid=True)
|
|
39
|
-
|
|
40
37
|
# Apply the viewport_overflow to the html box, see #35.
|
|
41
38
|
if box.is_for_root_element and (
|
|
42
39
|
stacking_context.page.style['overflow'] != 'visible'):
|
|
@@ -68,21 +65,20 @@ def draw_stacking_context(stream, stacking_context):
|
|
|
68
65
|
if box.transformation_matrix.determinant:
|
|
69
66
|
stream.transform(*box.transformation_matrix.values)
|
|
70
67
|
else:
|
|
71
|
-
stream.end_marked_content()
|
|
72
68
|
return
|
|
73
69
|
|
|
74
70
|
# Point 1 is done in draw_page.
|
|
75
71
|
|
|
76
72
|
# Point 2.
|
|
77
|
-
if isinstance(box, (boxes.BlockBox, boxes.MarginBox,
|
|
78
|
-
boxes.
|
|
79
|
-
boxes.
|
|
73
|
+
if isinstance(box, (boxes.BlockBox, boxes.MarginBox, boxes.InlineBlockBox,
|
|
74
|
+
boxes.TableCellBox, boxes.FlexContainerBox,
|
|
75
|
+
boxes.GridContainerBox, boxes.ReplacedBox)):
|
|
80
76
|
set_mask_border(stream, box)
|
|
81
|
-
# The canvas background was removed by layout_backgrounds
|
|
77
|
+
# The canvas background was removed by layout_backgrounds.
|
|
82
78
|
draw_background(stream, box.background)
|
|
83
79
|
draw_border(stream, box)
|
|
84
80
|
|
|
85
|
-
with stacked(
|
|
81
|
+
with stream.stacked():
|
|
86
82
|
# Dont clip the page box, see #35.
|
|
87
83
|
clip = (
|
|
88
84
|
box.style['overflow'] != 'visible' and
|
|
@@ -118,17 +114,8 @@ def draw_stacking_context(stream, stacking_context):
|
|
|
118
114
|
draw_inline_level(stream, stacking_context.page, box)
|
|
119
115
|
|
|
120
116
|
# Point 7.
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
draw_replacedbox(stream, block)
|
|
124
|
-
elif block.children:
|
|
125
|
-
if block != box:
|
|
126
|
-
stream.begin_marked_content(block, mcid=True)
|
|
127
|
-
if isinstance(block.children[-1], boxes.LineBox):
|
|
128
|
-
for child in block.children:
|
|
129
|
-
draw_inline_level(stream, stacking_context.page, child)
|
|
130
|
-
if block != box:
|
|
131
|
-
stream.end_marked_content()
|
|
117
|
+
draw_block_level(
|
|
118
|
+
stacking_context.page, stream, {box: stacking_context.blocks_and_cells})
|
|
132
119
|
|
|
133
120
|
# Point 8.
|
|
134
121
|
for child_context in stacking_context.zero_z_contexts:
|
|
@@ -144,12 +131,10 @@ def draw_stacking_context(stream, stacking_context):
|
|
|
144
131
|
if box.style['opacity'] < 1:
|
|
145
132
|
group_id = stream.id
|
|
146
133
|
stream = original_stream
|
|
147
|
-
with stacked(
|
|
134
|
+
with stream.stacked():
|
|
148
135
|
stream.set_alpha(box.style['opacity'], stroke=True, fill=True)
|
|
149
136
|
stream.draw_x_object(group_id)
|
|
150
137
|
|
|
151
|
-
stream.end_marked_content()
|
|
152
|
-
|
|
153
138
|
|
|
154
139
|
def draw_background(stream, bg, clip_box=True, bleed=None, marks=()):
|
|
155
140
|
"""Draw the background color and image to a ``pdf.stream.Stream``.
|
|
@@ -161,7 +146,7 @@ def draw_background(stream, bg, clip_box=True, bleed=None, marks=()):
|
|
|
161
146
|
if bg is None:
|
|
162
147
|
return
|
|
163
148
|
|
|
164
|
-
with stacked(
|
|
149
|
+
with stream.stacked():
|
|
165
150
|
if clip_box:
|
|
166
151
|
for box in bg.layers[-1].clipped_boxes:
|
|
167
152
|
rounded_box(stream, box)
|
|
@@ -170,7 +155,7 @@ def draw_background(stream, bg, clip_box=True, bleed=None, marks=()):
|
|
|
170
155
|
|
|
171
156
|
# Draw background color.
|
|
172
157
|
if bg.color.alpha > 0:
|
|
173
|
-
with stacked(
|
|
158
|
+
with stream.artifact(), stream.stacked():
|
|
174
159
|
stream.set_color(bg.color)
|
|
175
160
|
painting_area = bg.layers[-1].painting_area
|
|
176
161
|
stream.rectangle(*painting_area)
|
|
@@ -232,7 +217,7 @@ def draw_background(stream, bg, clip_box=True, bleed=None, marks=()):
|
|
|
232
217
|
'''
|
|
233
218
|
svg += '</svg>'
|
|
234
219
|
tree = ElementTree.fromstring(svg)
|
|
235
|
-
image = SVGImage(tree, None, None,
|
|
220
|
+
image = SVGImage(tree, None, None, None)
|
|
236
221
|
# Painting area is the PDF media box
|
|
237
222
|
size = (width, height)
|
|
238
223
|
position = (x, y)
|
|
@@ -247,10 +232,10 @@ def draw_background(stream, bg, clip_box=True, bleed=None, marks=()):
|
|
|
247
232
|
bg.layers.insert(0, layer)
|
|
248
233
|
# Paint in reversed order: first layer is "closest" to the viewer.
|
|
249
234
|
for layer in reversed(bg.layers):
|
|
250
|
-
draw_background_image(stream, layer, bg.
|
|
235
|
+
draw_background_image(stream, layer, bg.style)
|
|
251
236
|
|
|
252
237
|
|
|
253
|
-
def draw_background_image(stream, layer,
|
|
238
|
+
def draw_background_image(stream, layer, style):
|
|
254
239
|
if layer.image is None or 0 in layer.size:
|
|
255
240
|
return
|
|
256
241
|
|
|
@@ -262,21 +247,21 @@ def draw_background_image(stream, layer, image_rendering):
|
|
|
262
247
|
image_width, image_height = layer.size
|
|
263
248
|
|
|
264
249
|
if repeat_x == 'no-repeat' and repeat_y == 'no-repeat':
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
250
|
+
with stream.artifact():
|
|
251
|
+
# We don't use a pattern when we don't need to because some viewers
|
|
252
|
+
# (e.g., Preview on Mac) introduce unnecessary pixelation when vector
|
|
253
|
+
# images are used in patterns.
|
|
254
|
+
if not layer.unbounded:
|
|
255
|
+
stream.rectangle(
|
|
256
|
+
painting_x, painting_y, painting_width, painting_height)
|
|
257
|
+
stream.clip()
|
|
258
|
+
stream.end()
|
|
259
|
+
# Put the image in a group so that masking outside the image and
|
|
260
|
+
# masking within the image don't conflict.
|
|
261
|
+
group = stream.add_group(*stream.page_rectangle)
|
|
262
|
+
group.transform(e=position_x + positioning_x, f=position_y + positioning_y)
|
|
263
|
+
layer.image.draw(group, image_width, image_height, style)
|
|
264
|
+
stream.draw_x_object(group.id)
|
|
280
265
|
return
|
|
281
266
|
|
|
282
267
|
if repeat_x == 'no-repeat':
|
|
@@ -322,9 +307,10 @@ def draw_background_image(stream, layer, image_rendering):
|
|
|
322
307
|
0, 0, image_width, image_height, repeat_width, repeat_height, matrix)
|
|
323
308
|
group = pattern.add_group(0, 0, repeat_width, repeat_height)
|
|
324
309
|
|
|
325
|
-
with stacked(
|
|
326
|
-
layer.image.draw(group, image_width, image_height,
|
|
327
|
-
pattern.
|
|
310
|
+
with stream.artifact(), stream.stacked():
|
|
311
|
+
layer.image.draw(group, image_width, image_height, style)
|
|
312
|
+
with pattern.artifact():
|
|
313
|
+
pattern.draw_x_object(group.id)
|
|
328
314
|
stream.set_color_space('Pattern')
|
|
329
315
|
stream.set_color_special(pattern.id)
|
|
330
316
|
if layer.unbounded:
|
|
@@ -476,9 +462,9 @@ def draw_collapsed_borders(stream, table):
|
|
|
476
462
|
|
|
477
463
|
for segment in segments:
|
|
478
464
|
_, style, width, color, side, border_box = segment
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
465
|
+
bx, by, bw, bh = border_box
|
|
466
|
+
color = styled_color(style, color, side)
|
|
467
|
+
with stream.artifact(), stream.stacked():
|
|
482
468
|
draw_line(stream, bx, by, bx + bw, by + bh, width, style, color)
|
|
483
469
|
|
|
484
470
|
|
|
@@ -491,14 +477,13 @@ def draw_replacedbox(stream, box):
|
|
|
491
477
|
if draw_width <= 0 or draw_height <= 0:
|
|
492
478
|
return
|
|
493
479
|
|
|
494
|
-
with stacked(
|
|
480
|
+
with stream.stacked():
|
|
495
481
|
stream.set_alpha(1)
|
|
496
482
|
stream.transform(e=draw_x, f=draw_y)
|
|
497
|
-
with stacked(
|
|
483
|
+
with stream.stacked():
|
|
498
484
|
# TODO: Use the real intrinsic size here, not affected by
|
|
499
485
|
# 'image-resolution'?
|
|
500
|
-
box.replacement.draw(
|
|
501
|
-
stream, draw_width, draw_height, box.style['image_rendering'])
|
|
486
|
+
box.replacement.draw(stream, draw_width, draw_height, box.style)
|
|
502
487
|
|
|
503
488
|
|
|
504
489
|
def draw_inline_level(stream, page, box, offset_x=0, text_overflow='clip',
|
|
@@ -513,15 +498,10 @@ def draw_inline_level(stream, page, box, offset_x=0, text_overflow='clip',
|
|
|
513
498
|
draw_background(stream, box.background)
|
|
514
499
|
draw_border(stream, box)
|
|
515
500
|
if isinstance(box, (boxes.InlineBox, boxes.LineBox)):
|
|
516
|
-
link_annotation = None
|
|
517
501
|
if isinstance(box, boxes.LineBox):
|
|
518
502
|
text_overflow = box.text_overflow
|
|
519
503
|
block_ellipsis = box.block_ellipsis
|
|
520
|
-
else:
|
|
521
|
-
link_annotation = box.link_annotation
|
|
522
504
|
ellipsis = 'none'
|
|
523
|
-
if link_annotation:
|
|
524
|
-
stream.begin_marked_content(box, mcid=True, tag='Link')
|
|
525
505
|
for i, child in enumerate(box.children):
|
|
526
506
|
if i == len(box.children) - 1:
|
|
527
507
|
# Last child
|
|
@@ -531,15 +511,28 @@ def draw_inline_level(stream, page, box, offset_x=0, text_overflow='clip',
|
|
|
531
511
|
else:
|
|
532
512
|
child_offset_x = offset_x + child.position_x - box.position_x
|
|
533
513
|
if isinstance(child, boxes.TextBox):
|
|
534
|
-
|
|
514
|
+
with stream.marked(child, 'Span'):
|
|
515
|
+
draw_text(
|
|
516
|
+
stream, child, child_offset_x, text_overflow, ellipsis)
|
|
535
517
|
else:
|
|
536
518
|
draw_inline_level(
|
|
537
519
|
stream, page, child, child_offset_x, text_overflow, ellipsis)
|
|
538
|
-
if link_annotation:
|
|
539
|
-
stream.end_marked_content()
|
|
540
520
|
elif isinstance(box, boxes.InlineReplacedBox):
|
|
541
|
-
|
|
521
|
+
with stream.marked(box, 'Figure'):
|
|
522
|
+
draw_replacedbox(stream, box)
|
|
542
523
|
else:
|
|
543
524
|
assert isinstance(box, boxes.TextBox)
|
|
544
525
|
# Should only happen for list markers.
|
|
545
526
|
draw_text(stream, box, offset_x, text_overflow)
|
|
527
|
+
|
|
528
|
+
|
|
529
|
+
def draw_block_level(page, stream, blocks_and_cells):
|
|
530
|
+
for block, blocks_and_cells in blocks_and_cells.items():
|
|
531
|
+
if isinstance(block, boxes.ReplacedBox):
|
|
532
|
+
with stream.marked(block, 'Figure'):
|
|
533
|
+
draw_replacedbox(stream, block)
|
|
534
|
+
elif block.children:
|
|
535
|
+
if isinstance(block.children[-1], boxes.LineBox):
|
|
536
|
+
for child in block.children:
|
|
537
|
+
draw_inline_level(stream, page, child)
|
|
538
|
+
draw_block_level(page, stream, blocks_and_cells)
|