255 lines
7.4 KiB
Python
255 lines
7.4 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Demo: Optimized Table Column Width Layout
|
|
|
|
This example demonstrates the intelligent table column width optimization:
|
|
- Automatic width distribution based on content
|
|
- HTML width overrides (fixed column widths)
|
|
- Sampling for performance (large tables)
|
|
- Comparison: before (equal distribution) vs after (optimized)
|
|
|
|
The optimizer:
|
|
1. Samples first ~5 rows from each section
|
|
2. Measures minimum and preferred widths for each column
|
|
3. Distributes available space proportionally
|
|
4. Respects HTML width attributes
|
|
"""
|
|
|
|
from pyWebLayout.concrete.page import Page
|
|
from pyWebLayout.concrete.table import TableRenderer, TableStyle
|
|
from pyWebLayout.style.page_style import PageStyle
|
|
from pyWebLayout.style import Font
|
|
from pyWebLayout.abstract.block import Table, TableRow, TableCell, Paragraph
|
|
from pyWebLayout.abstract.inline import Word
|
|
from PIL import ImageDraw
|
|
|
|
|
|
def create_demo_table_1():
|
|
"""Create a table with varying content lengths (shows optimization)."""
|
|
table = Table()
|
|
table.caption = "Example 1: Optimized Width Distribution"
|
|
|
|
font = Font(font_size=11)
|
|
|
|
# Header
|
|
header_row = TableRow()
|
|
for text in ["ID", "Name", "Description"]:
|
|
cell = TableCell(is_header=True)
|
|
para = Paragraph(font)
|
|
para.add_word(Word(text, font))
|
|
cell.add_block(para)
|
|
header_row.add_cell(cell)
|
|
table.add_row(header_row, section="header")
|
|
|
|
# Body rows with varying content lengths
|
|
data = [
|
|
("1", "Alice", "Short description"),
|
|
("2", "Bob", "This is a much longer description that demonstrates how the optimizer allocates more space to columns with longer content"),
|
|
("3", "Charlie", "Medium length description here"),
|
|
("4", "Diana", "Another longer description that shows the column width optimization working effectively for content-heavy cells"),
|
|
]
|
|
|
|
for row_data in data:
|
|
row = TableRow()
|
|
for text in row_data:
|
|
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")
|
|
|
|
return table
|
|
|
|
|
|
def create_demo_table_2():
|
|
"""Create a table with HTML width overrides."""
|
|
table = Table()
|
|
table.caption = "Example 2: Fixed Column Widths (HTML override)"
|
|
|
|
font = Font(font_size=11)
|
|
|
|
# Header with width attributes
|
|
header_row = TableRow()
|
|
|
|
# Fixed width column
|
|
cell1 = TableCell(is_header=True)
|
|
cell1.width = "80px" # HTML width override!
|
|
para1 = Paragraph(font)
|
|
para1.add_word(Word("ID", font))
|
|
para1.add_word(Word("(Fixed", font))
|
|
para1.add_word(Word("80px)", font))
|
|
cell1.add_block(para1)
|
|
header_row.add_cell(cell1)
|
|
|
|
# Auto-width columns
|
|
for text in ["Name (Auto)", "Description (Auto)"]:
|
|
cell = TableCell(is_header=True)
|
|
para = Paragraph(font)
|
|
for word in text.split():
|
|
para.add_word(Word(word, font))
|
|
cell.add_block(para)
|
|
header_row.add_cell(cell)
|
|
|
|
table.add_row(header_row, section="header")
|
|
|
|
# Body rows
|
|
data = [
|
|
("1", "Alice", "The first two columns adapt to remaining space"),
|
|
("2", "Bob", "ID column stays fixed at 80px width"),
|
|
("3", "Charlie", "Name and Description share the remaining width proportionally"),
|
|
]
|
|
|
|
for row_data in data:
|
|
row = TableRow()
|
|
# First cell also has fixed width
|
|
cell = TableCell()
|
|
cell.width = "80px"
|
|
para = Paragraph(font)
|
|
para.add_word(Word(row_data[0], font))
|
|
cell.add_block(para)
|
|
row.add_cell(cell)
|
|
|
|
# Other cells auto-width
|
|
for text in row_data[1:]:
|
|
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")
|
|
|
|
return table
|
|
|
|
|
|
def create_demo_table_3():
|
|
"""Create a large table (demonstrates sampling)."""
|
|
table = Table()
|
|
table.caption = "Example 3: Large Table (uses sampling for performance)"
|
|
|
|
font = Font(font_size=10)
|
|
|
|
# Header
|
|
header_row = TableRow()
|
|
for text in ["Index", "Data A", "Data B", "Data C"]:
|
|
cell = TableCell(is_header=True)
|
|
para = Paragraph(font)
|
|
para.add_word(Word(text, font))
|
|
cell.add_block(para)
|
|
header_row.add_cell(cell)
|
|
table.add_row(header_row, section="header")
|
|
|
|
# Many body rows (only first ~5 will be sampled for measurement)
|
|
for i in range(50):
|
|
row = TableRow()
|
|
|
|
# Index
|
|
cell = TableCell()
|
|
para = Paragraph(font)
|
|
para.add_word(Word(str(i + 1), font))
|
|
cell.add_block(para)
|
|
row.add_cell(cell)
|
|
|
|
# Data columns with varying content
|
|
if i % 3 == 0:
|
|
data = ["Short", "Medium length", "Longer content here"]
|
|
elif i % 3 == 1:
|
|
data = ["Medium", "Short", "Also longer content"]
|
|
else:
|
|
data = ["Longer text", "Short", "Medium"]
|
|
|
|
for text in data:
|
|
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")
|
|
|
|
return table
|
|
|
|
|
|
def main():
|
|
# Create page
|
|
page_style = PageStyle(
|
|
border_width=1,
|
|
padding=(20, 20, 20, 20),
|
|
background_color=(255, 255, 255)
|
|
)
|
|
page = Page(size=(800, 2200), style=page_style)
|
|
|
|
# Get canvas and draw
|
|
canvas = page._create_canvas()
|
|
page._canvas = canvas
|
|
page._draw = ImageDraw.Draw(canvas)
|
|
|
|
current_y = 30
|
|
|
|
# Table style
|
|
table_style = TableStyle(
|
|
border_width=1,
|
|
border_color=(100, 100, 100),
|
|
cell_padding=(8, 8, 8, 8),
|
|
header_bg_color=(220, 230, 240),
|
|
cell_bg_color=(255, 255, 255),
|
|
alternate_row_color=(248, 248, 248)
|
|
)
|
|
|
|
# Render Example 1: Optimized distribution
|
|
table1 = create_demo_table_1()
|
|
renderer1 = TableRenderer(
|
|
table1,
|
|
origin=(20, current_y),
|
|
available_width=760,
|
|
draw=page._draw,
|
|
style=table_style,
|
|
canvas=canvas
|
|
)
|
|
renderer1.render()
|
|
current_y += renderer1.height + 40
|
|
|
|
# Render Example 2: Fixed widths
|
|
table2 = create_demo_table_2()
|
|
renderer2 = TableRenderer(
|
|
table2,
|
|
origin=(20, current_y),
|
|
available_width=760,
|
|
draw=page._draw,
|
|
style=table_style,
|
|
canvas=canvas
|
|
)
|
|
renderer2.render()
|
|
current_y += renderer2.height + 40
|
|
|
|
# Render Example 3: Large table with sampling
|
|
table3 = create_demo_table_3()
|
|
renderer3 = TableRenderer(
|
|
table3,
|
|
origin=(20, current_y),
|
|
available_width=760,
|
|
draw=page._draw,
|
|
style=table_style,
|
|
canvas=canvas
|
|
)
|
|
renderer3.render()
|
|
|
|
# Save
|
|
output_path = "docs/images/example_12_optimized_table_layout.png"
|
|
canvas.save(output_path)
|
|
|
|
print(f"✓ Optimized table layout demo created!")
|
|
print(f" Output: {output_path}")
|
|
print(f" Image size: {canvas.size}")
|
|
print(f"\nExamples demonstrated:")
|
|
print(f" 1. Content-aware width distribution")
|
|
print(f" 2. HTML width overrides (80px fixed column)")
|
|
print(f" 3. Large table with sampling (50 rows, only ~5 measured)")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|