#!/usr/bin/env python3 """ Basic Usage Example for pyPhotoAlbum This example demonstrates: - Creating a new project - Adding pages with images and text - Working with the asset manager - Saving and loading projects - Basic element manipulation Based on unit test examples from the pyPhotoAlbum test suite. """ import os import sys import tempfile from pathlib import Path # Add parent directory to path to import pyPhotoAlbum sys.path.insert(0, str(Path(__file__).parent.parent)) from pyPhotoAlbum.project import Project, Page from pyPhotoAlbum.page_layout import PageLayout from pyPhotoAlbum.models import ImageData, TextBoxData, PlaceholderData from pyPhotoAlbum.project_serializer import save_to_zip, load_from_zip, get_project_info from pyPhotoAlbum.pdf_exporter import PDFExporter def create_sample_image(path: str, color: str = 'blue', size: tuple = (400, 300)): """Create a sample image for testing""" try: from PIL import Image, ImageDraw, ImageFont img = Image.new('RGB', size, color=color) draw = ImageDraw.Draw(img) # Draw a border border_width = 10 draw.rectangle( [(border_width, border_width), (size[0]-border_width, size[1]-border_width)], outline='white', width=5 ) # Add some text text = f"{color.upper()}\n{size[0]}x{size[1]}" try: # Try to use a nice font font = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf", 36) except: # Fallback to default font = ImageFont.load_default() # Calculate text position (center) bbox = draw.textbbox((0, 0), text, font=font) text_width = bbox[2] - bbox[0] text_height = bbox[3] - bbox[1] x = (size[0] - text_width) // 2 y = (size[1] - text_height) // 2 draw.text((x, y), text, fill='white', font=font) img.save(path) print(f"Created sample image: {path}") return True except Exception as e: print(f"Could not create sample image: {e}") return False def example_1_create_basic_project(): """ Example 1: Create a basic project with one page and an image This demonstrates the fundamental workflow based on test_project.py """ print("\n" + "="*60) print("Example 1: Creating a Basic Project") print("="*60) # Create output directory output_dir = Path(__file__).parent / "output" output_dir.mkdir(exist_ok=True) # Create a temporary directory for the project temp_dir = tempfile.mkdtemp(prefix="photo_album_") project_folder = os.path.join(temp_dir, "my_album") print(f"\nProject folder: {project_folder}") # Create a new project (from test_project.py) project = Project(name="My First Album", folder_path=project_folder) project.page_size_mm = (210, 297) # A4 size project.working_dpi = 300 project.export_dpi = 300 print(f"Created project: {project.name}") print(f"Page size: {project.page_size_mm[0]}mm x {project.page_size_mm[1]}mm") print(f"DPI: {project.working_dpi}") # Create a sample image image_path = output_dir / "sample_photo.jpg" create_sample_image(str(image_path), color='blue', size=(800, 600)) # Import the image into the project (from test_project_serialization.py) imported_path = project.asset_manager.import_asset(str(image_path)) print(f"\nImported asset: {imported_path}") # Create a page layout (from test_project.py) layout = PageLayout(width=210, height=297) # Add an image element (from test_models.py) image = ImageData( image_path=imported_path, x=10.0, y=10.0, width=190.0, height=140.0, rotation=0, z_index=0 ) layout.add_element(image) print(f"Added image at position ({image.position[0]}, {image.position[1]})") print(f"Image size: {image.size[0]}mm x {image.size[1]}mm") # Add a text box (from test_models.py) textbox = TextBoxData( text_content="My First Photo Album", font_settings={"family": "Arial", "size": 24, "color": (0, 0, 0)}, alignment="center", x=10.0, y=160.0, width=190.0, height=30.0 ) layout.add_element(textbox) print(f"Added text box: '{textbox.text_content}'") # Create a page and add it to the project page = Page(layout=layout, page_number=1) project.add_page(page) print(f"\nAdded page {page.page_number} to project") print(f"Total pages: {len(project.pages)}") print(f"Total elements on page: {len(page.layout.elements)}") # Save the project (from test_project_serialization.py) output_path = output_dir / "basic_project.ppz" print(f"\nSaving project to: {output_path}") success, error = save_to_zip(project, str(output_path)) if success: print("Project saved successfully!") # Get project info without loading (from test_project_serialization.py) info = get_project_info(str(output_path)) if info: print(f"\nProject Info:") print(f" Name: {info['name']}") print(f" Pages: {info['page_count']}") print(f" Version: {info['version']}") print(f" Working DPI: {info['working_dpi']}") else: print(f"Error saving project: {error}") return str(output_path) def example_2_load_and_modify_project(project_path: str): """ Example 2: Load an existing project and add more pages Based on test_project_serialization.py """ print("\n" + "="*60) print("Example 2: Loading and Modifying a Project") print("="*60) # Load the project (from test_project_serialization.py) print(f"\nLoading project from: {project_path}") loaded_project, error = load_from_zip(project_path) if not loaded_project: print(f"Error loading project: {error}") return print(f"Loaded project: {loaded_project.name}") print(f"Pages: {len(loaded_project.pages)}") # Create output directory output_dir = Path(__file__).parent / "output" # Create more sample images for i in range(2, 4): image_path = output_dir / f"sample_photo_{i}.jpg" colors = ['red', 'green'] create_sample_image(str(image_path), color=colors[i-2], size=(600, 800)) # Import the image imported_path = loaded_project.asset_manager.import_asset(str(image_path)) # Create a new page with the image (from test_project.py) layout = PageLayout(width=210, height=297) image = ImageData( image_path=imported_path, x=20.0 + i*5, y=20.0 + i*5, width=170.0, height=230.0 ) layout.add_element(image) # Add caption caption = TextBoxData( text_content=f"Page {i}", font_settings={"family": "Arial", "size": 18, "color": (0, 0, 0)}, alignment="center", x=20.0 + i*5, y=260.0, width=170.0, height=25.0 ) layout.add_element(caption) page = Page(layout=layout, page_number=i) loaded_project.add_page(page) print(f"Added page {i} with {len(page.layout.elements)} elements") # Save the modified project modified_path = output_dir / "modified_project.ppz" print(f"\nSaving modified project to: {modified_path}") success, error = save_to_zip(loaded_project, str(modified_path)) if success: print("Modified project saved successfully!") print(f"Total pages: {len(loaded_project.pages)}") else: print(f"Error saving: {error}") return str(modified_path) def example_3_serialization_roundtrip(): """ Example 3: Demonstrate serialization/deserialization Based on test_models.py serialization tests """ print("\n" + "="*60) print("Example 3: Serialization Round-Trip") print("="*60) # Create an image element (from test_models.py) print("\n1. Creating ImageData element...") original_image = ImageData( image_path="test.jpg", x=50.0, y=60.0, width=300.0, height=200.0, rotation=15.0, z_index=2, crop_info=(0.1, 0.1, 0.9, 0.9) ) print(f" Position: {original_image.position}") print(f" Size: {original_image.size}") print(f" Rotation: {original_image.rotation}°") print(f" Z-index: {original_image.z_index}") # Serialize (from test_models.py) print("\n2. Serializing to dictionary...") data = original_image.serialize() print(f" Serialized data keys: {list(data.keys())}") print(f" Type: {data['type']}") # Deserialize (from test_models.py) print("\n3. Deserializing from dictionary...") restored_image = ImageData() restored_image.deserialize(data) print(f" Position: {restored_image.position}") print(f" Size: {restored_image.size}") print(f" Rotation: {restored_image.rotation}°") # Verify round-trip print("\n4. Verifying round-trip...") assert restored_image.position == original_image.position assert restored_image.size == original_image.size assert restored_image.rotation == original_image.rotation assert restored_image.z_index == original_image.z_index assert restored_image.crop_info == original_image.crop_info print(" Round-trip successful!") # Do the same for TextBoxData (from test_models.py) print("\n5. Testing TextBoxData serialization...") font_settings = {"family": "Georgia", "size": 20, "color": (255, 255, 0)} original_text = TextBoxData( text_content="Round Trip Test", font_settings=font_settings, alignment="center", x=85.0, y=95.0, width=320.0, height=120.0, rotation=25.0, z_index=9 ) data = original_text.serialize() restored_text = TextBoxData() restored_text.deserialize(data) assert restored_text.text_content == original_text.text_content assert restored_text.alignment == original_text.alignment assert restored_text.position == original_text.position print(" TextBoxData round-trip successful!") def example_4_export_to_pdf(): """ Example 4: Export a project to PDF Based on pdf_exporter.py usage """ print("\n" + "="*60) print("Example 4: Export to PDF") print("="*60) # Create a simple project output_dir = Path(__file__).parent / "output" output_dir.mkdir(exist_ok=True) temp_dir = tempfile.mkdtemp(prefix="photo_album_pdf_") project_folder = os.path.join(temp_dir, "pdf_export") project = Project(name="PDF Export Example", folder_path=project_folder) project.page_size_mm = (140, 140) # Square format project.working_dpi = 300 project.export_dpi = 300 print(f"\nCreating project with {project.page_size_mm[0]}x{project.page_size_mm[1]}mm pages") # Create multiple pages with different colored images colors = ['red', 'green', 'blue', 'yellow'] for i, color in enumerate(colors, 1): # Create sample image image_path = output_dir / f"pdf_sample_{color}.jpg" create_sample_image(str(image_path), color=color, size=(600, 600)) # Import and add to page imported_path = project.asset_manager.import_asset(str(image_path)) layout = PageLayout(width=140, height=140) image = ImageData( image_path=imported_path, x=10.0, y=10.0, width=120.0, height=120.0 ) layout.add_element(image) page = Page(layout=layout, page_number=i) project.add_page(page) print(f"Created {len(project.pages)} pages") # Export to PDF pdf_path = output_dir / "example_album.pdf" print(f"\nExporting to PDF: {pdf_path}") exporter = PDFExporter(project, export_dpi=300) def progress_callback(current, total): print(f" Exporting page {current}/{total}...") success, errors = exporter.export( output_path=str(pdf_path), progress_callback=progress_callback ) if success: print(f"\nPDF exported successfully!") print(f"File size: {os.path.getsize(pdf_path) / 1024:.1f} KB") else: print(f"\nErrors during export:") for error in errors: print(f" - {error}") def main(): """Run all examples""" print("\n" + "="*60) print("pyPhotoAlbum - Basic Usage Examples") print("="*60) print("\nThese examples demonstrate core functionality using") print("code patterns from the pyPhotoAlbum unit tests.\n") try: # Example 1: Create a basic project project_path = example_1_create_basic_project() # Example 2: Load and modify if project_path and os.path.exists(project_path): example_2_load_and_modify_project(project_path) # Example 3: Serialization example_3_serialization_roundtrip() # Example 4: PDF export example_4_export_to_pdf() print("\n" + "="*60) print("All examples completed successfully!") print("="*60) print(f"\nOutput files are in: {Path(__file__).parent / 'output'}") except Exception as e: print(f"\nError running examples: {e}") import traceback traceback.print_exc() if __name__ == "__main__": main()