253 lines
7.8 KiB
Python
253 lines
7.8 KiB
Python
"""
|
|
Generate a demo image for README.md showing font family switching feature.
|
|
|
|
Creates a side-by-side comparison of the same content rendered in
|
|
Sans, Serif, and Monospace fonts.
|
|
"""
|
|
|
|
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, ImageDraw, ImageFont
|
|
|
|
|
|
def create_demo_content():
|
|
"""Create concise demo content that fits nicely on a small page"""
|
|
blocks = []
|
|
|
|
# Title
|
|
title_font = Font.from_family(BundledFont.SANS, font_size=28, weight=FontWeight.BOLD)
|
|
title = Heading(level=HeadingLevel.H1, style=title_font)
|
|
for word in "The Adventure Begins".split():
|
|
title.add_word(Word(word, title_font))
|
|
blocks.append(title)
|
|
|
|
# Paragraph
|
|
body_font = Font.from_family(BundledFont.SANS, font_size=14)
|
|
para = Paragraph(body_font)
|
|
text = (
|
|
"In the quiet village of Millbrook, young Emma discovered an ancient map "
|
|
"hidden in her grandmother's attic. The parchment revealed a mysterious "
|
|
"forest path marked with symbols she had never seen before. With courage "
|
|
"in her heart and the map in her pocket, she set out at dawn to uncover "
|
|
"the secrets that lay beyond the old oak trees."
|
|
)
|
|
for word in text.split():
|
|
para.add_word(Word(word, body_font))
|
|
blocks.append(para)
|
|
|
|
return blocks
|
|
|
|
|
|
def render_with_font_family(blocks, page_size, font_family, family_name):
|
|
"""Render a page with a specific font family"""
|
|
manager = create_ereader_manager(
|
|
blocks,
|
|
page_size,
|
|
document_id=f"demo_{family_name.lower()}"
|
|
)
|
|
|
|
# Set font family (None means original/default)
|
|
manager.set_font_family(font_family)
|
|
|
|
# Get the first page
|
|
page = manager.get_current_page()
|
|
return page.render()
|
|
|
|
|
|
def create_comparison_image():
|
|
"""Create a side-by-side comparison of all three font families"""
|
|
|
|
# Page size for each panel
|
|
page_width = 400
|
|
page_height = 300
|
|
|
|
# Create demo content
|
|
print("Creating demo content...")
|
|
blocks = create_demo_content()
|
|
|
|
# Render with each font family
|
|
print("Rendering with Sans font...")
|
|
sans_image = render_with_font_family(
|
|
blocks, (page_width, page_height), BundledFont.SANS, "Sans"
|
|
)
|
|
|
|
print("Rendering with Serif font...")
|
|
serif_image = render_with_font_family(
|
|
blocks, (page_width, page_height), BundledFont.SERIF, "Serif"
|
|
)
|
|
|
|
print("Rendering with Monospace font...")
|
|
mono_image = render_with_font_family(
|
|
blocks, (page_width, page_height), BundledFont.MONOSPACE, "Monospace"
|
|
)
|
|
|
|
# Create a composite image with all three side by side
|
|
spacing = 20
|
|
label_height = 30
|
|
total_width = page_width * 3 + spacing * 4
|
|
total_height = page_height + label_height + spacing * 2
|
|
|
|
composite = Image.new('RGB', (total_width, total_height), color='#f5f5f5')
|
|
|
|
# Paste the three images
|
|
x_positions = [
|
|
spacing,
|
|
spacing * 2 + page_width,
|
|
spacing * 3 + page_width * 2
|
|
]
|
|
|
|
for img, x_pos in zip([sans_image, serif_image, mono_image], x_positions):
|
|
composite.paste(img, (x_pos, label_height + spacing))
|
|
|
|
# Add labels
|
|
draw = ImageDraw.Draw(composite)
|
|
|
|
# Try to use a nice font, fallback to default if not available
|
|
try:
|
|
label_font = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf", 20)
|
|
except:
|
|
label_font = ImageFont.load_default()
|
|
|
|
labels = ["Sans-Serif", "Serif", "Monospace"]
|
|
for label, x_pos in zip(labels, x_positions):
|
|
# Calculate text position to center it
|
|
bbox = draw.textbbox((0, 0), label, font=label_font)
|
|
text_width = bbox[2] - bbox[0]
|
|
text_x = x_pos + (page_width - text_width) // 2
|
|
|
|
draw.text((text_x, 5), label, fill='#333333', font=label_font)
|
|
|
|
# Save the image
|
|
output_path = "docs/images/font_family_switching.png"
|
|
composite.save(output_path, quality=95)
|
|
print(f"\n✓ Saved demo image to: {output_path}")
|
|
print(f" Image size: {total_width}x{total_height}")
|
|
|
|
return output_path
|
|
|
|
|
|
def create_single_vertical_comparison():
|
|
"""Create a vertical comparison that's better for README"""
|
|
|
|
# Page size for each panel
|
|
page_width = 700
|
|
page_height = 280
|
|
|
|
# Create demo content
|
|
print("\nCreating vertical comparison for README...")
|
|
blocks = create_demo_content()
|
|
|
|
# Render with each font family
|
|
print(" Rendering Sans...")
|
|
sans_image = render_with_font_family(
|
|
blocks, (page_width, page_height), BundledFont.SANS, "Sans"
|
|
)
|
|
|
|
print(" Rendering Serif...")
|
|
serif_image = render_with_font_family(
|
|
blocks, (page_width, page_height), BundledFont.SERIF, "Serif"
|
|
)
|
|
|
|
print(" Rendering Monospace...")
|
|
mono_image = render_with_font_family(
|
|
blocks, (page_width, page_height), BundledFont.MONOSPACE, "Monospace"
|
|
)
|
|
|
|
# Create a composite image stacked vertically
|
|
spacing = 15
|
|
label_width = 120
|
|
total_width = page_width + label_width + spacing * 2
|
|
total_height = page_height * 3 + spacing * 4
|
|
|
|
composite = Image.new('RGB', (total_width, total_height), color='#ffffff')
|
|
|
|
# Add a subtle border
|
|
draw = ImageDraw.Draw(composite)
|
|
draw.rectangle([(0, 0), (total_width-1, total_height-1)], outline='#e0e0e0', width=1)
|
|
|
|
# Paste the three images vertically
|
|
y_positions = [
|
|
spacing,
|
|
spacing * 2 + page_height,
|
|
spacing * 3 + page_height * 2
|
|
]
|
|
|
|
images_data = [
|
|
(sans_image, "Sans-Serif", "#4A90E2"),
|
|
(serif_image, "Serif", "#E94B3C"),
|
|
(mono_image, "Monospace", "#50C878")
|
|
]
|
|
|
|
# Try to use a nice font
|
|
try:
|
|
label_font = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf", 16)
|
|
small_font = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf", 11)
|
|
except:
|
|
label_font = ImageFont.load_default()
|
|
small_font = ImageFont.load_default()
|
|
|
|
for (img, label, color), y_pos in zip(images_data, y_positions):
|
|
# Paste the page image
|
|
composite.paste(img, (label_width + spacing, y_pos))
|
|
|
|
# Draw label background
|
|
draw.rectangle(
|
|
[(spacing, y_pos + 10), (label_width, y_pos + 40)],
|
|
fill=color
|
|
)
|
|
|
|
# Draw label text
|
|
draw.text(
|
|
(spacing + 10, y_pos + 17),
|
|
label,
|
|
fill='#ffffff',
|
|
font=label_font
|
|
)
|
|
|
|
# Draw font description
|
|
descriptions = {
|
|
"Sans-Serif": "Clean & Modern",
|
|
"Serif": "Classic & Formal",
|
|
"Monospace": "Code & Technical"
|
|
}
|
|
draw.text(
|
|
(spacing + 5, y_pos + 50),
|
|
descriptions[label],
|
|
fill='#666666',
|
|
font=small_font
|
|
)
|
|
|
|
# Save the image
|
|
output_path = "docs/images/font_family_switching_vertical.png"
|
|
composite.save(output_path, quality=95)
|
|
print(f" ✓ Saved: {output_path}")
|
|
print(f" Size: {total_width}x{total_height}")
|
|
|
|
return output_path
|
|
|
|
|
|
if __name__ == "__main__":
|
|
print("=" * 70)
|
|
print("Generating README Demo Images")
|
|
print("=" * 70)
|
|
|
|
# Create both versions
|
|
horizontal_path = create_comparison_image()
|
|
vertical_path = create_single_vertical_comparison()
|
|
|
|
print("\n" + "=" * 70)
|
|
print("Demo images generated successfully!")
|
|
print("=" * 70)
|
|
print(f"\nHorizontal comparison: {horizontal_path}")
|
|
print(f"Vertical comparison: {vertical_path}")
|
|
print("\nRecommended for README: vertical version")
|
|
print("\nMarkdown snippet:")
|
|
print("```markdown")
|
|
print("")
|
|
print("```")
|
|
print()
|