added line wrapping to table
This commit is contained in:
parent
303179865d
commit
5afad2ca07
@ -108,21 +108,34 @@ class TableCellRenderer(Box):
|
|||||||
return None # Cell rendering is done directly on the page
|
return None # Cell rendering is done directly on the page
|
||||||
|
|
||||||
def _render_cell_content(self, x: int, y: int, width: int, height: int):
|
def _render_cell_content(self, x: int, y: int, width: int, height: int):
|
||||||
"""Render the content inside the cell (text and images)."""
|
"""Render the content inside the cell (text and images) with line wrapping."""
|
||||||
from PIL import ImageFont
|
from pyWebLayout.concrete.text import Line, Text
|
||||||
|
from pyWebLayout.style.fonts import Font
|
||||||
|
from pyWebLayout.style import FontWeight, Alignment
|
||||||
|
|
||||||
current_y = y + 2
|
current_y = y + 2
|
||||||
|
available_height = height - 4 # Account for top/bottom padding
|
||||||
|
|
||||||
# Get font
|
# Create font for the cell
|
||||||
try:
|
font_size = 12
|
||||||
|
font_path = "/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf"
|
||||||
if self._is_header_section and self._style.header_text_bold:
|
if self._is_header_section and self._style.header_text_bold:
|
||||||
font = ImageFont.truetype(
|
font_path = "/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf"
|
||||||
"/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf", 12)
|
|
||||||
else:
|
font = Font(
|
||||||
font = ImageFont.truetype(
|
font_path=font_path,
|
||||||
"/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf", 12)
|
font_size=font_size,
|
||||||
except BaseException:
|
weight=FontWeight.BOLD if self._is_header_section and self._style.header_text_bold else FontWeight.NORMAL
|
||||||
font = ImageFont.load_default()
|
)
|
||||||
|
|
||||||
|
# Word spacing constraints (min, max)
|
||||||
|
min_spacing = int(font_size * 0.25)
|
||||||
|
max_spacing = int(font_size * 0.5)
|
||||||
|
word_spacing = (min_spacing, max_spacing)
|
||||||
|
|
||||||
|
# Line height (baseline spacing)
|
||||||
|
line_height = font_size + 4
|
||||||
|
ascent, descent = font.font.getmetrics()
|
||||||
|
|
||||||
# Render each block in the cell
|
# Render each block in the cell
|
||||||
for block in self._cell.blocks():
|
for block in self._cell.blocks():
|
||||||
@ -131,38 +144,83 @@ class TableCellRenderer(Box):
|
|||||||
current_y = self._render_image_in_cell(
|
current_y = self._render_image_in_cell(
|
||||||
block, x, current_y, width, height - (current_y - y))
|
block, x, current_y, width, height - (current_y - y))
|
||||||
elif isinstance(block, (Paragraph, Heading)):
|
elif isinstance(block, (Paragraph, Heading)):
|
||||||
# Extract and render text
|
# Get words from the block
|
||||||
words = []
|
|
||||||
word_items = block.words() if callable(block.words) else block.words
|
word_items = block.words() if callable(block.words) else block.words
|
||||||
for word in word_items:
|
words = list(word_items)
|
||||||
if hasattr(word, 'text'):
|
|
||||||
words.append(word.text)
|
|
||||||
elif isinstance(word, tuple) and len(word) >= 2:
|
|
||||||
word_obj = word[1]
|
|
||||||
if hasattr(word_obj, 'text'):
|
|
||||||
words.append(word_obj.text)
|
|
||||||
|
|
||||||
if words:
|
if not words:
|
||||||
text = " ".join(words)
|
continue
|
||||||
if current_y <= y + height - 15:
|
|
||||||
self._draw.text((x + 2, current_y), text,
|
# Layout words using Line objects with wrapping
|
||||||
fill=(0, 0, 0), font=font)
|
word_index = 0
|
||||||
current_y += 16
|
pretext = None
|
||||||
|
|
||||||
|
while word_index < len(words):
|
||||||
|
# Check if we have space for another line
|
||||||
|
if current_y + ascent + descent > y + available_height:
|
||||||
|
break # No more space in cell
|
||||||
|
|
||||||
|
# Create a new line
|
||||||
|
line = Line(
|
||||||
|
spacing=word_spacing,
|
||||||
|
origin=(x + 2, current_y),
|
||||||
|
size=(width - 4, line_height),
|
||||||
|
draw=self._draw,
|
||||||
|
font=font,
|
||||||
|
halign=Alignment.LEFT
|
||||||
|
)
|
||||||
|
|
||||||
|
# Add words to this line until it's full
|
||||||
|
line_has_content = False
|
||||||
|
while word_index < len(words):
|
||||||
|
word = words[word_index]
|
||||||
|
|
||||||
|
# Handle word tuples (index, word_obj)
|
||||||
|
if isinstance(word, tuple) and len(word) >= 2:
|
||||||
|
word_obj = word[1]
|
||||||
|
else:
|
||||||
|
word_obj = word
|
||||||
|
|
||||||
|
# Try to add word to line
|
||||||
|
success, overflow = line.add_word(word_obj, pretext)
|
||||||
|
pretext = None # Clear pretext after use
|
||||||
|
|
||||||
|
if success:
|
||||||
|
line_has_content = True
|
||||||
|
if overflow:
|
||||||
|
# Word was hyphenated, carry over to next line
|
||||||
|
pretext = overflow
|
||||||
|
word_index += 1
|
||||||
|
else:
|
||||||
|
# Word doesn't fit on this line
|
||||||
|
if not line_has_content:
|
||||||
|
# Even first word doesn't fit, force it anyway to avoid infinite loop
|
||||||
|
word_index += 1
|
||||||
|
break
|
||||||
|
|
||||||
|
# Render the line if it has content
|
||||||
|
if line_has_content or len(line.text_objects) > 0:
|
||||||
|
line.render()
|
||||||
|
current_y += line_height
|
||||||
|
|
||||||
if current_y > y + height - 10: # Don't overflow cell
|
if current_y > y + height - 10: # Don't overflow cell
|
||||||
break
|
break
|
||||||
|
|
||||||
# If no structured content, try to get any text representation
|
# If no structured content, try to get any text representation
|
||||||
if current_y == y + 2 and hasattr(self._cell, '_text_content'):
|
if current_y == y + 2 and hasattr(self._cell, '_text_content'):
|
||||||
|
# Use simple text rendering for fallback case
|
||||||
|
from PIL import ImageFont
|
||||||
|
try:
|
||||||
|
pil_font = ImageFont.truetype(font_path, font_size)
|
||||||
|
except BaseException:
|
||||||
|
pil_font = ImageFont.load_default()
|
||||||
|
|
||||||
self._draw.text(
|
self._draw.text(
|
||||||
(x + 2,
|
(x + 2, current_y),
|
||||||
current_y),
|
|
||||||
self._cell._text_content,
|
self._cell._text_content,
|
||||||
fill=(
|
fill=(0, 0, 0),
|
||||||
0,
|
font=pil_font
|
||||||
0,
|
)
|
||||||
0),
|
|
||||||
font=font)
|
|
||||||
|
|
||||||
def _render_image_in_cell(self, image_block: AbstractImage, x: int, y: int,
|
def _render_image_in_cell(self, image_block: AbstractImage, x: int, y: int,
|
||||||
max_width: int, max_height: int) -> int:
|
max_width: int, max_height: int) -> int:
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user