Added links
Python CI / test (push) Failing after 6m34s

This commit is contained in:
2025-11-04 13:39:21 +01:00
parent de18b1c2cc
commit 4fe5f8cf60
8 changed files with 805 additions and 34 deletions
+151
View File
@@ -3,7 +3,11 @@ from __future__ import annotations
from typing import List, Tuple, Optional, Union
from pyWebLayout.concrete import Page, Line, Text
from pyWebLayout.concrete.image import RenderableImage
from pyWebLayout.concrete.functional import LinkText
from pyWebLayout.abstract import Paragraph, Word, Link
from pyWebLayout.abstract.block import Image as AbstractImage
from pyWebLayout.abstract.inline import LinkedWord
from pyWebLayout.style.concrete_style import ConcreteStyleRegistry, RenderingContext, StyleResolver
def paragraph_layouter(paragraph: Paragraph, page: Page, start_word: int = 0, pretext: Optional[Text] = None, alignment_override: Optional['Alignment'] = None) -> Tuple[bool, Optional[int], Optional[Text]]:
@@ -130,6 +134,12 @@ def paragraph_layouter(paragraph: Paragraph, page: Page, start_word: int = 0, pr
# Process words starting from start_word
for i, word in enumerate(paragraph.words[start_word:], start=start_word):
# Check if this is a LinkedWord and needs special handling in concrete layer
# Note: The Line.add_word method will create Text objects internally,
# but we may want to create LinkText for LinkedWord instances in future
# For now, the abstract layer (LinkedWord) carries the link info,
# and the concrete layer (LinkText) would be created during rendering
success, overflow_text = current_line.add_word(word, current_pretext)
if success:
@@ -191,3 +201,144 @@ def paragraph_layouter(paragraph: Paragraph, page: Page, start_word: int = 0, pr
# All words processed successfully
return True, None, None
def image_layouter(image: AbstractImage, page: Page, max_width: Optional[int] = None,
max_height: Optional[int] = None) -> bool:
"""
Layout an image within a given page.
This function places an image on the page, respecting size constraints
and available space. Images are centered horizontally by default.
Args:
image: The abstract Image object to layout
page: The page to layout the image on
max_width: Maximum width constraint (defaults to page available width)
max_height: Maximum height constraint (defaults to remaining page height)
Returns:
bool: True if image was successfully laid out, False if page ran out of space
"""
from pyWebLayout.style import Alignment
# Use page available width if max_width not specified
if max_width is None:
max_width = page.available_width
# Calculate available height on page
available_height = page.size[1] - page._current_y_offset - page.border_size
if max_height is None:
max_height = available_height
else:
max_height = min(max_height, available_height)
# Calculate scaled dimensions
scaled_width, scaled_height = image.calculate_scaled_dimensions(max_width, max_height)
# Check if image fits on current page
if scaled_height is None or scaled_height > available_height:
return False
# Create renderable image
x_offset = page.border_size
y_offset = page._current_y_offset
renderable_image = RenderableImage(
image=image,
canvas=page.canvas,
max_width=max_width,
max_height=max_height,
origin=(x_offset, y_offset),
size=(scaled_width or max_width, scaled_height or max_height),
halign=Alignment.CENTER,
valign=Alignment.TOP
)
# Add to page
page.add_child(renderable_image)
return True
class DocumentLayouter:
"""
Document layouter that orchestrates layout of various abstract elements.
Delegates to specialized layouters for different content types:
- paragraph_layouter for text paragraphs
- image_layouter for images (future)
- table_layouter for tables (future)
This class acts as a coordinator, managing the overall document flow
and page context while delegating specific layout tasks to specialized
layouter functions.
"""
def __init__(self, page: Page):
"""
Initialize the document layouter with a page.
Args:
page: The page to layout content on
"""
self.page = page
self.style_registry = ConcreteStyleRegistry(page.style_resolver)
def layout_paragraph(self, paragraph: Paragraph, start_word: int = 0,
pretext: Optional[Text] = None) -> Tuple[bool, Optional[int], Optional[Text]]:
"""
Layout a paragraph using the paragraph_layouter.
Args:
paragraph: The paragraph to layout
start_word: Index of the first word to process (for continuation)
pretext: Optional pretext from a previous hyphenated word
Returns:
Tuple of (success, failed_word_index, remaining_pretext)
"""
return paragraph_layouter(paragraph, self.page, start_word, pretext)
def layout_image(self, image: AbstractImage, max_width: Optional[int] = None,
max_height: Optional[int] = None) -> bool:
"""
Layout an image using the image_layouter.
Args:
image: The abstract Image object to layout
max_width: Maximum width constraint (defaults to page available width)
max_height: Maximum height constraint (defaults to remaining page height)
Returns:
bool: True if image was successfully laid out, False if page ran out of space
"""
return image_layouter(image, self.page, max_width, max_height)
def layout_document(self, elements: List[Union[Paragraph, AbstractImage]]) -> bool:
"""
Layout a list of abstract elements (paragraphs and images).
This method delegates to specialized layouters based on element type:
- Paragraphs are handled by layout_paragraph
- Images are handled by layout_image
- Tables and other elements can be added in the future
Args:
elements: List of abstract elements to layout
Returns:
True if all elements were successfully laid out, False otherwise
"""
for element in elements:
if isinstance(element, Paragraph):
success, _, _ = self.layout_paragraph(element)
if not success:
return False
elif isinstance(element, AbstractImage):
success = self.layout_image(element)
if not success:
return False
# Future: elif isinstance(element, Table): use table_layouter
# Future: elif isinstance(element, CodeBlock): use code_layouter
return True