A comprehensive Python document processing library that renders structured documents to PDF with advanced typography and customizable styling
—
Advanced layout system managing container-based document layout, automatic text flow, pagination, and precise element positioning. The layout engine handles the complex task of transforming flowable content into positioned elements on pages while respecting layout constraints and style requirements.
Core container classes that define rectangular areas for content placement and provide the foundation for document layout.
class Container:
"""
Rectangular rendering area for flowable content.
Parameters:
- name: str, container identifier
- parent: Container, parent container (None for root)
- left: Dimension, left edge position
- top: Dimension, top edge position
- width: Dimension, container width
- height: Dimension, container height
- clip: bool, whether to clip overflow content
"""
def __init__(self, name, parent, left=None, top=None, width=None, height=None, clip=False): ...
# Properties
@property
def width(self): ... # Container width
@property
def height(self): ... # Container height
@property
def cursor(self): ... # Current vertical position
@property
def remaining_height(self): ... # Available height remaining
# Layout methods
def render(self, flowable, last_descender=None): ... # Render flowable in container
def place(self, item, width, height, baseline=None): ... # Place item at cursor
def advance(self, height): ... # Move cursor down by height
def advance2(self, advance_type): ... # Advance cursor by type
# Positioning
def float_space(self, float_spec): ... # Get space for floated elements
def mark_page_nonempty(self): ... # Mark page as containing content
class FlowablesContainer(Container):
"""Container specialized for flowing flowable content."""
def render_flowable(self, flowable, last_descender, state, first_line_only=False): ...
class ChainedContainer(Container):
"""Container that can overflow to next container in chain."""
@property
def next(self): ... # Next container in chain
def create_next_container(self): ... # Create overflow containerSpecialized container types for different layout scenarios and content flow patterns.
class DownExpandingContainer(FlowablesContainer):
"""
Container that expands downward as content is added.
Automatically grows in height to accommodate content while
maintaining position and width constraints.
"""
def __init__(self, name, parent, left=None, top=None, width=None, clip=False): ...
class InlineDownExpandingContainer(DownExpandingContainer):
"""Inline version of down-expanding container for inline content."""
class UpExpandingContainer(FlowablesContainer):
"""
Container that expands upward from a fixed bottom position.
Used for footnotes and other bottom-anchored content.
"""
def __init__(self, name, parent, left=None, bottom=None, width=None, clip=False): ...
class UpDownExpandingContainer(FlowablesContainer):
"""
Container that can expand both upward and downward.
Maintains a center position while growing in both directions
as content is added.
"""
def __init__(self, name, parent, left=None, center=None, width=None, clip=False): ...
class VirtualContainer(Container):
"""
Non-placing container used for measurements and calculations.
Allows content to be measured and processed without actually
placing it in the document layout.
"""
def __init__(self, container): ...
def place(self, item, width, height, baseline=None): ... # No-op placement
class MaybeContainer(Container):
"""Container that may or may not be used based on conditions."""
def __init__(self, name, parent, condition, *args, **kwargs): ...Task-specific containers for handling special layout requirements like footnotes and floating elements.
class FootnoteContainer(UpExpandingContainer):
"""
Specialized container for footnote content at page bottom.
Manages footnote placement, numbering, and separation from
main content while handling overflow across pages.
Parameters:
- name: str, container identifier
- parent: Container, parent page container
- left: Dimension, left edge position
- bottom: Dimension, bottom edge position
- width: Dimension, container width
"""
def __init__(self, name, parent, left, bottom, width): ...
def add_footnote(self, note): ... # Add footnote to container
def prepare_footnote(self, note, note_marker): ... # Prepare footnote layoutChain system for connecting containers to enable content overflow across pages and columns.
class Chain:
"""
Series of connected containers for content overflow.
Manages the flow of content from one container to the next
when content doesn't fit in the current container.
Parameters:
- name: str, chain identifier
"""
def __init__(self, name): ...
def append(self, container): ... # Add container to chain
def prepend(self, container): ... # Add container at beginning
def remove(self, container): ... # Remove container from chain
@property
def first_container(self): ... # First container in chain
@property
def last_container(self): ... # Last container in chain
class ChainedContainer(Container):
"""Container that participates in a chain for overflow handling."""
@property
def chain(self): ... # Chain this container belongs to
@property
def previous(self): ... # Previous container in chain
@property
def next(self): ... # Next container in chainException types for handling layout-specific error conditions and control flow.
class ContainerOverflow(Exception):
"""
Raised when content doesn't fit in available container space.
Parameters:
- flowable: Flowable that caused overflow
- container: Container where overflow occurred
"""
def __init__(self, flowable, container): ...
class EndOfContainer(Exception):
"""
Raised when reaching end of container during layout.
Signals that no more content can be placed in current container
and overflow handling should be triggered.
"""
class PageBreakException(Exception):
"""
Raised to force a page break at current position.
Used by page break elements and manual break requests to
interrupt normal flow and move to next page.
"""Core interface that all flowable content must implement for participation in the layout system.
class Flowable(Styled):
"""
Base class for content that can flow through containers.
All document content elements inherit from Flowable and implement
the flow/render protocol for layout system integration.
"""
def flow(self, container, last_descender, state=None):
"""
Flow content into container with layout state management.
Parameters:
- container: Container to flow content into
- last_descender: Dimension, previous content's descender
- state: layout state from previous flow attempt
Returns:
- (width, height, baseline, state): layout result tuple
"""
...
def render(self, container, descender, state=None, first_line_only=False):
"""
Render content at current container position.
Parameters:
- container: Container to render content in
- descender: Dimension, descender from previous content
- state: layout state for partial rendering
- first_line_only: bool, render only first line if True
Returns:
- layout state for continuation if needed
"""
...
def initial_state(self, container):
"""
Create initial layout state for this flowable.
Parameters:
- container: Container where flowable will be placed
Returns:
- initial state object for flow/render operations
"""
...from rinohtype.layout import Container, DownExpandingContainer
from rinohtype.dimension import PT, MM
from rinohtype.paragraph import Paragraph
# Create parent container (page area)
page_container = Container(
name='page',
parent=None,
left=25*MM,
top=25*MM,
width=160*MM,
height=247*MM # A4 with margins
)
# Create content container that expands downward
content_container = DownExpandingContainer(
name='content',
parent=page_container,
left=0*PT,
top=0*PT,
width=160*MM
)
# Flow content into container
paragraph = Paragraph("This is sample content that will flow into the container.")
result = paragraph.flow(content_container, last_descender=0*PT)
if result:
width, height, baseline, state = result
paragraph.render(content_container, descender=0*PT, state=state)
content_container.advance(height)from rinohtype.layout import Chain, ChainedContainer
# Create container chain for multi-column or multi-page layout
main_chain = Chain('main_content')
# Create first page container
page1_container = ChainedContainer(
name='page1',
parent=None,
left=25*MM,
top=25*MM,
width=160*MM,
height=220*MM
)
main_chain.append(page1_container)
# Create second page container (linked automatically)
page2_container = ChainedContainer(
name='page2',
parent=None,
left=25*MM,
top=25*MM,
width=160*MM,
height=220*MM
)
main_chain.append(page2_container)
# Flow long content across pages
long_content = [
Paragraph("First paragraph..."),
Paragraph("Second paragraph..."),
# ... more content
]
current_container = main_chain.first_container
for paragraph in long_content:
try:
result = paragraph.flow(current_container, last_descender=0*PT)
if result:
paragraph.render(current_container, descender=0*PT)
except ContainerOverflow:
# Move to next container in chain
current_container = current_container.next
if current_container:
result = paragraph.flow(current_container, last_descender=0*PT)
paragraph.render(current_container, descender=0*PT)from rinohtype.layout import FootnoteContainer
from rinohtype.reference import Note
# Create footnote container at bottom of page
footnote_container = FootnoteContainer(
name='footnotes',
parent=page_container,
left=0*PT,
bottom=0*PT,
width=160*MM
)
# Add footnotes to container
footnote1 = Note([Paragraph("This is a footnote.")])
footnote2 = Note([Paragraph("Another footnote with more content.")])
footnote_container.add_footnote(footnote1)
footnote_container.add_footnote(footnote2)from rinohtype.layout import VirtualContainer
# Create virtual container for measuring content
virtual = VirtualContainer(content_container)
# Measure content without placing it
test_paragraph = Paragraph("Test content for measurement")
result = test_paragraph.flow(virtual, last_descender=0*PT)
if result:
width, height, baseline, state = result
print(f"Content would be {width} wide and {height} tall")
# Now place it for real if it fits
if height <= content_container.remaining_height:
test_paragraph.render(content_container, descender=0*PT, state=state)from rinohtype.layout import ContainerOverflow, EndOfContainer, PageBreakException
def flow_content_safely(flowable, container):
"""Flow content with proper error handling."""
try:
result = flowable.flow(container, last_descender=0*PT)
if result:
flowable.render(container, descender=0*PT)
return True
except ContainerOverflow as e:
print(f"Content overflow in {e.container.name}")
# Handle overflow - move to next page/container
return False
except EndOfContainer:
print("Reached end of container")
return False
except PageBreakException:
print("Page break requested")
# Handle page break
return Falsefrom rinohtype.layout import FlowablesContainer
class CustomContainer(FlowablesContainer):
"""Custom container with special layout behavior."""
def __init__(self, name, parent, **kwargs):
super().__init__(name, parent, **kwargs)
self.special_mode = True
def render_flowable(self, flowable, last_descender, state, first_line_only=False):
"""Custom rendering logic."""
if self.special_mode:
# Apply custom layout rules
pass
return super().render_flowable(flowable, last_descender, state, first_line_only)
def advance(self, height):
"""Custom cursor advancement."""
if self.special_mode:
# Custom spacing rules
height = height * 1.1
super().advance(height)Install with Tessl CLI
npx tessl i tessl/pypi-rinohtype