pyPhotoAlbum/tests/test_page_renderer.py
2025-11-11 16:02:02 +00:00

464 lines
15 KiB
Python

"""
Unit tests for PageRenderer coordinate transformations
"""
import pytest
from pyPhotoAlbum.page_renderer import PageRenderer
class TestPageRendererCoordinates:
"""Test coordinate transformation methods"""
def test_page_to_screen_no_zoom_no_pan(self):
"""Test page_to_screen conversion with zoom=1.0 and no pan"""
renderer = PageRenderer(
page_width_mm=210.0,
page_height_mm=297.0,
screen_x=100.0,
screen_y=200.0,
dpi=96,
zoom=1.0
)
# Element at page origin should map to screen_x, screen_y
screen_x, screen_y = renderer.page_to_screen(0, 0)
assert screen_x == 100.0
assert screen_y == 200.0
# Element at (50, 75) should be offset by that amount
screen_x, screen_y = renderer.page_to_screen(50, 75)
assert screen_x == 150.0
assert screen_y == 275.0
def test_page_to_screen_with_zoom(self):
"""Test page_to_screen conversion with zoom applied"""
renderer = PageRenderer(
page_width_mm=210.0,
page_height_mm=297.0,
screen_x=100.0,
screen_y=200.0,
dpi=96,
zoom=2.0
)
# With zoom=2.0, distances should be doubled
screen_x, screen_y = renderer.page_to_screen(50, 75)
assert screen_x == 200.0 # 100 + 50*2
assert screen_y == 350.0 # 200 + 75*2
def test_page_to_screen_with_fractional_zoom(self):
"""Test page_to_screen conversion with fractional zoom"""
renderer = PageRenderer(
page_width_mm=210.0,
page_height_mm=297.0,
screen_x=100.0,
screen_y=200.0,
dpi=96,
zoom=0.5
)
# With zoom=0.5, distances should be halved
screen_x, screen_y = renderer.page_to_screen(100, 150)
assert screen_x == 150.0 # 100 + 100*0.5
assert screen_y == 275.0 # 200 + 150*0.5
def test_screen_to_page_no_zoom_no_pan(self):
"""Test screen_to_page conversion with zoom=1.0 and no pan"""
renderer = PageRenderer(
page_width_mm=210.0,
page_height_mm=297.0,
screen_x=100.0,
screen_y=200.0,
dpi=96,
zoom=1.0
)
# Screen position at screen_x, screen_y should map to page origin
page_x, page_y = renderer.screen_to_page(100.0, 200.0)
assert page_x == 0.0
assert page_y == 0.0
# Screen position offset should map to same offset in page coords
page_x, page_y = renderer.screen_to_page(150.0, 275.0)
assert page_x == 50.0
assert page_y == 75.0
def test_screen_to_page_with_zoom(self):
"""Test screen_to_page conversion with zoom applied"""
renderer = PageRenderer(
page_width_mm=210.0,
page_height_mm=297.0,
screen_x=100.0,
screen_y=200.0,
dpi=96,
zoom=2.0
)
# With zoom=2.0, screen distances should be divided by 2 to get page coords
page_x, page_y = renderer.screen_to_page(200.0, 350.0)
assert page_x == 50.0 # (200-100)/2
assert page_y == 75.0 # (350-200)/2
def test_roundtrip_conversion_no_zoom(self):
"""Test that page->screen->page conversion is accurate with no zoom"""
renderer = PageRenderer(
page_width_mm=210.0,
page_height_mm=297.0,
screen_x=100.0,
screen_y=200.0,
dpi=96,
zoom=1.0
)
# Start with page coordinates
orig_page_x, orig_page_y = 123.45, 678.90
# Convert to screen and back
screen_x, screen_y = renderer.page_to_screen(orig_page_x, orig_page_y)
page_x, page_y = renderer.screen_to_page(screen_x, screen_y)
# Should get back the original values
assert abs(page_x - orig_page_x) < 0.001
assert abs(page_y - orig_page_y) < 0.001
def test_roundtrip_conversion_with_zoom(self):
"""Test that page->screen->page conversion is accurate with zoom"""
renderer = PageRenderer(
page_width_mm=210.0,
page_height_mm=297.0,
screen_x=100.0,
screen_y=200.0,
dpi=96,
zoom=1.5
)
# Start with page coordinates
orig_page_x, orig_page_y = 123.45, 678.90
# Convert to screen and back
screen_x, screen_y = renderer.page_to_screen(orig_page_x, orig_page_y)
page_x, page_y = renderer.screen_to_page(screen_x, screen_y)
# Should get back the original values (with floating point tolerance)
assert abs(page_x - orig_page_x) < 0.001
assert abs(page_y - orig_page_y) < 0.001
def test_roundtrip_conversion_extreme_zoom(self):
"""Test coordinate conversion with extreme zoom levels"""
for zoom in [0.1, 0.5, 1.0, 2.0, 5.0]:
renderer = PageRenderer(
page_width_mm=210.0,
page_height_mm=297.0,
screen_x=50.0,
screen_y=100.0,
dpi=96,
zoom=zoom
)
orig_page_x, orig_page_y = 250.0, 400.0
screen_x, screen_y = renderer.page_to_screen(orig_page_x, orig_page_y)
page_x, page_y = renderer.screen_to_page(screen_x, screen_y)
assert abs(page_x - orig_page_x) < 0.001
assert abs(page_y - orig_page_y) < 0.001
class TestPageRendererBounds:
"""Test page bounds and point detection"""
def test_is_point_in_page_inside(self):
"""Test is_point_in_page for points inside the page"""
renderer = PageRenderer(
page_width_mm=210.0, # A4 width
page_height_mm=297.0, # A4 height
screen_x=100.0,
screen_y=200.0,
dpi=96,
zoom=1.0
)
# Calculate page dimensions in pixels
page_width_px = 210.0 * 96 / 25.4 # ~794 pixels
page_height_px = 297.0 * 96 / 25.4 # ~1123 pixels
# Point in center should be inside
center_x = 100.0 + page_width_px / 2
center_y = 200.0 + page_height_px / 2
assert renderer.is_point_in_page(center_x, center_y)
# Point at origin should be inside
assert renderer.is_point_in_page(100.0, 200.0)
# Point at bottom-right corner should be inside
assert renderer.is_point_in_page(
100.0 + page_width_px,
200.0 + page_height_px
)
def test_is_point_in_page_outside(self):
"""Test is_point_in_page for points outside the page"""
renderer = PageRenderer(
page_width_mm=210.0,
page_height_mm=297.0,
screen_x=100.0,
screen_y=200.0,
dpi=96,
zoom=1.0
)
# Point before page start
assert not renderer.is_point_in_page(50.0, 150.0)
# Point way beyond page
assert not renderer.is_point_in_page(2000.0, 2000.0)
# Point to the left of page
assert not renderer.is_point_in_page(50.0, 500.0)
# Point above page
assert not renderer.is_point_in_page(500.0, 150.0)
def test_is_point_in_page_with_zoom(self):
"""Test is_point_in_page with different zoom levels"""
for zoom in [0.5, 1.0, 2.0]:
renderer = PageRenderer(
page_width_mm=210.0,
page_height_mm=297.0,
screen_x=100.0,
screen_y=200.0,
dpi=96,
zoom=zoom
)
# Center of page should always be inside regardless of zoom
page_width_px = 210.0 * 96 / 25.4
page_height_px = 297.0 * 96 / 25.4
center_x = 100.0 + (page_width_px * zoom) / 2
center_y = 200.0 + (page_height_px * zoom) / 2
assert renderer.is_point_in_page(center_x, center_y)
def test_get_page_bounds_screen(self):
"""Test get_page_bounds_screen returns correct screen coordinates"""
renderer = PageRenderer(
page_width_mm=210.0,
page_height_mm=297.0,
screen_x=100.0,
screen_y=200.0,
dpi=96,
zoom=1.5
)
x, y, w, h = renderer.get_page_bounds_screen()
assert x == 100.0
assert y == 200.0
# Width and height should be scaled by zoom
page_width_px = 210.0 * 96 / 25.4
page_height_px = 297.0 * 96 / 25.4
assert abs(w - page_width_px * 1.5) < 0.1
assert abs(h - page_height_px * 1.5) < 0.1
def test_get_page_bounds_page(self):
"""Test get_page_bounds_page returns correct page-local coordinates"""
renderer = PageRenderer(
page_width_mm=210.0,
page_height_mm=297.0,
screen_x=100.0,
screen_y=200.0,
dpi=96,
zoom=1.5
)
x, y, w, h = renderer.get_page_bounds_page()
# Origin should be at 0,0 in page-local coordinates
assert x == 0.0
assert y == 0.0
# Width and height should NOT be affected by zoom (page-local coords)
page_width_px = 210.0 * 96 / 25.4
page_height_px = 297.0 * 96 / 25.4
assert abs(w - page_width_px) < 0.1
assert abs(h - page_height_px) < 0.1
class TestPageRendererSubPages:
"""Test sub-page detection for facing pages"""
def test_get_sub_page_at_single_page(self):
"""Test that get_sub_page_at returns None for single pages"""
renderer = PageRenderer(
page_width_mm=210.0,
page_height_mm=297.0,
screen_x=100.0,
screen_y=200.0,
dpi=96,
zoom=1.0
)
# For non-facing pages, should return None
result = renderer.get_sub_page_at(500.0, is_facing_page=False)
assert result is None
def test_get_sub_page_at_facing_page_left(self):
"""Test get_sub_page_at for left side of facing page"""
renderer = PageRenderer(
page_width_mm=420.0, # Double width for facing page
page_height_mm=297.0,
screen_x=100.0,
screen_y=200.0,
dpi=96,
zoom=1.0
)
# Calculate center line
page_width_px = 420.0 * 96 / 25.4
center_x = 100.0 + page_width_px / 2
# Point before center should be 'left'
result = renderer.get_sub_page_at(center_x - 10, is_facing_page=True)
assert result == 'left'
def test_get_sub_page_at_facing_page_right(self):
"""Test get_sub_page_at for right side of facing page"""
renderer = PageRenderer(
page_width_mm=420.0, # Double width for facing page
page_height_mm=297.0,
screen_x=100.0,
screen_y=200.0,
dpi=96,
zoom=1.0
)
# Calculate center line
page_width_px = 420.0 * 96 / 25.4
center_x = 100.0 + page_width_px / 2
# Point after center should be 'right'
result = renderer.get_sub_page_at(center_x + 10, is_facing_page=True)
assert result == 'right'
class TestPageRendererDimensions:
"""Test page dimension calculations"""
def test_page_dimensions_calculated_correctly(self):
"""Test that page dimensions are calculated correctly from mm to pixels"""
renderer = PageRenderer(
page_width_mm=210.0, # A4 width
page_height_mm=297.0, # A4 height
screen_x=0.0,
screen_y=0.0,
dpi=96,
zoom=1.0
)
# A4 at 96 DPI
expected_width = 210.0 * 96 / 25.4 # ~794 pixels
expected_height = 297.0 * 96 / 25.4 # ~1123 pixels
assert abs(renderer.page_width_px - expected_width) < 0.1
assert abs(renderer.page_height_px - expected_height) < 0.1
def test_screen_dimensions_with_zoom(self):
"""Test that screen dimensions account for zoom"""
renderer = PageRenderer(
page_width_mm=210.0,
page_height_mm=297.0,
screen_x=0.0,
screen_y=0.0,
dpi=96,
zoom=2.0
)
# Screen dimensions should be doubled due to zoom
expected_width = (210.0 * 96 / 25.4) * 2.0
expected_height = (297.0 * 96 / 25.4) * 2.0
assert abs(renderer.screen_width - expected_width) < 0.1
assert abs(renderer.screen_height - expected_height) < 0.1
def test_different_dpi_values(self):
"""Test page dimensions with different DPI values"""
dpi_values = [72, 96, 150, 300]
for dpi in dpi_values:
renderer = PageRenderer(
page_width_mm=210.0,
page_height_mm=297.0,
screen_x=0.0,
screen_y=0.0,
dpi=dpi,
zoom=1.0
)
expected_width = 210.0 * dpi / 25.4
expected_height = 297.0 * dpi / 25.4
assert abs(renderer.page_width_px - expected_width) < 0.1
assert abs(renderer.page_height_px - expected_height) < 0.1
class TestPageRendererEdgeCases:
"""Test edge cases and boundary conditions"""
def test_zero_coordinates(self):
"""Test handling of zero coordinates"""
renderer = PageRenderer(
page_width_mm=210.0,
page_height_mm=297.0,
screen_x=100.0,
screen_y=200.0,
dpi=96,
zoom=1.0
)
screen_x, screen_y = renderer.page_to_screen(0, 0)
assert screen_x == 100.0
assert screen_y == 200.0
page_x, page_y = renderer.screen_to_page(100.0, 200.0)
assert page_x == 0.0
assert page_y == 0.0
def test_negative_page_coordinates(self):
"""Test handling of negative page coordinates"""
renderer = PageRenderer(
page_width_mm=210.0,
page_height_mm=297.0,
screen_x=100.0,
screen_y=200.0,
dpi=96,
zoom=1.0
)
# Negative page coordinates should still convert correctly
screen_x, screen_y = renderer.page_to_screen(-50, -75)
assert screen_x == 50.0
assert screen_y == 125.0
# And back again
page_x, page_y = renderer.screen_to_page(50.0, 125.0)
assert page_x == -50.0
assert page_y == -75.0
def test_very_large_coordinates(self):
"""Test handling of very large coordinates"""
renderer = PageRenderer(
page_width_mm=210.0,
page_height_mm=297.0,
screen_x=100.0,
screen_y=200.0,
dpi=96,
zoom=1.0
)
large_x, large_y = 10000.0, 20000.0
screen_x, screen_y = renderer.page_to_screen(large_x, large_y)
page_x, page_y = renderer.screen_to_page(screen_x, screen_y)
assert abs(page_x - large_x) < 0.001
assert abs(page_y - large_y) < 0.001