0
# Layout Engine
1
2
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.
3
4
## Capabilities
5
6
### Container System
7
8
Core container classes that define rectangular areas for content placement and provide the foundation for document layout.
9
10
```python { .api }
11
class Container:
12
"""
13
Rectangular rendering area for flowable content.
14
15
Parameters:
16
- name: str, container identifier
17
- parent: Container, parent container (None for root)
18
- left: Dimension, left edge position
19
- top: Dimension, top edge position
20
- width: Dimension, container width
21
- height: Dimension, container height
22
- clip: bool, whether to clip overflow content
23
"""
24
def __init__(self, name, parent, left=None, top=None, width=None, height=None, clip=False): ...
25
26
# Properties
27
@property
28
def width(self): ... # Container width
29
@property
30
def height(self): ... # Container height
31
@property
32
def cursor(self): ... # Current vertical position
33
@property
34
def remaining_height(self): ... # Available height remaining
35
36
# Layout methods
37
def render(self, flowable, last_descender=None): ... # Render flowable in container
38
def place(self, item, width, height, baseline=None): ... # Place item at cursor
39
def advance(self, height): ... # Move cursor down by height
40
def advance2(self, advance_type): ... # Advance cursor by type
41
42
# Positioning
43
def float_space(self, float_spec): ... # Get space for floated elements
44
def mark_page_nonempty(self): ... # Mark page as containing content
45
46
class FlowablesContainer(Container):
47
"""Container specialized for flowing flowable content."""
48
49
def render_flowable(self, flowable, last_descender, state, first_line_only=False): ...
50
51
class ChainedContainer(Container):
52
"""Container that can overflow to next container in chain."""
53
54
@property
55
def next(self): ... # Next container in chain
56
def create_next_container(self): ... # Create overflow container
57
```
58
59
### Container Types
60
61
Specialized container types for different layout scenarios and content flow patterns.
62
63
```python { .api }
64
class DownExpandingContainer(FlowablesContainer):
65
"""
66
Container that expands downward as content is added.
67
68
Automatically grows in height to accommodate content while
69
maintaining position and width constraints.
70
"""
71
def __init__(self, name, parent, left=None, top=None, width=None, clip=False): ...
72
73
class InlineDownExpandingContainer(DownExpandingContainer):
74
"""Inline version of down-expanding container for inline content."""
75
76
class UpExpandingContainer(FlowablesContainer):
77
"""
78
Container that expands upward from a fixed bottom position.
79
80
Used for footnotes and other bottom-anchored content.
81
"""
82
def __init__(self, name, parent, left=None, bottom=None, width=None, clip=False): ...
83
84
class UpDownExpandingContainer(FlowablesContainer):
85
"""
86
Container that can expand both upward and downward.
87
88
Maintains a center position while growing in both directions
89
as content is added.
90
"""
91
def __init__(self, name, parent, left=None, center=None, width=None, clip=False): ...
92
93
class VirtualContainer(Container):
94
"""
95
Non-placing container used for measurements and calculations.
96
97
Allows content to be measured and processed without actually
98
placing it in the document layout.
99
"""
100
def __init__(self, container): ...
101
102
def place(self, item, width, height, baseline=None): ... # No-op placement
103
104
class MaybeContainer(Container):
105
"""Container that may or may not be used based on conditions."""
106
107
def __init__(self, name, parent, condition, *args, **kwargs): ...
108
```
109
110
### Specialized Containers
111
112
Task-specific containers for handling special layout requirements like footnotes and floating elements.
113
114
```python { .api }
115
class FootnoteContainer(UpExpandingContainer):
116
"""
117
Specialized container for footnote content at page bottom.
118
119
Manages footnote placement, numbering, and separation from
120
main content while handling overflow across pages.
121
122
Parameters:
123
- name: str, container identifier
124
- parent: Container, parent page container
125
- left: Dimension, left edge position
126
- bottom: Dimension, bottom edge position
127
- width: Dimension, container width
128
"""
129
def __init__(self, name, parent, left, bottom, width): ...
130
131
def add_footnote(self, note): ... # Add footnote to container
132
def prepare_footnote(self, note, note_marker): ... # Prepare footnote layout
133
```
134
135
### Container Chains
136
137
Chain system for connecting containers to enable content overflow across pages and columns.
138
139
```python { .api }
140
class Chain:
141
"""
142
Series of connected containers for content overflow.
143
144
Manages the flow of content from one container to the next
145
when content doesn't fit in the current container.
146
147
Parameters:
148
- name: str, chain identifier
149
"""
150
def __init__(self, name): ...
151
152
def append(self, container): ... # Add container to chain
153
def prepend(self, container): ... # Add container at beginning
154
def remove(self, container): ... # Remove container from chain
155
156
@property
157
def first_container(self): ... # First container in chain
158
@property
159
def last_container(self): ... # Last container in chain
160
161
class ChainedContainer(Container):
162
"""Container that participates in a chain for overflow handling."""
163
164
@property
165
def chain(self): ... # Chain this container belongs to
166
@property
167
def previous(self): ... # Previous container in chain
168
@property
169
def next(self): ... # Next container in chain
170
```
171
172
### Layout Exceptions
173
174
Exception types for handling layout-specific error conditions and control flow.
175
176
```python { .api }
177
class ContainerOverflow(Exception):
178
"""
179
Raised when content doesn't fit in available container space.
180
181
Parameters:
182
- flowable: Flowable that caused overflow
183
- container: Container where overflow occurred
184
"""
185
def __init__(self, flowable, container): ...
186
187
class EndOfContainer(Exception):
188
"""
189
Raised when reaching end of container during layout.
190
191
Signals that no more content can be placed in current container
192
and overflow handling should be triggered.
193
"""
194
195
class PageBreakException(Exception):
196
"""
197
Raised to force a page break at current position.
198
199
Used by page break elements and manual break requests to
200
interrupt normal flow and move to next page.
201
"""
202
```
203
204
### Flowable Layout Interface
205
206
Core interface that all flowable content must implement for participation in the layout system.
207
208
```python { .api }
209
class Flowable(Styled):
210
"""
211
Base class for content that can flow through containers.
212
213
All document content elements inherit from Flowable and implement
214
the flow/render protocol for layout system integration.
215
"""
216
217
def flow(self, container, last_descender, state=None):
218
"""
219
Flow content into container with layout state management.
220
221
Parameters:
222
- container: Container to flow content into
223
- last_descender: Dimension, previous content's descender
224
- state: layout state from previous flow attempt
225
226
Returns:
227
- (width, height, baseline, state): layout result tuple
228
"""
229
...
230
231
def render(self, container, descender, state=None, first_line_only=False):
232
"""
233
Render content at current container position.
234
235
Parameters:
236
- container: Container to render content in
237
- descender: Dimension, descender from previous content
238
- state: layout state for partial rendering
239
- first_line_only: bool, render only first line if True
240
241
Returns:
242
- layout state for continuation if needed
243
"""
244
...
245
246
def initial_state(self, container):
247
"""
248
Create initial layout state for this flowable.
249
250
Parameters:
251
- container: Container where flowable will be placed
252
253
Returns:
254
- initial state object for flow/render operations
255
"""
256
...
257
```
258
259
## Usage Examples
260
261
### Basic Container Usage
262
263
```python
264
from rinohtype.layout import Container, DownExpandingContainer
265
from rinohtype.dimension import PT, MM
266
from rinohtype.paragraph import Paragraph
267
268
# Create parent container (page area)
269
page_container = Container(
270
name='page',
271
parent=None,
272
left=25*MM,
273
top=25*MM,
274
width=160*MM,
275
height=247*MM # A4 with margins
276
)
277
278
# Create content container that expands downward
279
content_container = DownExpandingContainer(
280
name='content',
281
parent=page_container,
282
left=0*PT,
283
top=0*PT,
284
width=160*MM
285
)
286
287
# Flow content into container
288
paragraph = Paragraph("This is sample content that will flow into the container.")
289
result = paragraph.flow(content_container, last_descender=0*PT)
290
291
if result:
292
width, height, baseline, state = result
293
paragraph.render(content_container, descender=0*PT, state=state)
294
content_container.advance(height)
295
```
296
297
### Container Chain for Multi-Page Layout
298
299
```python
300
from rinohtype.layout import Chain, ChainedContainer
301
302
# Create container chain for multi-column or multi-page layout
303
main_chain = Chain('main_content')
304
305
# Create first page container
306
page1_container = ChainedContainer(
307
name='page1',
308
parent=None,
309
left=25*MM,
310
top=25*MM,
311
width=160*MM,
312
height=220*MM
313
)
314
main_chain.append(page1_container)
315
316
# Create second page container (linked automatically)
317
page2_container = ChainedContainer(
318
name='page2',
319
parent=None,
320
left=25*MM,
321
top=25*MM,
322
width=160*MM,
323
height=220*MM
324
)
325
main_chain.append(page2_container)
326
327
# Flow long content across pages
328
long_content = [
329
Paragraph("First paragraph..."),
330
Paragraph("Second paragraph..."),
331
# ... more content
332
]
333
334
current_container = main_chain.first_container
335
for paragraph in long_content:
336
try:
337
result = paragraph.flow(current_container, last_descender=0*PT)
338
if result:
339
paragraph.render(current_container, descender=0*PT)
340
except ContainerOverflow:
341
# Move to next container in chain
342
current_container = current_container.next
343
if current_container:
344
result = paragraph.flow(current_container, last_descender=0*PT)
345
paragraph.render(current_container, descender=0*PT)
346
```
347
348
### Footnote Container Usage
349
350
```python
351
from rinohtype.layout import FootnoteContainer
352
from rinohtype.reference import Note
353
354
# Create footnote container at bottom of page
355
footnote_container = FootnoteContainer(
356
name='footnotes',
357
parent=page_container,
358
left=0*PT,
359
bottom=0*PT,
360
width=160*MM
361
)
362
363
# Add footnotes to container
364
footnote1 = Note([Paragraph("This is a footnote.")])
365
footnote2 = Note([Paragraph("Another footnote with more content.")])
366
367
footnote_container.add_footnote(footnote1)
368
footnote_container.add_footnote(footnote2)
369
```
370
371
### Virtual Container for Measurements
372
373
```python
374
from rinohtype.layout import VirtualContainer
375
376
# Create virtual container for measuring content
377
virtual = VirtualContainer(content_container)
378
379
# Measure content without placing it
380
test_paragraph = Paragraph("Test content for measurement")
381
result = test_paragraph.flow(virtual, last_descender=0*PT)
382
383
if result:
384
width, height, baseline, state = result
385
print(f"Content would be {width} wide and {height} tall")
386
387
# Now place it for real if it fits
388
if height <= content_container.remaining_height:
389
test_paragraph.render(content_container, descender=0*PT, state=state)
390
```
391
392
### Error Handling
393
394
```python
395
from rinohtype.layout import ContainerOverflow, EndOfContainer, PageBreakException
396
397
def flow_content_safely(flowable, container):
398
"""Flow content with proper error handling."""
399
try:
400
result = flowable.flow(container, last_descender=0*PT)
401
if result:
402
flowable.render(container, descender=0*PT)
403
return True
404
except ContainerOverflow as e:
405
print(f"Content overflow in {e.container.name}")
406
# Handle overflow - move to next page/container
407
return False
408
except EndOfContainer:
409
print("Reached end of container")
410
return False
411
except PageBreakException:
412
print("Page break requested")
413
# Handle page break
414
return False
415
```
416
417
### Custom Container Implementation
418
419
```python
420
from rinohtype.layout import FlowablesContainer
421
422
class CustomContainer(FlowablesContainer):
423
"""Custom container with special layout behavior."""
424
425
def __init__(self, name, parent, **kwargs):
426
super().__init__(name, parent, **kwargs)
427
self.special_mode = True
428
429
def render_flowable(self, flowable, last_descender, state, first_line_only=False):
430
"""Custom rendering logic."""
431
if self.special_mode:
432
# Apply custom layout rules
433
pass
434
435
return super().render_flowable(flowable, last_descender, state, first_line_only)
436
437
def advance(self, height):
438
"""Custom cursor advancement."""
439
if self.special_mode:
440
# Custom spacing rules
441
height = height * 1.1
442
443
super().advance(height)
444
```