or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

dimensions.mddocument-structure.mdgraphics-images.mdindex.mdlayout-engine.mdreferences.mdstyling-system.mdtemplate-system.mdtypography-text.md

layout-engine.mddocs/

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

```