dreader-application/examples/word_selection_highlighting.py
Duncan Tourolle 5a573c901e
Some checks failed
Python CI / test (3.13) (push) Successful in 7m3s
Python CI / test (3.12) (push) Failing after 3h8m53s
fixed word highlighting issue, added test for examples
2025-11-10 18:31:02 +01:00

361 lines
11 KiB
Python

#!/usr/bin/env python3
"""
Example: Word Selection and Highlighting
This example demonstrates how to:
1. Query a pixel location to find a word
2. Select a range of words between two points
3. Highlight selected words by drawing overlays
4. Handle tap gestures to select words
This is useful for:
- Word definition lookup
- Text highlighting/annotation
- Copy/paste functionality
- Interactive reading features
"""
from PIL import Image, ImageDraw
import numpy as np
from dreader import EbookReader, TouchEvent, GestureType
from pyWebLayout.core.query import QueryResult
def draw_highlight(image: Image.Image, bounds: tuple, color: tuple = (255, 255, 0, 100)):
"""
Draw a highlight overlay on an image at the given bounds.
Args:
image: PIL Image to draw on
bounds: (x, y, width, height) tuple
color: RGBA color tuple (with alpha for transparency)
"""
# Create a semi-transparent overlay
overlay = Image.new('RGBA', image.size, (255, 255, 255, 0))
draw = ImageDraw.Draw(overlay)
x, y, w, h = bounds
# Draw rectangle with rounded corners for nicer appearance
draw.rectangle([x, y, x + w, y + h], fill=color)
# Composite the overlay onto the original image
image = Image.alpha_composite(image.convert('RGBA'), overlay)
return image
def example_1_single_word_selection():
"""Example 1: Select and highlight a single word by tapping"""
print("=" * 60)
print("Example 1: Single Word Selection")
print("=" * 60)
# Create reader and load a book
reader = EbookReader(page_size=(800, 1000))
success = reader.load_epub("tests/data/test.epub")
if not success:
print("Failed to load EPUB")
return
print(f"Loaded: {reader.book_title} by {reader.book_author}")
# Get current page as image
page_img = reader.get_current_page()
if not page_img:
print("No page rendered")
return
# Simulate a tap at coordinates (200, 300)
tap_x, tap_y = 200, 300
print(f"\nSimulating tap at ({tap_x}, {tap_y})")
# Query what's at that location
result = reader.query_pixel(tap_x, tap_y)
if result and result.text:
print(f"Found word: '{result.text}'")
print(f"Type: {result.object_type}")
print(f"Bounds: {result.bounds}")
print(f"Is interactive: {result.is_interactive}")
# Highlight the word
highlighted_img = draw_highlight(page_img, result.bounds, color=(255, 255, 0, 80))
highlighted_img.save("output_single_word_highlight.png")
print(f"\nSaved highlighted image to: output_single_word_highlight.png")
else:
print("No word found at that location")
reader.close()
def example_2_range_selection():
"""Example 2: Select and highlight a range of words (text selection)"""
print("\n" + "=" * 60)
print("Example 2: Range Selection (Multi-word)")
print("=" * 60)
# Create reader and load a book
reader = EbookReader(page_size=(800, 1000))
success = reader.load_epub("tests/data/test.epub")
if not success:
print("Failed to load EPUB")
return
# Get current page
page_img = reader.get_current_page()
if not page_img:
return
# Simulate dragging from (100, 200) to (400, 250)
start_x, start_y = 100, 200
end_x, end_y = 400, 250
print(f"Simulating selection from ({start_x}, {start_y}) to ({end_x}, {end_y})")
# Create drag gesture events
drag_start = TouchEvent(GestureType.DRAG_START, start_x, start_y)
drag_move = TouchEvent(GestureType.DRAG_MOVE, end_x, end_y)
drag_end = TouchEvent(GestureType.DRAG_END, end_x, end_y)
# Handle the gesture (business logic)
reader.handle_touch(drag_start)
reader.handle_touch(drag_move)
response = reader.handle_touch(drag_end)
if response.action == "selection_complete":
selected_text = response.data.get('text', '')
bounds_list = response.data.get('bounds', [])
word_count = response.data.get('word_count', 0)
print(f"\nSelected {word_count} words:")
print(f"Text: \"{selected_text}\"")
# Highlight all selected words
highlighted_img = page_img
for bounds in bounds_list:
highlighted_img = draw_highlight(
highlighted_img,
bounds,
color=(100, 200, 255, 80) # Light blue highlight
)
highlighted_img.save("output_range_highlight.png")
print(f"\nSaved highlighted image to: output_range_highlight.png")
else:
print(f"Selection action: {response.action}")
reader.close()
def example_3_interactive_word_lookup():
"""Example 3: Interactive word lookup with gesture handling"""
print("\n" + "=" * 60)
print("Example 3: Interactive Word Lookup (with Gestures)")
print("=" * 60)
# Create reader
reader = EbookReader(page_size=(800, 1000))
success = reader.load_epub("tests/data/test.epub")
if not success:
print("Failed to load EPUB")
return
# Get page
page_img = reader.get_current_page()
if not page_img:
return
# Define some simulated touch events
test_gestures = [
("Tap at (250, 300)", TouchEvent(GestureType.TAP, 250, 300)),
("Long press at (250, 300)", TouchEvent(GestureType.LONG_PRESS, 250, 300)),
("Swipe left", TouchEvent(GestureType.SWIPE_LEFT, 600, 500)),
]
for description, event in test_gestures:
print(f"\n{description}:")
response = reader.handle_touch(event)
print(f" Action: {response.action}")
if response.action == "word_selected":
word = response.data.get('word', '')
bounds = response.data.get('bounds', (0, 0, 0, 0))
print(f" Selected word: '{word}'")
print(f" Bounds: {bounds}")
# Highlight the word
highlighted_img = draw_highlight(page_img, bounds, color=(255, 200, 0, 100))
filename = f"output_word_lookup_{word}.png"
highlighted_img.save(filename)
print(f" Saved: {filename}")
elif response.action == "define":
word = response.data.get('word', '')
print(f" Show definition for: '{word}'")
# In real app, you'd call a dictionary API here
elif response.action == "page_turn":
direction = response.data.get('direction', '')
progress = response.data.get('progress', 0)
print(f" Page turn {direction}, progress: {progress:.1%}")
reader.close()
def example_4_multi_word_annotation():
"""Example 4: Annotate multiple words with different colors"""
print("\n" + "=" * 60)
print("Example 4: Multi-word Annotation")
print("=" * 60)
# Create reader
reader = EbookReader(page_size=(800, 1000))
success = reader.load_epub("tests/data/test.epub")
if not success:
print("Failed to load EPUB")
return
# Get page
page_img = reader.get_current_page()
if not page_img:
return
# Simulate multiple taps at different locations
tap_locations = [
(150, 200, "Important word", (255, 100, 100, 80)), # Red
(300, 200, "Key concept", (100, 255, 100, 80)), # Green
(450, 200, "Notable term", (100, 100, 255, 80)), # Blue
]
annotated_img = page_img
annotations = []
for x, y, label, color in tap_locations:
result = reader.query_pixel(x, y)
if result and result.text:
print(f"\nFound word at ({x}, {y}): '{result.text}'")
print(f" Annotation: {label}")
# Highlight with specific color
annotated_img = draw_highlight(annotated_img, result.bounds, color)
annotations.append({
'word': result.text,
'label': label,
'bounds': result.bounds
})
# Save annotated image
annotated_img.save("output_multi_annotation.png")
print(f"\nSaved annotated image with {len(annotations)} highlights")
print("File: output_multi_annotation.png")
# Print annotation summary
print("\nAnnotation Summary:")
for i, ann in enumerate(annotations, 1):
print(f" {i}. '{ann['word']}' - {ann['label']}")
reader.close()
def example_5_link_highlighting():
"""Example 5: Find and highlight all links on a page"""
print("\n" + "=" * 60)
print("Example 5: Find and Highlight All Links")
print("=" * 60)
# Create reader
reader = EbookReader(page_size=(800, 1000))
success = reader.load_epub("tests/data/test.epub")
if not success:
print("Failed to load EPUB")
return
# Get page
page_img = reader.get_current_page()
if not page_img:
return
# Get the page object to scan for links
page = reader.manager.get_current_page()
# Scan through all rendered content to find links
links_found = []
from pyWebLayout.concrete.text import Line
from pyWebLayout.concrete.functional import LinkText
for child in page._children:
if isinstance(child, Line):
for text_obj in child._text_objects:
if isinstance(text_obj, LinkText):
origin = text_obj._origin
size = text_obj.size
bounds = (
int(origin[0]),
int(origin[1]),
int(size[0]),
int(size[1])
)
links_found.append({
'text': text_obj._text,
'target': text_obj._link.location,
'bounds': bounds
})
print(f"Found {len(links_found)} links on page")
# Highlight all links
highlighted_img = page_img
for link in links_found:
print(f"\nLink: '{link['text']}'{link['target']}")
highlighted_img = draw_highlight(
highlighted_img,
link['bounds'],
color=(0, 150, 255, 100) # Blue for links
)
if links_found:
highlighted_img.save("output_links_highlighted.png")
print(f"\nSaved image with {len(links_found)} highlighted links")
print("File: output_links_highlighted.png")
else:
print("\nNo links found on this page")
reader.close()
if __name__ == "__main__":
print("Word Selection and Highlighting Examples")
print("=" * 60)
print()
print("These examples demonstrate the query system for:")
print("- Single word selection")
print("- Range selection (multiple words)")
print("- Interactive gesture handling")
print("- Multi-word annotation")
print("- Link detection and highlighting")
print()
try:
# Run all examples
example_1_single_word_selection()
example_2_range_selection()
example_3_interactive_word_lookup()
example_4_multi_word_annotation()
example_5_link_highlighting()
print("\n" + "=" * 60)
print("All examples completed successfully!")
print("=" * 60)
except Exception as e:
print(f"\nError running examples: {e}")
import traceback
traceback.print_exc()