update reaquirements. removed redundant loading logic
All checks were successful
Python CI / test (push) Successful in 1m26s
Lint / lint (push) Successful in 1m37s
Tests / test (3.10) (push) Successful in 1m17s
Tests / test (3.11) (push) Successful in 1m14s
Tests / test (3.9) (push) Successful in 1m11s

This commit is contained in:
Duncan Tourolle 2025-11-27 20:06:01 +01:00
parent e8d9eaca51
commit 45268cdfe4
3 changed files with 31 additions and 73 deletions

View File

@ -131,20 +131,38 @@ class ImageData(BaseLayoutElement):
self._async_loading = False self._async_loading = False
self._async_load_requested = False self._async_load_requested = False
def resolve_image_path(self) -> Optional[str]:
"""
Resolve the image path to an absolute path.
Returns the absolute path if the image exists, None otherwise.
"""
if not self.image_path:
return None
# Already absolute
if os.path.isabs(self.image_path):
if os.path.exists(self.image_path):
return self.image_path
return None
# Relative path - look in project folder
project_folder, _ = get_asset_search_paths()
if project_folder:
full_path = os.path.join(project_folder, self.image_path)
if os.path.exists(full_path):
return full_path
return None
def _extract_dimensions_metadata(self): def _extract_dimensions_metadata(self):
""" """
Extract image dimensions without loading the full image. Extract image dimensions without loading the full image.
Uses PIL's lazy loading to just read the header. Uses PIL's lazy loading to just read the header.
""" """
try: try:
# Resolve path image_path = self.resolve_image_path()
image_path = self.image_path if image_path:
if not os.path.isabs(self.image_path):
project_folder, search_paths = get_asset_search_paths()
if project_folder and os.path.exists(os.path.join(project_folder, self.image_path)):
image_path = os.path.join(project_folder, self.image_path)
if os.path.exists(image_path):
# Use PIL to just read dimensions (fast, doesn't load pixel data) # Use PIL to just read dimensions (fast, doesn't load pixel data)
with Image.open(image_path) as img: with Image.open(image_path) as img:
width, height = img.width, img.height width, height = img.width, img.height
@ -166,44 +184,14 @@ class ImageData(BaseLayoutElement):
"""Render the image using OpenGL""" """Render the image using OpenGL"""
from OpenGL.GL import (glBegin, glEnd, glVertex2f, glColor3f, glColor4f, GL_QUADS, GL_LINE_LOOP, from OpenGL.GL import (glBegin, glEnd, glVertex2f, glColor3f, glColor4f, GL_QUADS, GL_LINE_LOOP,
glEnable, glDisable, GL_TEXTURE_2D, glBindTexture, glTexCoord2f, glEnable, glDisable, GL_TEXTURE_2D, glBindTexture, glTexCoord2f,
glGenTextures, glTexImage2D, GL_RGBA, GL_UNSIGNED_BYTE, glTexParameteri, GL_TEXTURE_MIN_FILTER, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
glTexParameteri, GL_TEXTURE_MIN_FILTER, GL_TEXTURE_MAG_FILTER, GL_LINEAR,
glDeleteTextures)
from PIL import Image
import os
x, y = self.position x, y = self.position
w, h = self.size w, h = self.size
# Note: Rotation is now handled at the PIL image level, not visually
# The image data itself is rotated, so we render it without transformation
# Try to load and render the actual image
texture_id = None texture_id = None
# Handle both absolute and relative paths
image_full_path = self.image_path
if self.image_path and not os.path.isabs(self.image_path):
# Relative path - only look in project folder (assets)
# Search paths are only used for healing, not for rendering
project_folder, _ = get_asset_search_paths()
if project_folder:
full_path = os.path.join(project_folder, self.image_path)
if os.path.exists(full_path):
image_full_path = full_path
else:
print(f"ImageData: Could not resolve path: {self.image_path}")
print(f" Expected at: {full_path}")
print(f" Image needs healing - not found in assets folder")
# NOTE: Async loading is now handled by page_layout.py calling request_image_load()
# This sync path should only be reached if async loading is not available
# The actual image will be loaded in the background and the texture created
# via _on_async_image_loaded() callback when ready
# Create texture from pending image if one exists (deferred from async load) # Create texture from pending image if one exists (deferred from async load)
# This ensures texture creation happens when GL context is active # Texture creation must happen during render when GL context is active
if hasattr(self, '_pending_pil_image') and self._pending_pil_image is not None: if hasattr(self, '_pending_pil_image') and self._pending_pil_image is not None:
self._create_texture_from_pending_image() self._create_texture_from_pending_image()

View File

@ -409,37 +409,6 @@ class PDFExporter:
self._render_element(params.canvas, params.element, params.x_offset_mm, self._render_element(params.canvas, params.element, params.x_offset_mm,
params.page_width_pt, params.page_height_pt, params.page_number) params.page_width_pt, params.page_height_pt, params.page_number)
def _resolve_image_path(self, image_path: str) -> Optional[str]:
"""
Resolve an image path, handling both absolute and relative paths.
Uses the same logic as ImageData.render() for consistency.
Args:
image_path: The image path (absolute or relative)
Returns:
Resolved absolute path if found, None otherwise
"""
if not image_path:
return None
# If already absolute and exists, return it
if os.path.isabs(image_path) and os.path.exists(image_path):
return image_path
# For relative paths, only look in project folder (assets)
# Search paths are only used for healing, not for PDF export
from pyPhotoAlbum.models import get_asset_search_paths
project_folder, _ = get_asset_search_paths()
if project_folder:
full_path = os.path.join(project_folder, image_path)
if os.path.exists(full_path):
return full_path
return None
def _render_image(self, ctx: RenderContext): def _render_image(self, ctx: RenderContext):
""" """
Render an image element on the PDF canvas. Render an image element on the PDF canvas.
@ -447,8 +416,8 @@ class PDFExporter:
Args: Args:
ctx: RenderContext containing all rendering parameters ctx: RenderContext containing all rendering parameters
""" """
# Resolve image path (handles both absolute and relative paths) # Resolve image path using ImageData's method
image_full_path = self._resolve_image_path(ctx.image_element.image_path) image_full_path = ctx.image_element.resolve_image_path()
# Check if image exists # Check if image exists
if not image_full_path: if not image_full_path:

View File

@ -39,6 +39,7 @@ dev = [
"pytest-qt>=4.2.0", "pytest-qt>=4.2.0",
"pytest-cov>=4.0.0", "pytest-cov>=4.0.0",
"pytest-mock>=3.10.0", "pytest-mock>=3.10.0",
"pdfplumber>=0.10.0",
"flake8>=5.0.0", "flake8>=5.0.0",
"black>=22.0.0", "black>=22.0.0",
"mypy>=0.990", "mypy>=0.990",