361 lines
11 KiB
Python
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()
|