PhotoFramer is a Bash shell script to add professional frames with EXIF metadata and geolocation to your photos.
- Features
- Requirements
- Installation
- Usage
- Configuration
- Examples
- Important Notes
- Troubleshooting
- Logging
- Contributing
- License
- Credits
- Smart Framing: Adds white frames with configurable padding (50px sides, 50px top, 200px bottom)
- EXIF Extraction: Reads and displays camera metadata including ISO, shutter speed, aperture, and focal length
- GPS Geocoding: Converts GPS coordinates to human-readable location names using OpenStreetMap's Nominatim API
- Branding Support: Adds custom logo/avatar to bottom frame (preserves original DPI for print quality)
- Left Side: Location + date + time (12-hour format with AM/PM)
- Right Side: Camera settings (ISO, exposure, aperture, focal length)
- Date Handling: Extracts DateTimeOriginal from EXIF, falls back to file modification date
- Smart Formatting: Converts decimal exposures to fractions (e.g., 0.004 → 1/250s)
- Geocoding Cache: Caches location lookups to avoid repeated API calls and respect rate limits
- Progress Tracking: Visual progress bar with ETA for batch processing
- Comprehensive Logging: Console output + detailed log file
- Graceful Degradation: Handles missing metadata without crashing
- Directory Protection: Prevents accidental overwrite of original images (input/output must differ)
- Dependency Checking: Validates all required tools before processing
- Case-Insensitive: Supports .jpg, .jpeg, .JPG, .JPEG, .png, .PNG
- Linux (bash 4.0+)
- ImageMagick (identify + convert)
- exiftool
- bc, curl, jq, coreutils (realpath)
sudo apt install imagemagick exiftool bc curl jq coreutils- Nominatim API access (free, no API key)
- Valid email address required for API requests
- Inter font recommended:
Inter[wght].ttf - Falls back to system sans if not found
- PNG file with transparency (recommended)
- Any size (script resizes to 100×100px)
-
Clone or download the script to your preferred location
-
Make it executable:
chmod +x photoframer.sh
-
Edit configuration in the script:
# Required changes INPUT_DIR="/your/photo/directory" OUTPUT_DIR="/your/output/directory" AVATAR="/path/to/your/logo.png" # Required for geocoding (replace with your email) NOMINATIM_EMAIL="your-email@example.com" # Optional: adjust font path FONT="/path/to/Inter[wght].ttf"
Make the script executable first:
chmod +x photoframer.shThen run it from your Terminal:
./photoframer.shSimply execute the script in your Terminal. It will process all supported images in the configured input directory and save the framed versions to the output directory.
- Scans
INPUT_DIRfor supported images - Extracts EXIF metadata and GPS coordinates
- Reverse-geocodes coordinates to location names
- Creates framed photo in
OUTPUT_DIR - Preserves original files (input/output must differ)
Top frame: 50px white border
Left frame: 50px white border
Right frame: 50px white border
Bottom frame: 200px with:
- Left:
City, Country · YYYY-MM-DD · HH:MM AM/PM - Right:
ISO 100 1/250s f/8 50mm - Avatar/logo aligned left
| Variable | Default | Description |
|---|---|---|
FRAME_PADDING |
50 |
Left/right padding in pixels |
TOP_FRAME_HEIGHT |
50 |
Top frame height in pixels |
BOTTOM_FRAME_HEIGHT |
200 |
Bottom frame height in pixels |
FONT_SIZE |
26 |
Text size in points |
TEXT_COLOR |
#000000 |
Text color (hex) |
FRAME_COLOR |
white |
Frame background color |
AVATAR_SIZE |
100 |
Avatar size in pixels |
SHOW_PROGRESS_BAR |
true |
Show/hide progress indicator |
SHOW_COORDINATES_AS_FALLBACK |
false |
Show coordinates if no location name |
| Variable | Description |
|---|---|
NOMINATIM_EMAIL |
Required - Your email (respects API policy) |
NOMINATIM_ACCEPT_LANGUAGE |
Language for location names (default: "en") |
CACHE_FILE |
Cache file location (avoid repeated API calls) |
You can find many examples of images framed with PhotoFramer here:
photo.jpg (4096 × 3072 pixels)
photo.jpg (4096 × 3072 pixels)
- 50px top white bar
- 50px left/right padding
- 200px bottom bar with metadata
- Avatar and text overlays
- Input and output directories must be different
- Script checks this automatically to prevent overwriting originals
- Uses Nominatim (OpenStreetMap) - free, no API key required
- Must provide valid email in
NOMINATIM_EMAIL - 1-second delay between requests (respects API limits)
- Results cached to avoid redundant lookups
- Supports: JPG, JPEG, PNG (case-insensitive)
- Preserves original quality (no recompression)
- Maintains original DPI for print
# Use different directories:
INPUT_DIR="/path/to/originals"
OUTPUT_DIR="/path/to/framed"- Check if photo has GPS coordinates:
exiftool -gpslatitude photo.jpg - Verify internet connection
- Set
SHOW_COORDINATES_AS_FALLBACK="true"to show coordinates instead
Script handles missing values gracefully:
- Missing ISO →
-- - Missing location → shows date/time only
- Missing GPS → skips location lookup
- Install Inter font or update
FONTpath - Script falls back to system sans font automatically
- Console output shows progress and current file
- Full log saved to
$OUTPUT_DIR/log.txt - Geocoding cache saved to
$OUTPUT_DIR/geocode_cache.txt
Report issues or suggest features via GitHub Issues.
MIT License - Free for personal and commercial use.
- Reverse geocoding: Nominatim (OpenStreetMap)
- Image processing: ImageMagick
- Metadata: ExifTool by Phil Harvey