import base64 import io import logging import struct _logger = logging.getLogger(__name__) class ImageProcessor: """Process product images: EXIF geo-tagging, metadata, optimization.""" @staticmethod def geo_tag_image(image_b64, company_name, address, phone, lat, lng): """Write EXIF metadata with company info and GPS coordinates. Args: image_b64: base64-encoded image data company_name: company name for Copyright/Artist tags address: company address for ImageDescription phone: company phone lat: GPS latitude (float) lng: GPS longitude (float) Returns: base64-encoded image with EXIF data """ try: import piexif except ImportError: _logger.warning("piexif not installed — skipping geo-tagging. Run: pip install piexif") return image_b64 try: image_data = base64.b64decode(image_b64) # Check if it's a JPEG (piexif only works with JPEG) if not image_data[:2] == b'\xff\xd8': _logger.info("Image is not JPEG — skipping EXIF geo-tagging") return image_b64 # Try to load existing EXIF try: exif_dict = piexif.load(image_data) except Exception: exif_dict = {"0th": {}, "Exif": {}, "GPS": {}, "1st": {}} # Set 0th IFD tags exif_dict["0th"][piexif.ImageIFD.ImageDescription] = address.encode('utf-8') if address else b'' exif_dict["0th"][piexif.ImageIFD.Copyright] = ( f"Copyright {company_name}".encode('utf-8') if company_name else b'' ) exif_dict["0th"][piexif.ImageIFD.Artist] = company_name.encode('utf-8') if company_name else b'' # Set GPS tags if coordinates provided if lat and lng: lat_deg = ImageProcessor._decimal_to_dms(abs(lat)) lng_deg = ImageProcessor._decimal_to_dms(abs(lng)) exif_dict["GPS"] = { piexif.GPSIFD.GPSLatitudeRef: b'N' if lat >= 0 else b'S', piexif.GPSIFD.GPSLatitude: lat_deg, piexif.GPSIFD.GPSLongitudeRef: b'E' if lng >= 0 else b'W', piexif.GPSIFD.GPSLongitude: lng_deg, } exif_bytes = piexif.dump(exif_dict) output = io.BytesIO() piexif.insert(exif_bytes, image_data, output) return base64.b64encode(output.getvalue()).decode('utf-8') except Exception as e: _logger.error("Failed to geo-tag image: %s", str(e)) return image_b64 @staticmethod def _decimal_to_dms(decimal): """Convert decimal degrees to EXIF DMS format (degrees, minutes, seconds as rationals).""" degrees = int(decimal) minutes_float = (decimal - degrees) * 60 minutes = int(minutes_float) seconds = int((minutes_float - minutes) * 60 * 10000) return ( (degrees, 1), (minutes, 1), (seconds, 10000), ) @staticmethod def prepare_wc_image(image_b64, filename, alt_text='', caption='', title='', description=''): """Prepare image data for WooCommerce upload. Returns dict ready for WC product images array, using src as base64 data URL. Note: WC REST API v3 accepts image URLs in 'src'. For base64, we need to upload via WordPress media endpoint first, then reference by URL. """ return { 'name': title or filename, 'alt': alt_text or '', 'caption': caption or '', 'description': description or '', # The actual upload will be handled by the wizard '_base64': image_b64, '_filename': filename, }