From afc7dabc1b550b7ef890dac9d6e130c4d892ee91 Mon Sep 17 00:00:00 2001 From: Raphael Gaschignard Date: Tue, 23 Jul 2019 14:12:08 +0900 Subject: [PATCH 1/3] Refactor LayoutContext building this will provide a simpler API for testing context-using functions inside tests --- weasyprint/document.py | 33 ++++++++++++++++++++++++++------- weasyprint/layout/__init__.py | 10 +++------- weasyprint/layout/pages.py | 9 +++++---- 3 files changed, 34 insertions(+), 18 deletions(-) diff --git a/weasyprint/document.py b/weasyprint/document.py index 6037f3f5f..082ed9aee 100644 --- a/weasyprint/document.py +++ b/weasyprint/document.py @@ -15,6 +15,7 @@ import warnings import cairocffi as cairo +from weasyprint.layout import LayoutContext from . import CSS from .css import get_all_computed_styles @@ -347,9 +348,11 @@ class Document(object): `. """ + @classmethod - def _render(cls, html, stylesheets, enable_hinting, - presentational_hints=False, font_config=None): + def _build_layout_context(cls, html, stylesheets, enable_hinting, + presentational_hints=False, + font_config=None): if font_config is None: font_config = FontConfiguration() target_collector = TargetCollector() @@ -367,12 +370,28 @@ def _render(cls, html, stylesheets, enable_hinting, get_image_from_uri = functools.partial( original_get_image_from_uri, {}, html.url_fetcher) PROGRESS_LOGGER.info('Step 4 - Creating formatting structure') + context = LayoutContext( + enable_hinting, style_for, get_image_from_uri, font_config, target_collector + ) + return context + + @classmethod + def _render(cls, html, stylesheets, enable_hinting, + presentational_hints=False, font_config=None): + if font_config is None: + font_config = FontConfiguration() + + context = cls._build_layout_context( + html, stylesheets, enable_hinting, presentational_hints, font_config + ) + root_box = build_formatting_structure( - html.etree_element, style_for, get_image_from_uri, - html.base_url, target_collector) - page_boxes = layout_document( - enable_hinting, style_for, get_image_from_uri, root_box, - font_config, html, target_collector) + html.etree_element, + context.style_for, context.get_image_from_uri, + html.base_url, context.target_collector + ) + + page_boxes = layout_document(html, root_box, context) rendering = cls( [Page(page_box, enable_hinting) for page_box in page_boxes], DocumentMetadata(**html._get_metadata()), diff --git a/weasyprint/layout/__init__.py b/weasyprint/layout/__init__.py index 0b7786dae..a456a0de1 100644 --- a/weasyprint/layout/__init__.py +++ b/weasyprint/layout/__init__.py @@ -101,8 +101,7 @@ def layout_fixed_boxes(context, pages, containing_page): absolute_boxes = new_absolute_boxes -def layout_document(enable_hinting, style_for, get_image_from_uri, root_box, - font_config, html, target_collector, max_loops=8): +def layout_document(html, root_box, context, max_loops=8): """Lay out the whole document. This includes line breaks, page breaks, absolute size and position for all @@ -114,9 +113,6 @@ def layout_document(enable_hinting, style_for, get_image_from_uri, root_box, :returns: a list of laid out Page objects. """ - context = LayoutContext( - enable_hinting, style_for, get_image_from_uri, font_config, - target_collector) initialize_page_maker(context, root_box) pages = [] actual_total_pages = 0 @@ -127,7 +123,7 @@ def layout_document(enable_hinting, style_for, get_image_from_uri, root_box, 'Step 5 - Creating layout - Repagination #%i' % loop) initial_total_pages = actual_total_pages - pages = list(make_all_pages(context, root_box, html, pages, style_for)) + pages = list(make_all_pages(context, root_box, html, pages)) actual_total_pages = len(pages) # Check whether another round is required @@ -185,7 +181,7 @@ def layout_document(enable_hinting, style_for, get_image_from_uri, root_box, state = context.page_maker[context.current_page][3] page.children = (root,) + tuple( make_margin_boxes(context, page, state)) - layout_backgrounds(page, get_image_from_uri) + layout_backgrounds(page, context.get_image_from_uri) yield page diff --git a/weasyprint/layout/pages.py b/weasyprint/layout/pages.py index e621a01bc..00a1d79b7 100644 --- a/weasyprint/layout/pages.py +++ b/weasyprint/layout/pages.py @@ -694,7 +694,7 @@ def set_page_type_computed_styles(page_type, html, style_for): base_url=html.base_url) -def remake_page(index, context, root_box, html, style_for): +def remake_page(index, context, root_box, html): """Return one laid out page without margin boxes. Start with the initial values from ``context.page_maker[index]``. @@ -723,7 +723,7 @@ def remake_page(index, context, root_box, html, style_for): next_page_name = '' side = 'right' if right_page else 'left' page_type = PageType(side, blank, first, index, name=next_page_name) - set_page_type_computed_styles(page_type, html, style_for) + set_page_type_computed_styles(page_type, html, context.style_for) context.forced_break = ( initial_next_page['break'] != 'any' or initial_next_page['page']) @@ -776,7 +776,7 @@ def remake_page(index, context, root_box, html, style_for): return page, resume_at -def make_all_pages(context, root_box, html, pages, style_for): +def make_all_pages(context, root_box, html, pages): """Return a list of laid out pages without margin boxes. Re-make pages only if necessary. @@ -795,7 +795,8 @@ def make_all_pages(context, root_box, html, pages, style_for): remake_state['anchors'] = [] remake_state['content_lookups'] = [] page, resume_at = remake_page( - i, context, root_box, html, style_for) + i, context, root_box, html + ) yield page else: PROGRESS_LOGGER.info( From 620e0754f4802b4a2c6830807143a5475663661a Mon Sep 17 00:00:00 2001 From: Guillaume Ayoub Date: Tue, 23 Jul 2019 18:07:14 +0200 Subject: [PATCH 2/3] Fix lint --- weasyprint/document.py | 17 +++++++---------- weasyprint/layout/pages.py | 4 +--- 2 files changed, 8 insertions(+), 13 deletions(-) diff --git a/weasyprint/document.py b/weasyprint/document.py index 082ed9aee..aa10c7e3c 100644 --- a/weasyprint/document.py +++ b/weasyprint/document.py @@ -351,8 +351,7 @@ class Document(object): @classmethod def _build_layout_context(cls, html, stylesheets, enable_hinting, - presentational_hints=False, - font_config=None): + presentational_hints=False, font_config=None): if font_config is None: font_config = FontConfiguration() target_collector = TargetCollector() @@ -371,8 +370,8 @@ def _build_layout_context(cls, html, stylesheets, enable_hinting, original_get_image_from_uri, {}, html.url_fetcher) PROGRESS_LOGGER.info('Step 4 - Creating formatting structure') context = LayoutContext( - enable_hinting, style_for, get_image_from_uri, font_config, target_collector - ) + enable_hinting, style_for, get_image_from_uri, font_config, + target_collector) return context @classmethod @@ -382,14 +381,12 @@ def _render(cls, html, stylesheets, enable_hinting, font_config = FontConfiguration() context = cls._build_layout_context( - html, stylesheets, enable_hinting, presentational_hints, font_config - ) + html, stylesheets, enable_hinting, presentational_hints, + font_config) root_box = build_formatting_structure( - html.etree_element, - context.style_for, context.get_image_from_uri, - html.base_url, context.target_collector - ) + html.etree_element, context.style_for, context.get_image_from_uri, + html.base_url, context.target_collector) page_boxes = layout_document(html, root_box, context) rendering = cls( diff --git a/weasyprint/layout/pages.py b/weasyprint/layout/pages.py index 3ace8f932..65a5492a4 100644 --- a/weasyprint/layout/pages.py +++ b/weasyprint/layout/pages.py @@ -801,9 +801,7 @@ def make_all_pages(context, root_box, html, pages): remake_state['pages_wanted'] = False remake_state['anchors'] = [] remake_state['content_lookups'] = [] - page, resume_at = remake_page( - i, context, root_box, html - ) + page, resume_at = remake_page(i, context, root_box, html) yield page else: PROGRESS_LOGGER.info( From 8d10c011d9d5f6bcd474d76feadf3c1832f87d48 Mon Sep 17 00:00:00 2001 From: Guillaume Ayoub Date: Wed, 24 Jul 2019 10:47:55 +0200 Subject: [PATCH 3/3] Don't respect table cell width when content doesn't fit Fix #906. --- weasyprint/layout/preferred.py | 49 +++++++++++++++++----- weasyprint/tests/test_layout/test_table.py | 29 +++++++++++++ 2 files changed, 67 insertions(+), 11 deletions(-) diff --git a/weasyprint/layout/preferred.py b/weasyprint/layout/preferred.py index 97ec265a9..a43ac1afb 100644 --- a/weasyprint/layout/preferred.py +++ b/weasyprint/layout/preferred.py @@ -42,12 +42,13 @@ def min_content_width(context, box, outer=True): This is the width by breaking at every line-break opportunity. """ - if isinstance(box, ( + if box.is_table_wrapper: + return table_and_columns_preferred_widths(context, box, outer)[0] + elif isinstance(box, boxes.TableCellBox): + return table_cell_min_content_width(context, box, outer) + elif isinstance(box, ( boxes.BlockContainerBox, boxes.TableColumnBox, boxes.FlexBox)): - if box.is_table_wrapper: - return table_and_columns_preferred_widths(context, box, outer)[0] - else: - return block_min_content_width(context, box, outer) + return block_min_content_width(context, box, outer) elif isinstance(box, boxes.TableColumnGroupBox): return column_group_content_width(context, box) elif isinstance(box, (boxes.InlineBox, boxes.LineBox)): @@ -69,12 +70,13 @@ def max_content_width(context, box, outer=True): This is the width by only breaking at forced line breaks. """ - if isinstance(box, ( + if box.is_table_wrapper: + return table_and_columns_preferred_widths(context, box, outer)[1] + elif isinstance(box, boxes.TableCellBox): + return table_cell_max_content_width(context, box, outer) + elif isinstance(box, ( boxes.BlockContainerBox, boxes.TableColumnBox, boxes.FlexBox)): - if box.is_table_wrapper: - return table_and_columns_preferred_widths(context, box, outer)[1] - else: - return block_max_content_width(context, box, outer) + return block_max_content_width(context, box, outer) elif isinstance(box, boxes.TableColumnGroupBox): return column_group_content_width(context, box) elif isinstance(box, (boxes.InlineBox, boxes.LineBox)): @@ -206,7 +208,7 @@ def inline_max_content_width(context, box, outer=True, is_line_start=False): def column_group_content_width(context, box): - """Return the *-content width for an ``TableColumnGroupBox``.""" + """Return the *-content width for a ``TableColumnGroupBox``.""" width = box.style['width'] if width == 'auto' or width.unit == '%': width = 0 @@ -217,6 +219,31 @@ def column_group_content_width(context, box): return adjust(box, False, width) +def table_cell_min_content_width(context, box, outer): + """Return the min-content width for a ``TableCellBox``.""" + children_widths = [ + min_content_width(context, child, outer=True) + for child in box.children + if not child.is_absolutely_positioned()] + children_min_width = margin_width( + box, max(children_widths) if children_widths else 0) + + width = box.style['width'] + if width != 'auto' and width.unit == 'px': + cell_min_width = adjust(box, outer, width.value) + else: + cell_min_width = 0 + + return max(children_min_width, cell_min_width) + + +def table_cell_max_content_width(context, box, outer): + """Return the max-content width for a ``TableCellBox``.""" + return max( + table_cell_min_content_width(context, box, outer), + block_max_content_width(context, box, outer)) + + def inline_line_widths(context, box, outer, is_line_start, minimum, skip_stack=None, first_line=False): if box.style['text_indent'].unit == '%': diff --git a/weasyprint/tests/test_layout/test_table.py b/weasyprint/tests/test_layout/test_table.py index 8519ed04b..66093a8b0 100644 --- a/weasyprint/tests/test_layout/test_table.py +++ b/weasyprint/tests/test_layout/test_table.py @@ -382,6 +382,35 @@ def test_layout_table_auto_4(): assert table.width == 27 # 3 * spacing + 4 + 4 + 2 * b1 + 2 * b2 +@assert_no_logs +def test_layout_table_auto_5(): + page, = render_pages(''' + + + + + + + +
aa aa aa aaaaaaaaaaaaaThis will take the rest of the width
+ ''') + html, = page.children + body, = html.children + table_wrapper, = body.children + table, = table_wrapper.children + row_group, = table.children + row, = row_group.children + td_1, td_2, td_3 = row.children + + assert table.width == 1000 + assert td_1.width == 40 + assert td_2.width == 11 * 16 + assert td_3.width == 1000 - 40 - 11 * 16 + + @assert_no_logs def test_layout_table_auto_6(): page, = render_pages('''