251 lines
8.5 KiB
Python
251 lines
8.5 KiB
Python
#!/usr/bin/env python3
|
||
"""
|
||
Simple demonstration of mono-space font testing concepts.
|
||
"""
|
||
|
||
from pyWebLayout.concrete.text import Text, Line
|
||
from pyWebLayout.style.fonts import Font
|
||
from pyWebLayout.style.layout import Alignment
|
||
|
||
def main():
|
||
print("=== Mono-space Font Testing Demo ===\n")
|
||
|
||
# Create a regular font
|
||
font = Font(font_size=12)
|
||
|
||
print("1. Character Width Variance Analysis:")
|
||
print("-" * 40)
|
||
|
||
# Test different characters to show width variance
|
||
test_chars = "iIlLmMwW"
|
||
widths = {}
|
||
|
||
for char in test_chars:
|
||
text = Text(char, font)
|
||
widths[char] = text.width
|
||
print(f" '{char}': {text.width:3d}px")
|
||
|
||
min_w = min(widths.values())
|
||
max_w = max(widths.values())
|
||
variance = max_w - min_w
|
||
|
||
print(f"\n Range: {min_w}-{max_w}px (variance: {variance}px)")
|
||
print(f" Ratio: {max_w/min_w:.1f}x difference")
|
||
|
||
print("\n2. Why This Matters for Testing:")
|
||
print("-" * 40)
|
||
|
||
# Show how same-length strings have different widths
|
||
word1 = "ill" # narrow
|
||
word2 = "WWW" # wide
|
||
|
||
text1 = Text(word1, font)
|
||
text2 = Text(word2, font)
|
||
|
||
print(f" '{word1}' (3 chars): {text1.width}px")
|
||
print(f" '{word2}' (3 chars): {text2.width}px")
|
||
print(f" Same length, {abs(text1.width - text2.width)}px difference!")
|
||
|
||
print("\n3. Line Capacity Prediction:")
|
||
print("-" * 40)
|
||
|
||
line_width = 100
|
||
print(f" Line width: {line_width}px")
|
||
|
||
# Test how many characters fit
|
||
test_cases = [
|
||
("narrow chars", "i" * 20),
|
||
("wide chars", "W" * 10),
|
||
("mixed text", "Hello World")
|
||
]
|
||
|
||
for name, text_str in test_cases:
|
||
text_obj = Text(text_str, font)
|
||
fits = "YES" if text_obj.width <= line_width else "NO"
|
||
print(f" {name:12}: '{text_str[:10]}...' ({len(text_str)} chars, {text_obj.width}px) → {fits}")
|
||
|
||
print("\n4. With Mono-space Fonts:")
|
||
print("-" * 40)
|
||
|
||
# Try to use an actual mono-space font
|
||
mono_font = None
|
||
mono_paths = [
|
||
"/usr/share/fonts/truetype/dejavu/DejaVuSansMono.ttf",
|
||
"/usr/share/fonts/truetype/liberation/LiberationMono-Regular.ttf",
|
||
"/System/Library/Fonts/Monaco.ttf",
|
||
"C:/Windows/Fonts/consola.ttf"
|
||
]
|
||
|
||
import os
|
||
for path in mono_paths:
|
||
if os.path.exists(path):
|
||
try:
|
||
mono_font = Font(font_path=path, font_size=12)
|
||
print(f" Using actual mono-space font: {os.path.basename(path)}")
|
||
break
|
||
except:
|
||
continue
|
||
|
||
if mono_font:
|
||
# Test actual mono-space character consistency
|
||
mono_test_chars = "iIlLmMwW"
|
||
mono_widths = {}
|
||
|
||
for char in mono_test_chars:
|
||
text = Text(char, mono_font)
|
||
mono_widths[char] = text.width
|
||
|
||
mono_min = min(mono_widths.values())
|
||
mono_max = max(mono_widths.values())
|
||
mono_variance = mono_max - mono_min
|
||
|
||
print(f" Mono-space character widths:")
|
||
for char, width in mono_widths.items():
|
||
print(f" '{char}': {width}px")
|
||
print(f" Range: {mono_min}-{mono_max}px (variance: {mono_variance}px)")
|
||
|
||
# Compare to regular font variance
|
||
regular_variance = max_w - min_w
|
||
improvement = regular_variance / max(1, mono_variance)
|
||
print(f" Improvement: {improvement:.1f}x more consistent!")
|
||
|
||
# Test line capacity with actual mono-space
|
||
mono_char_width = mono_widths['M'] # Use actual width
|
||
capacity = line_width // mono_char_width
|
||
|
||
print(f"\n Actual mono-space line capacity:")
|
||
print(f" Each character: {mono_char_width}px")
|
||
print(f" Line capacity: {capacity} characters")
|
||
|
||
# Prove consistency with different character combinations
|
||
test_strings = [
|
||
"i" * capacity,
|
||
"W" * capacity,
|
||
"M" * capacity,
|
||
"l" * capacity
|
||
]
|
||
|
||
print(f" Testing {capacity}-character strings:")
|
||
all_same_width = True
|
||
first_width = None
|
||
|
||
for test_str in test_strings:
|
||
text_obj = Text(test_str, mono_font)
|
||
if first_width is None:
|
||
first_width = text_obj.width
|
||
elif abs(text_obj.width - first_width) > 2: # Allow 2px tolerance
|
||
all_same_width = False
|
||
|
||
print(f" '{test_str[0]}' × {len(test_str)}: {text_obj.width}px")
|
||
|
||
if all_same_width:
|
||
print(f" ✓ ALL {capacity}-character strings have the same width!")
|
||
else:
|
||
print(f" ⚠ Some variance detected (font may not be perfectly mono-space)")
|
||
|
||
else:
|
||
print(" No mono-space font found - showing theoretical values:")
|
||
mono_char_width = 8 # Typical mono-space width
|
||
capacity = line_width // mono_char_width
|
||
|
||
print(f" Each character: {mono_char_width}px (theoretical)")
|
||
print(f" Line capacity: {capacity} characters")
|
||
print(f" ANY {capacity}-character string would fit!")
|
||
print(f" Layout calculations become simple math")
|
||
|
||
print("\n5. Line Fitting Test:")
|
||
print("-" * 40)
|
||
|
||
# Test actual line fitting
|
||
line = Line(
|
||
spacing=(2, 4),
|
||
origin=(0, 0),
|
||
size=(line_width, 20),
|
||
font=font,
|
||
halign=Alignment.LEFT
|
||
)
|
||
|
||
test_word = "development" # 11 characters
|
||
word_obj = Text(test_word, font)
|
||
|
||
print(f" Test word: '{test_word}' ({len(test_word)} chars, {word_obj.width}px)")
|
||
print(f" Line width: {line_width}px")
|
||
|
||
result = line.add_word(test_word, font)
|
||
|
||
if result is None:
|
||
print(" Result: Word fits completely")
|
||
else:
|
||
if line.text_objects:
|
||
added = line.text_objects[0].text
|
||
print(f" Result: Added '{added}', remaining '{result}'")
|
||
else:
|
||
print(" Result: Word rejected completely")
|
||
|
||
# Use actual mono font width if available, otherwise theoretical
|
||
if mono_font:
|
||
actual_mono_width = mono_widths['M']
|
||
print(f"\n With actual mono-space ({actual_mono_width}px/char):")
|
||
print(f" Word would be: {len(test_word)} × {actual_mono_width} = {len(test_word) * actual_mono_width}px")
|
||
|
||
if len(test_word) * actual_mono_width <= line_width:
|
||
print(" → Would fit completely")
|
||
else:
|
||
chars_that_fit = line_width // actual_mono_width
|
||
print(f" → Would need breaking after {chars_that_fit} characters")
|
||
else:
|
||
theoretical_mono_width = 8
|
||
print(f"\n With theoretical mono-space ({theoretical_mono_width}px/char):")
|
||
print(f" Word would be: {len(test_word)} × {theoretical_mono_width} = {len(test_word) * theoretical_mono_width}px")
|
||
|
||
if len(test_word) * theoretical_mono_width <= line_width:
|
||
print(" → Would fit completely")
|
||
else:
|
||
chars_that_fit = line_width // theoretical_mono_width
|
||
print(f" → Would need breaking after {chars_that_fit} characters")
|
||
|
||
print("\n=== Conclusion ===")
|
||
print("Mono-space fonts make testing predictable because:")
|
||
print("- Character width is constant")
|
||
print("- Line capacity is calculable")
|
||
print("- Word fitting is based on character count")
|
||
print("- Layout behavior is deterministic")
|
||
|
||
# Check if test_output directory exists, if so save a simple visual
|
||
import os
|
||
if os.path.exists("test_output"):
|
||
print(f"\nCreating visual test output...")
|
||
|
||
# Create a simple line rendering test
|
||
from pyWebLayout.concrete.page import Page, Container
|
||
|
||
page = Page(size=(400, 200))
|
||
|
||
container = Container(
|
||
origin=(0, 0),
|
||
size=(380, 180),
|
||
direction='vertical',
|
||
spacing=5,
|
||
padding=(10, 10, 10, 10)
|
||
)
|
||
|
||
# Add title
|
||
title = Text("Character Width Variance Demo", font)
|
||
container.add_child(title)
|
||
|
||
# Add test lines showing different characters
|
||
for char_type, char in [("Narrow", "i"), ("Wide", "W"), ("Average", "n")]:
|
||
line_text = f"{char_type}: {char * 10}"
|
||
text_obj = Text(line_text, font)
|
||
container.add_child(text_obj)
|
||
|
||
page.add_child(container)
|
||
image = page.render()
|
||
|
||
output_path = os.path.join("test_output", "monospace_demo.png")
|
||
image.save(output_path)
|
||
print(f"Visual demo saved to: {output_path}")
|
||
|
||
if __name__ == "__main__":
|
||
main()
|