Tables now use ""dynamic page" allowing the contents to be anything that can be rendered ion a page.
This commit is contained in:
@@ -0,0 +1,313 @@
|
||||
"""
|
||||
Unit tests for DynamicPage class.
|
||||
"""
|
||||
|
||||
import pytest
|
||||
from PIL import Image
|
||||
from pyWebLayout.concrete.dynamic_page import DynamicPage, SizeConstraints
|
||||
from pyWebLayout.concrete.text import Line, Text
|
||||
from pyWebLayout.style.fonts import Font
|
||||
from pyWebLayout.style.page_style import PageStyle
|
||||
from pyWebLayout.style import Alignment
|
||||
|
||||
|
||||
class TestSizeConstraints:
|
||||
"""Test SizeConstraints dataclass."""
|
||||
|
||||
def test_default_constraints(self):
|
||||
"""Test default constraint values."""
|
||||
constraints = SizeConstraints()
|
||||
assert constraints.min_width is None
|
||||
assert constraints.max_width is None
|
||||
assert constraints.min_height is None
|
||||
assert constraints.max_height is None
|
||||
|
||||
def test_custom_constraints(self):
|
||||
"""Test custom constraint values."""
|
||||
constraints = SizeConstraints(
|
||||
min_width=100,
|
||||
max_width=500,
|
||||
min_height=50,
|
||||
max_height=1000
|
||||
)
|
||||
assert constraints.min_width == 100
|
||||
assert constraints.max_width == 500
|
||||
assert constraints.min_height == 50
|
||||
assert constraints.max_height == 1000
|
||||
|
||||
|
||||
class TestDynamicPage:
|
||||
"""Test DynamicPage class."""
|
||||
|
||||
def test_initialization(self):
|
||||
"""Test DynamicPage initialization."""
|
||||
page = DynamicPage()
|
||||
|
||||
assert page.size == (0, 0) # Starts with zero size
|
||||
assert not page._is_measured
|
||||
assert not page._is_laid_out
|
||||
assert page._render_offset == 0
|
||||
assert page.constraints is not None
|
||||
|
||||
def test_initialization_with_constraints(self):
|
||||
"""Test initialization with custom constraints."""
|
||||
constraints = SizeConstraints(min_width=200, max_width=800)
|
||||
page = DynamicPage(constraints=constraints)
|
||||
|
||||
assert page.constraints.min_width == 200
|
||||
assert page.constraints.max_width == 800
|
||||
|
||||
def test_initialization_with_style(self):
|
||||
"""Test initialization with custom style."""
|
||||
style = PageStyle(border_width=2, padding=(10, 20, 10, 20))
|
||||
page = DynamicPage(style=style)
|
||||
|
||||
assert page.style.border_width == 2
|
||||
assert page.style.padding_top == 10
|
||||
|
||||
def test_measure_empty_page(self):
|
||||
"""Test measuring an empty page."""
|
||||
page = DynamicPage()
|
||||
width, height = page.measure()
|
||||
|
||||
# Empty page should have minimal size (just padding/borders)
|
||||
assert width > 0 # At least padding/borders
|
||||
assert height > 0
|
||||
assert page._is_measured
|
||||
|
||||
def test_measure_with_constraints(self):
|
||||
"""Test measuring respects constraints."""
|
||||
constraints = SizeConstraints(min_width=300, min_height=200)
|
||||
page = DynamicPage(constraints=constraints)
|
||||
|
||||
width, height = page.measure()
|
||||
|
||||
assert width >= 300
|
||||
assert height >= 200
|
||||
|
||||
def test_measure_caching(self):
|
||||
"""Test that measurement is cached."""
|
||||
page = DynamicPage()
|
||||
|
||||
# First measurement
|
||||
size1 = page.measure()
|
||||
|
||||
# Second measurement should return cached value
|
||||
size2 = page.measure()
|
||||
|
||||
assert size1 == size2
|
||||
assert page._is_measured
|
||||
|
||||
def test_get_min_width(self):
|
||||
"""Test get_min_width."""
|
||||
page = DynamicPage()
|
||||
min_width = page.get_min_width()
|
||||
|
||||
assert min_width > 0
|
||||
assert isinstance(min_width, int)
|
||||
|
||||
def test_get_preferred_width(self):
|
||||
"""Test get_preferred_width."""
|
||||
page = DynamicPage()
|
||||
pref_width = page.get_preferred_width()
|
||||
|
||||
assert pref_width > 0
|
||||
assert isinstance(pref_width, int)
|
||||
|
||||
def test_measure_content_height(self):
|
||||
"""Test measure_content_height."""
|
||||
page = DynamicPage()
|
||||
content_height = page.measure_content_height()
|
||||
|
||||
assert content_height > 0
|
||||
assert isinstance(content_height, int)
|
||||
|
||||
def test_layout(self):
|
||||
"""Test layout method."""
|
||||
page = DynamicPage()
|
||||
target_size = (400, 600)
|
||||
|
||||
page.layout(target_size)
|
||||
|
||||
assert page.size == target_size
|
||||
assert page._is_laid_out
|
||||
assert page._dirty # Should be marked for re-render
|
||||
|
||||
def test_render_without_layout(self):
|
||||
"""Test rendering without explicit layout (auto-sizing)."""
|
||||
page = DynamicPage()
|
||||
image = page.render()
|
||||
|
||||
assert isinstance(image, Image.Image)
|
||||
assert image.size[0] > 0
|
||||
assert image.size[1] > 0
|
||||
|
||||
def test_render_with_layout(self):
|
||||
"""Test rendering after explicit layout."""
|
||||
page = DynamicPage()
|
||||
page.layout((500, 700))
|
||||
|
||||
image = page.render()
|
||||
|
||||
assert isinstance(image, Image.Image)
|
||||
assert image.size == (500, 700)
|
||||
|
||||
def test_add_child_invalidates_cache(self):
|
||||
"""Test that adding a child invalidates measurement caches."""
|
||||
page = DynamicPage()
|
||||
|
||||
# Measure to populate cache
|
||||
page.measure()
|
||||
assert page._is_measured
|
||||
|
||||
# Add a child (mock renderable)
|
||||
class MockRenderable:
|
||||
def __init__(self):
|
||||
self.size = (100, 50)
|
||||
self._origin = (0, 0)
|
||||
|
||||
@property
|
||||
def origin(self):
|
||||
return self._origin
|
||||
|
||||
def render(self):
|
||||
pass
|
||||
|
||||
page.add_child(MockRenderable())
|
||||
|
||||
# Caches should be invalidated
|
||||
assert not page._is_measured
|
||||
assert page._intrinsic_size is None
|
||||
|
||||
def test_clear_children_invalidates_cache(self):
|
||||
"""Test that clearing children invalidates caches."""
|
||||
page = DynamicPage()
|
||||
|
||||
# Measure to populate cache
|
||||
page.measure()
|
||||
assert page._is_measured
|
||||
|
||||
# Clear children
|
||||
page.clear_children()
|
||||
|
||||
# Caches should be invalidated
|
||||
assert not page._is_measured
|
||||
|
||||
def test_pagination_reset(self):
|
||||
"""Test pagination reset."""
|
||||
page = DynamicPage()
|
||||
page._render_offset = 100
|
||||
|
||||
page.reset_pagination()
|
||||
|
||||
assert page._render_offset == 0
|
||||
|
||||
def test_has_more_content_false(self):
|
||||
"""Test has_more_content when all content is rendered."""
|
||||
page = DynamicPage()
|
||||
|
||||
# Set render offset to total height
|
||||
total_height = page.measure_content_height()
|
||||
page._render_offset = total_height
|
||||
|
||||
assert not page.has_more_content()
|
||||
|
||||
def test_has_more_content_true(self):
|
||||
"""Test has_more_content when content remains."""
|
||||
page = DynamicPage()
|
||||
|
||||
# Offset is less than total
|
||||
page._render_offset = 0
|
||||
|
||||
assert page.has_more_content()
|
||||
|
||||
def test_min_width_measurement(self):
|
||||
"""Test min width measures longest word."""
|
||||
page = DynamicPage()
|
||||
|
||||
# Min width should be at least padding/borders
|
||||
min_width = page.get_min_width()
|
||||
assert min_width > 0
|
||||
|
||||
def test_invalidate_caches(self):
|
||||
"""Test cache invalidation."""
|
||||
page = DynamicPage()
|
||||
|
||||
# Populate caches
|
||||
page.measure()
|
||||
page.get_min_width()
|
||||
page.get_preferred_width()
|
||||
page.measure_content_height()
|
||||
|
||||
assert page._is_measured
|
||||
assert page._intrinsic_size is not None
|
||||
assert page._min_width_cache is not None
|
||||
assert page._preferred_width_cache is not None
|
||||
assert page._content_height_cache is not None
|
||||
|
||||
# Invalidate
|
||||
page.invalidate_caches()
|
||||
|
||||
assert not page._is_measured
|
||||
assert page._intrinsic_size is None
|
||||
assert page._min_width_cache is None
|
||||
assert page._preferred_width_cache is None
|
||||
assert page._content_height_cache is None
|
||||
assert not page._is_laid_out
|
||||
|
||||
def test_measure_with_available_width(self):
|
||||
"""Test measurement with available_width constraint."""
|
||||
page = DynamicPage()
|
||||
|
||||
width, height = page.measure(available_width=300)
|
||||
|
||||
# Width should respect available_width
|
||||
assert width <= 300
|
||||
|
||||
def test_constraints_override_available_width(self):
|
||||
"""Test that constraints override available_width."""
|
||||
constraints = SizeConstraints(min_width=400)
|
||||
page = DynamicPage(constraints=constraints)
|
||||
|
||||
width, height = page.measure(available_width=300)
|
||||
|
||||
# Should use min_width constraint, not available_width
|
||||
assert width >= 400
|
||||
|
||||
def test_render_partial_empty_page(self):
|
||||
"""Test partial rendering on empty page."""
|
||||
page = DynamicPage()
|
||||
|
||||
rendered = page.render_partial(available_height=100)
|
||||
|
||||
assert rendered >= 0
|
||||
assert isinstance(rendered, int)
|
||||
|
||||
def test_method_chaining_add_child(self):
|
||||
"""Test that add_child returns self for chaining."""
|
||||
page = DynamicPage()
|
||||
|
||||
class MockRenderable:
|
||||
def __init__(self):
|
||||
self.size = (50, 50)
|
||||
self._origin = (0, 0)
|
||||
|
||||
@property
|
||||
def origin(self):
|
||||
return self._origin
|
||||
|
||||
result = page.add_child(MockRenderable())
|
||||
|
||||
assert result is page
|
||||
|
||||
def test_method_chaining_clear_children(self):
|
||||
"""Test that clear_children returns self for chaining."""
|
||||
page = DynamicPage()
|
||||
|
||||
result = page.clear_children()
|
||||
|
||||
assert result is page
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
pytest.main([__file__, '-v'])
|
||||
@@ -0,0 +1,397 @@
|
||||
"""
|
||||
Unit tests for table column width optimization.
|
||||
"""
|
||||
|
||||
import pytest
|
||||
from pyWebLayout.layout.table_optimizer import (
|
||||
optimize_table_layout,
|
||||
sample_table_rows,
|
||||
extract_html_column_widths,
|
||||
parse_html_width,
|
||||
distribute_column_widths,
|
||||
get_column_count,
|
||||
calculate_table_overhead
|
||||
)
|
||||
from pyWebLayout.abstract.block import Table
|
||||
from pyWebLayout.concrete.table import TableStyle
|
||||
|
||||
|
||||
class TestParseHtmlWidth:
|
||||
"""Test HTML width parsing."""
|
||||
|
||||
def test_parse_int(self):
|
||||
assert parse_html_width(100) == 100
|
||||
|
||||
def test_parse_px_string(self):
|
||||
assert parse_html_width("150px") == 150
|
||||
|
||||
def test_parse_plain_number_string(self):
|
||||
assert parse_html_width("200") == 200
|
||||
|
||||
def test_parse_percentage_returns_none(self):
|
||||
assert parse_html_width("50%") is None
|
||||
|
||||
def test_parse_invalid_string(self):
|
||||
assert parse_html_width("invalid") is None
|
||||
|
||||
def test_parse_with_whitespace(self):
|
||||
assert parse_html_width(" 120px ") == 120
|
||||
|
||||
|
||||
class TestDistributeColumnWidths:
|
||||
"""Test column width distribution."""
|
||||
|
||||
def test_distribute_with_no_fixed_columns(self):
|
||||
min_widths = [50, 60, 70]
|
||||
pref_widths = [100, 120, 140]
|
||||
available = 360
|
||||
fixed = {}
|
||||
|
||||
result = distribute_column_widths(min_widths, pref_widths, available, fixed)
|
||||
|
||||
# Should use preferred widths (they fit)
|
||||
assert result == [100, 120, 140]
|
||||
|
||||
def test_distribute_when_preferred_fits(self):
|
||||
min_widths = [50, 50]
|
||||
pref_widths = [100, 100]
|
||||
available = 250
|
||||
fixed = {}
|
||||
|
||||
result = distribute_column_widths(min_widths, pref_widths, available, fixed)
|
||||
|
||||
# Preferred widths fit, extra 50px distributed proportionally (25px each)
|
||||
assert result == [125, 125]
|
||||
|
||||
def test_distribute_when_must_use_minimum(self):
|
||||
min_widths = [100, 100]
|
||||
pref_widths = [200, 200]
|
||||
available = 150
|
||||
fixed = {}
|
||||
|
||||
result = distribute_column_widths(min_widths, pref_widths, available, fixed)
|
||||
|
||||
# Can't even fit minimum, but force it anyway
|
||||
assert result == [100, 100]
|
||||
|
||||
def test_distribute_proportional(self):
|
||||
min_widths = [50, 50]
|
||||
pref_widths = [200, 100]
|
||||
available = 200 # Between min and pref totals
|
||||
fixed = {}
|
||||
|
||||
result = distribute_column_widths(min_widths, pref_widths, available, fixed)
|
||||
|
||||
# Should distribute proportionally
|
||||
assert len(result) == 2
|
||||
assert result[0] + result[1] == 200
|
||||
# First column should get more (higher pref)
|
||||
assert result[0] > result[1]
|
||||
|
||||
def test_distribute_with_fixed_columns(self):
|
||||
min_widths = [50, 50, 50]
|
||||
pref_widths = [100, 100, 100]
|
||||
available = 300
|
||||
fixed = {1: 80} # Second column fixed at 80
|
||||
|
||||
result = distribute_column_widths(min_widths, pref_widths, available, fixed)
|
||||
|
||||
# Second column should be 80
|
||||
assert result[1] == 80
|
||||
# Other columns share remaining space
|
||||
assert result[0] + result[2] == 220
|
||||
|
||||
def test_distribute_all_fixed(self):
|
||||
min_widths = [50, 50]
|
||||
pref_widths = [100, 100]
|
||||
available = 300
|
||||
fixed = {0: 120, 1: 150}
|
||||
|
||||
result = distribute_column_widths(min_widths, pref_widths, available, fixed)
|
||||
|
||||
assert result == [120, 150]
|
||||
|
||||
def test_distribute_empty(self):
|
||||
result = distribute_column_widths([], [], 100, {})
|
||||
assert result == []
|
||||
|
||||
|
||||
class TestGetColumnCount:
|
||||
"""Test column counting."""
|
||||
|
||||
def test_empty_table(self):
|
||||
table = Table()
|
||||
assert get_column_count(table) == 0
|
||||
|
||||
def test_table_with_header(self):
|
||||
from pyWebLayout.abstract.block import TableRow, TableCell, Paragraph, Word
|
||||
from pyWebLayout.style import Font
|
||||
|
||||
table = Table()
|
||||
font = Font(font_size=12)
|
||||
|
||||
row = TableRow()
|
||||
for text in ["A", "B", "C"]:
|
||||
cell = TableCell(is_header=True)
|
||||
para = Paragraph(font)
|
||||
para.add_word(Word(text, font))
|
||||
cell.add_block(para)
|
||||
row.add_cell(cell)
|
||||
|
||||
table.add_row(row, section="header")
|
||||
|
||||
assert get_column_count(table) == 3
|
||||
|
||||
def test_table_with_body(self):
|
||||
from pyWebLayout.abstract.block import TableRow, TableCell, Paragraph, Word
|
||||
from pyWebLayout.style import Font
|
||||
|
||||
table = Table()
|
||||
font = Font(font_size=12)
|
||||
|
||||
row = TableRow()
|
||||
for text in ["1", "2"]:
|
||||
cell = TableCell()
|
||||
para = Paragraph(font)
|
||||
para.add_word(Word(text, font))
|
||||
cell.add_block(para)
|
||||
row.add_cell(cell)
|
||||
|
||||
table.add_row(row, section="body")
|
||||
|
||||
assert get_column_count(table) == 2
|
||||
|
||||
|
||||
class TestSampleTableRows:
|
||||
"""Test row sampling."""
|
||||
|
||||
def test_sample_small_table(self):
|
||||
from pyWebLayout.abstract.block import TableRow, TableCell, Paragraph
|
||||
from pyWebLayout.abstract.inline import Word
|
||||
from pyWebLayout.style import Font
|
||||
|
||||
table = Table()
|
||||
font = Font(font_size=12)
|
||||
|
||||
for text in ["1", "2"]:
|
||||
row = TableRow()
|
||||
cell = TableCell()
|
||||
para = Paragraph(font)
|
||||
para.add_word(Word(text, font))
|
||||
cell.add_block(para)
|
||||
row.add_cell(cell)
|
||||
table.add_row(row, section="body")
|
||||
|
||||
sampled = sample_table_rows(table, sample_size=5)
|
||||
|
||||
# Should get all rows (only 2)
|
||||
assert len(sampled) == 2
|
||||
|
||||
def test_sample_large_table(self):
|
||||
from pyWebLayout.abstract.block import TableRow, TableCell, Paragraph
|
||||
from pyWebLayout.abstract.inline import Word
|
||||
from pyWebLayout.style import Font
|
||||
|
||||
table = Table()
|
||||
font = Font(font_size=12)
|
||||
|
||||
for i in range(20):
|
||||
row = TableRow()
|
||||
cell = TableCell()
|
||||
para = Paragraph(font)
|
||||
para.add_word(Word(str(i), font))
|
||||
cell.add_block(para)
|
||||
row.add_cell(cell)
|
||||
table.add_row(row, section="body")
|
||||
|
||||
sampled = sample_table_rows(table, sample_size=5)
|
||||
|
||||
# Should get only 5 body rows
|
||||
assert len(sampled) == 5
|
||||
|
||||
def test_sample_with_header_body_footer(self):
|
||||
from pyWebLayout.abstract.block import TableRow, TableCell, Paragraph
|
||||
from pyWebLayout.abstract.inline import Word
|
||||
from pyWebLayout.style import Font
|
||||
|
||||
table = Table()
|
||||
font = Font(font_size=12)
|
||||
|
||||
# 3 header rows
|
||||
for i in range(3):
|
||||
row = TableRow()
|
||||
cell = TableCell(is_header=True)
|
||||
para = Paragraph(font)
|
||||
para.add_word(Word(f"H{i}", font))
|
||||
cell.add_block(para)
|
||||
row.add_cell(cell)
|
||||
table.add_row(row, section="header")
|
||||
|
||||
# 10 body rows
|
||||
for i in range(10):
|
||||
row = TableRow()
|
||||
cell = TableCell()
|
||||
para = Paragraph(font)
|
||||
para.add_word(Word(f"B{i}", font))
|
||||
cell.add_block(para)
|
||||
row.add_cell(cell)
|
||||
table.add_row(row, section="body")
|
||||
|
||||
# 2 footer rows
|
||||
for i in range(2):
|
||||
row = TableRow()
|
||||
cell = TableCell()
|
||||
para = Paragraph(font)
|
||||
para.add_word(Word(f"F{i}", font))
|
||||
cell.add_block(para)
|
||||
row.add_cell(cell)
|
||||
table.add_row(row, section="footer")
|
||||
|
||||
sampled = sample_table_rows(table, sample_size=2)
|
||||
|
||||
# Should get 2 from each section = 6 total
|
||||
assert len(sampled) == 6
|
||||
|
||||
|
||||
class TestExtractHtmlColumnWidths:
|
||||
"""Test HTML width extraction."""
|
||||
|
||||
def test_no_widths(self):
|
||||
from pyWebLayout.abstract.block import TableRow, TableCell, Paragraph
|
||||
from pyWebLayout.abstract.inline import Word
|
||||
from pyWebLayout.style import Font
|
||||
|
||||
table = Table()
|
||||
font = Font(font_size=12)
|
||||
|
||||
row = TableRow()
|
||||
for text in ["A", "B"]:
|
||||
cell = TableCell()
|
||||
para = Paragraph(font)
|
||||
para.add_word(Word(text, font))
|
||||
cell.add_block(para)
|
||||
row.add_cell(cell)
|
||||
table.add_row(row, section="body")
|
||||
|
||||
widths = extract_html_column_widths(table)
|
||||
|
||||
assert widths == [None, None]
|
||||
|
||||
def test_cell_width_attributes(self):
|
||||
from pyWebLayout.abstract.block import TableRow, TableCell, Paragraph
|
||||
from pyWebLayout.abstract.inline import Word
|
||||
from pyWebLayout.style import Font
|
||||
|
||||
table = Table()
|
||||
font = Font(font_size=12)
|
||||
|
||||
row = TableRow()
|
||||
|
||||
cell1 = TableCell()
|
||||
cell1.width = "100px"
|
||||
para1 = Paragraph(font)
|
||||
para1.add_word(Word("A", font))
|
||||
cell1.add_block(para1)
|
||||
row.add_cell(cell1)
|
||||
|
||||
cell2 = TableCell()
|
||||
cell2.width = "150"
|
||||
para2 = Paragraph(font)
|
||||
para2.add_word(Word("B", font))
|
||||
cell2.add_block(para2)
|
||||
row.add_cell(cell2)
|
||||
|
||||
table.add_row(row, section="body")
|
||||
|
||||
widths = extract_html_column_widths(table)
|
||||
|
||||
assert widths == [100, 150]
|
||||
|
||||
|
||||
class TestCalculateTableOverhead:
|
||||
"""Test table overhead calculation."""
|
||||
|
||||
def test_basic_overhead(self):
|
||||
style = TableStyle(border_width=1, cell_spacing=0)
|
||||
overhead = calculate_table_overhead(3, style)
|
||||
|
||||
# 3 columns = 4 borders (n+1)
|
||||
assert overhead == 4
|
||||
|
||||
def test_with_cell_spacing(self):
|
||||
style = TableStyle(border_width=1, cell_spacing=5)
|
||||
overhead = calculate_table_overhead(3, style)
|
||||
|
||||
# 4 borders + 2 spacings (n-1)
|
||||
assert overhead == 4 + 10
|
||||
|
||||
def test_thicker_borders(self):
|
||||
style = TableStyle(border_width=3, cell_spacing=0)
|
||||
overhead = calculate_table_overhead(2, style)
|
||||
|
||||
# 2 columns = 3 borders * 3px
|
||||
assert overhead == 9
|
||||
|
||||
|
||||
class TestOptimizeTableLayout:
|
||||
"""Test full table optimization."""
|
||||
|
||||
def test_optimize_simple_table(self):
|
||||
from pyWebLayout.abstract.block import TableRow, TableCell, Paragraph
|
||||
from pyWebLayout.abstract.inline import Word
|
||||
from pyWebLayout.style import Font
|
||||
|
||||
table = Table()
|
||||
font = Font(font_size=12)
|
||||
|
||||
row = TableRow()
|
||||
for text in ["Short", "A bit longer text"]:
|
||||
cell = TableCell()
|
||||
para = Paragraph(font)
|
||||
for word in text.split():
|
||||
para.add_word(Word(word, font))
|
||||
cell.add_block(para)
|
||||
row.add_cell(cell)
|
||||
table.add_row(row, section="body")
|
||||
|
||||
style = TableStyle()
|
||||
widths = optimize_table_layout(table, available_width=400, style=style)
|
||||
|
||||
# Should return 2 column widths
|
||||
assert len(widths) == 2
|
||||
# Second column should be wider
|
||||
assert widths[1] > widths[0]
|
||||
|
||||
def test_optimize_empty_table(self):
|
||||
table = Table()
|
||||
|
||||
widths = optimize_table_layout(table, available_width=400)
|
||||
|
||||
assert widths == []
|
||||
|
||||
def test_optimize_respects_sample_size(self):
|
||||
from pyWebLayout.abstract.block import TableRow, TableCell, Paragraph
|
||||
from pyWebLayout.abstract.inline import Word
|
||||
from pyWebLayout.style import Font
|
||||
|
||||
table = Table()
|
||||
font = Font(font_size=12)
|
||||
|
||||
# Create 20 rows but only first 5 should be sampled
|
||||
for i in range(20):
|
||||
row = TableRow()
|
||||
cell = TableCell()
|
||||
para = Paragraph(font)
|
||||
para.add_word(Word(f"Data {i}", font))
|
||||
cell.add_block(para)
|
||||
row.add_cell(cell)
|
||||
table.add_row(row, section="body")
|
||||
|
||||
widths = optimize_table_layout(table, available_width=400, sample_size=5)
|
||||
|
||||
# Should return width for 1 column
|
||||
assert len(widths) == 1
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
pytest.main([__file__, '-v'])
|
||||
Reference in New Issue
Block a user