# HEIC Image Support Design - v1.4.2 **Status**: Ready for Implementation **Type**: Patch Release (backward compatible bug fix) **Date**: 2025-12-16 ## Problem Statement iPhones save photos in HEIC format by default (since iOS 11). When users upload photos from iPhones to StarPunk, they receive "Invalid image format" errors because: 1. HEIC is not in `ALLOWED_MIME_TYPES` 2. Pillow cannot open HEIC files without the `pillow-heif` plugin 3. iOS sometimes renames `.heic` files to `.jpeg` without converting, causing confusion HEIC files cannot be displayed directly in browsers, so conversion to JPEG is required. ## Design Overview This is a minimal patch release with a single conceptual change: detect HEIC/HEIF images and convert them to JPEG before processing. The implementation requires: 1. **Dependency Addition**: Add `pillow-heif` to requirements.txt 2. **Code Change**: Modify `validate_image()` in `starpunk/media.py` to detect and convert HEIC ## Implementation Specification ### 1. Dependency Update **File**: `/home/phil/Projects/starpunk/requirements.txt` Add after Pillow: ``` # HEIC/HEIF Support (v1.4.2 - iPhone photos) pillow-heif==0.18.* ``` Note: `pillow-heif` automatically registers with Pillow on import, enabling HEIC support. ### 2. Code Changes **File**: `/home/phil/Projects/starpunk/starpunk/media.py` #### 2.1 Add Import (top of file, after existing imports) ```python # HEIC/HEIF support - import registers with Pillow automatically try: import pillow_heif pillow_heif.register_heif_opener() HEIC_SUPPORTED = True except ImportError: HEIC_SUPPORTED = False ``` Rationale: Conditional import allows graceful degradation if pillow-heif is not installed (e.g., during development). #### 2.2 Modify `validate_image()` Function Insert HEIC detection and conversion immediately after the Pillow image verification (line ~99), before the format check (line ~104). **Insert after line 99** (after `img = Image.open(io.BytesIO(file_data))`): ```python # HEIC/HEIF conversion (v1.4.2) # HEIC cannot be displayed in browsers, convert to JPEG if img.format in ('HEIF', 'HEIC'): if not HEIC_SUPPORTED: raise ValueError( "HEIC/HEIF images require pillow-heif library. " "Please convert to JPEG before uploading." ) # Convert HEIC to JPEG in memory output = io.BytesIO() # Convert to RGB if needed (HEIC may have alpha channel) if img.mode in ('RGBA', 'P'): img = img.convert('RGB') img.save(output, format='JPEG', quality=95) output.seek(0) # Re-open as JPEG for further processing file_data = output.getvalue() img = Image.open(io.BytesIO(file_data)) ``` **Modify the return statement** to return the potentially converted `file_data`: The current function signature returns `Tuple[str, int, int]` (mime_type, width, height). We need to also return the converted bytes when HEIC conversion occurs. **Change return type** (line 84): ```python def validate_image(file_data: bytes, filename: str) -> Tuple[bytes, str, int, int]: ``` **Change return statement** (line 138): ```python return file_data, mime_type, width, height ``` #### 2.3 Update `save_media()` to Handle New Return Value **Modify line 400** in `save_media()`: ```python # Validate image (returns 4-tuple with potentially converted bytes) try: file_data, mime_type, orig_width, orig_height = validate_image(file_data, filename) except ValueError as e: ``` Note: The `file_data` variable is already in scope, so this reassignment handles the HEIC conversion case transparently. ## Design Decisions ### D1: Convert at Validation Time **Decision**: Convert HEIC to JPEG during `validate_image()` rather than in a separate step. **Rationale**: - Keeps the change minimal (single function modification) - Converted data flows naturally through existing `optimize_image()` pipeline - No new function signatures or abstractions needed - Validation is the logical place to normalize input formats ### D2: Convert to JPEG (not WebP or PNG) **Decision**: Convert HEIC to JPEG format. **Rationale**: - JPEG has universal browser support - HEIC photos are typically photographic content, which JPEG handles well - Quality 95 preserves detail while reducing file size - Consistent with existing JPEG optimization pipeline ### D3: Graceful Degradation **Decision**: Use conditional import with `HEIC_SUPPORTED` flag. **Rationale**: - Allows code to work without pillow-heif during development - Provides clear error message if HEIC upload attempted without library - No runtime crash if dependency missing ### D4: Quality Setting **Decision**: Use quality=95 for HEIC to JPEG conversion. **Rationale**: - Preserves most detail from the original - Subsequent `optimize_image()` call will further compress if needed - Matches existing optimization tier behavior for high-quality inputs ## Testing Requirements ### Unit Tests Add to existing media tests in `/home/phil/Projects/starpunk/tests/test_media.py`: 1. **Test HEIC detection and conversion** - Upload valid HEIC file - Verify output is JPEG format - Verify dimensions preserved 2. **Test HEIC with alpha channel** - Upload HEIC with transparency - Verify conversion to RGB (no alpha in JPEG) 3. **Test error handling without pillow-heif** - Mock `HEIC_SUPPORTED = False` - Verify appropriate error message ### Test Files A sample HEIC file is needed for testing. Options: - Create programmatically using pillow-heif - Download from a public test file repository - Use iPhone simulator to generate ## Migration Notes - **Database**: No changes required - **Configuration**: No changes required - **Existing uploads**: Not affected (HEIC was previously rejected) - **Backward compatibility**: Fully backward compatible ## Files Changed | File | Change | |------|--------| | `requirements.txt` | Add `pillow-heif==0.18.*` | | `starpunk/media.py` | Add HEIC import, modify `validate_image()` | | `starpunk/__init__.py` | Update `__version__` to `"1.4.2"` | | `CHANGELOG.md` | Add v1.4.2 release notes | | `tests/test_media.py` | Add HEIC test cases | ## Changelog Entry ```markdown ## [1.4.2] - 2025-12-XX ### Added - HEIC/HEIF image format support for iPhone photo uploads - Automatic HEIC to JPEG conversion (browsers cannot display HEIC) ### Dependencies - Added `pillow-heif` for HEIC image processing ``` ## Implementation Checklist - [ ] Add `pillow-heif==0.18.*` to requirements.txt - [ ] Add HEIC import and registration to media.py - [ ] Modify `validate_image()` return type to include bytes - [ ] Add HEIC detection and conversion logic to `validate_image()` - [ ] Update `save_media()` to handle new return value - [ ] Update `__version__` to "1.4.2" - [ ] Add HEIC test cases - [ ] Update CHANGELOG.md - [ ] Run full test suite