pyWebLayout/examples/11_font_family_switching_demo.py
Duncan Tourolle 889f27e1a3
All checks were successful
Python CI / test (3.10) (push) Successful in 2m18s
Python CI / test (3.12) (push) Successful in 2m8s
Python CI / test (3.13) (push) Successful in 2m6s
added fotn change API and examples
2025-11-11 12:44:18 +01:00

241 lines
8.3 KiB
Python

"""
Demonstration of dynamic font family switching in the ereader.
This example shows how to:
1. Initialize an ereader with content
2. Dynamically switch between different font families (Sans, Serif, Monospace)
3. Maintain reading position across font changes
4. Use the font family API
The ereader manager provides a high-level API for changing fonts on-the-fly
without losing your place in the document.
"""
from pyWebLayout.abstract import Paragraph, Heading, Word
from pyWebLayout.abstract.block import HeadingLevel
from pyWebLayout.style import Font
from pyWebLayout.style.fonts import BundledFont, FontWeight
from pyWebLayout.style.page_style import PageStyle
from pyWebLayout.layout.ereader_manager import create_ereader_manager
from PIL import Image
def create_sample_content():
"""Create sample document content with various text styles"""
blocks = []
# Create a default font for the content
default_font = Font.from_family(BundledFont.SANS, font_size=16)
heading_font = Font.from_family(BundledFont.SANS, font_size=24, weight=FontWeight.BOLD)
# Title
title = Heading(level=HeadingLevel.H1, style=heading_font)
for word in "Font Family Switching Demo".split():
title.add_word(Word(word, heading_font))
blocks.append(title)
# Introduction paragraph
intro_font = Font.from_family(BundledFont.SANS, font_size=16)
intro = Paragraph(intro_font)
intro_text = (
"This demonstration shows how the ereader can dynamically switch between "
"different font families while maintaining your reading position. "
"The three bundled font families (Sans, Serif, and Monospace) can be "
"changed on-the-fly without recreating the document."
)
for word in intro_text.split():
intro.add_word(Word(word, intro_font))
blocks.append(intro)
# Section 1
section1_heading = Heading(level=HeadingLevel.H2, style=heading_font)
for word in "Sans-Serif Font".split():
section1_heading.add_word(Word(word, heading_font))
blocks.append(section1_heading)
para1 = Paragraph(default_font)
text1 = (
"Sans-serif fonts like DejaVu Sans are clean and modern, making them "
"ideal for screen reading. They lack the decorative strokes (serifs) "
"found in traditional typefaces, which can improve legibility on digital displays. "
"Many ereader applications default to sans-serif fonts for this reason."
)
for word in text1.split():
para1.add_word(Word(word, default_font))
blocks.append(para1)
# Section 2
section2_heading = Heading(level=HeadingLevel.H2, style=heading_font)
for word in "Serif Font".split():
section2_heading.add_word(Word(word, heading_font))
blocks.append(section2_heading)
para2 = Paragraph(default_font)
text2 = (
"Serif fonts like DejaVu Serif have small decorative strokes at the ends "
"of letter strokes. These fonts are traditionally used in print media and "
"can give a more formal, classic appearance. Many readers prefer serif fonts "
"for long-form reading as they find them easier on the eyes."
)
for word in text2.split():
para2.add_word(Word(word, default_font))
blocks.append(para2)
# Section 3
section3_heading = Heading(level=HeadingLevel.H2, style=heading_font)
for word in "Monospace Font".split():
section3_heading.add_word(Word(word, heading_font))
blocks.append(section3_heading)
para3 = Paragraph(default_font)
text3 = (
"Monospace fonts like DejaVu Sans Mono have equal spacing between all characters. "
"They are commonly used for displaying code, technical documentation, and typewriter-style "
"text. While less common for general reading, some users prefer the uniform character "
"spacing for certain types of content."
)
for word in text3.split():
para3.add_word(Word(word, default_font))
blocks.append(para3)
# Final paragraph
conclusion = Paragraph(default_font)
conclusion_text = (
"The ability to switch fonts dynamically is a key feature of modern ereaders. "
"It allows readers to customize their reading experience based on personal preference, "
"lighting conditions, and content type. Try switching between the three font families "
"to see which one you prefer for different types of reading."
)
for word in conclusion_text.split():
conclusion.add_word(Word(word, default_font))
blocks.append(conclusion)
return blocks
def render_pages_with_different_fonts(manager, output_prefix="demo_11"):
"""Render the same page with different font families"""
print("\nRendering pages with different font families...")
print("=" * 70)
font_families = [
(None, "Original (Sans)"),
(BundledFont.SERIF, "Serif"),
(BundledFont.MONOSPACE, "Monospace"),
(BundledFont.SANS, "Sans (explicit)")
]
images = []
for font_family, name in font_families:
print(f"\nRendering with {name} font...")
# Switch font family
manager.set_font_family(font_family)
# Get current page
page = manager.get_current_page()
# Render to image
image = page.render()
filename = f"{output_prefix}_{name.lower().replace(' ', '_').replace('(', '').replace(')', '')}.png"
image.save(filename)
print(f" Saved: {filename}")
images.append((name, image))
return images
def demonstrate_font_switching():
"""Main demonstration function"""
print("\n")
print("=" * 70)
print("Font Family Switching Demonstration")
print("=" * 70)
print()
# Create sample content
print("Creating sample document...")
blocks = create_sample_content()
print(f" Created {len(blocks)} blocks")
# Initialize ereader manager
print("\nInitializing ereader manager...")
page_size = (600, 800)
manager = create_ereader_manager(
blocks,
page_size,
document_id="font_switching_demo"
)
print(f" Page size: {page_size[0]}x{page_size[1]}")
print(f" Initial font family: {manager.get_font_family()}")
# Render pages with different fonts
images = render_pages_with_different_fonts(manager)
# Show position info
print("\nPosition information after font switches:")
print(" " + "-" * 66)
pos_info = manager.get_position_info()
print(f" Current position: Block {pos_info['position']['block_index']}, "
f"Word {pos_info['position']['word_index']}")
print(f" Font family: {pos_info['font_family'] or 'Original'}")
print(f" Font scale: {pos_info['font_scale']}")
print(f" Reading progress: {pos_info['progress']:.1%}")
# Test navigation with font switching
print("\nTesting navigation with font switching...")
print(" " + "-" * 66)
# Reset to beginning
manager.jump_to_position(manager.current_position.__class__())
# Advance a few pages with serif font
manager.set_font_family(BundledFont.SERIF)
print(f" Switched to SERIF font")
for i in range(3):
next_page = manager.next_page()
if next_page:
print(f" Page {i+2}: Advanced successfully")
# Switch to monospace
manager.set_font_family(BundledFont.MONOSPACE)
print(f" Switched to MONOSPACE font")
current_page = manager.get_current_page()
print(f" Re-rendered current page with new font")
# Go back a page
prev_page = manager.previous_page()
if prev_page:
print(f" Navigated back successfully")
# Cache statistics
print("\nCache statistics:")
print(" " + "-" * 66)
stats = manager.get_cache_stats()
for key, value in stats.items():
print(f" {key}: {value}")
print()
print("=" * 70)
print("Demo complete!")
print()
print("Key features demonstrated:")
print(" ✓ Dynamic font family switching (Sans, Serif, Monospace)")
print(" ✓ Position preservation across font changes")
print(" ✓ Automatic cache invalidation on font change")
print(" ✓ Navigation with different fonts")
print(" ✓ Font family info in position tracking")
print()
print("The rendered pages show the same content in different font families.")
print("Notice how the layout adapts while maintaining readability.")
print("=" * 70)
print()
if __name__ == "__main__":
demonstrate_font_switching()