Created
April 21, 2026 08:10
-
-
Save ToolmanP/70ea65f585a5523076a27353f639de39 to your computer and use it in GitHub Desktop.
Script to convert Apple color map file (.clr) into GIMP color pallete (.gpl)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env python3 | |
| """Convert Apple .clr files to GIMP/Inkscape .gpl palette format""" | |
| import sys | |
| import plistlib | |
| from pathlib import Path | |
| def parse_ns_components(data): | |
| """Parse NSComponents byte string like b'0.5 0.2 0.8 1'""" | |
| try: | |
| # Decode bytes to string and split | |
| text = data.decode('utf-8') if isinstance(data, bytes) else data | |
| parts = text.strip().split() | |
| if len(parts) >= 3: | |
| r = float(parts[0]) | |
| g = float(parts[1]) | |
| b = float(parts[2]) | |
| # Normalize 0-1 range to 0-255 | |
| return int(r * 255), int(g * 255), int(b * 255) | |
| except (ValueError, AttributeError, IndexError): | |
| pass | |
| return 0, 0, 0 | |
| def read_apple_clr(filepath): | |
| """Read Apple .clr NSKeyedArchiver format and extract colors.""" | |
| with open(filepath, 'rb') as f: | |
| data = f.read() | |
| plist = plistlib.loads(data) | |
| colors = [] | |
| if not isinstance(plist, dict): | |
| return colors | |
| objects = plist.get('$objects', []) | |
| top = plist.get('$top', {}) | |
| # Get the keys (color names) and colors arrays | |
| keys_ref = top.get('NSKeys') | |
| colors_ref = top.get('NSColors') | |
| if not keys_ref or not colors_ref: | |
| return colors | |
| # Dereference UIDs | |
| if isinstance(keys_ref, plistlib.UID): | |
| keys_obj = objects[keys_ref.data] if keys_ref.data < len(objects) else None | |
| else: | |
| keys_obj = None | |
| if isinstance(colors_ref, plistlib.UID): | |
| colors_obj = objects[colors_ref.data] if colors_ref.data < len(objects) else None | |
| else: | |
| colors_obj = None | |
| if not keys_obj or not colors_obj: | |
| return colors | |
| # Get the arrays of references | |
| key_refs = keys_obj.get('NS.objects', []) if isinstance(keys_obj, dict) else [] | |
| color_refs = colors_obj.get('NS.objects', []) if isinstance(colors_obj, dict) else [] | |
| # Match up names with colors | |
| for name_ref, color_ref in zip(key_refs, color_refs): | |
| # Get name | |
| name = 'Untitled' | |
| if isinstance(name_ref, plistlib.UID): | |
| name_data = objects[name_ref.data] | |
| if isinstance(name_data, str): | |
| name = name_data | |
| # Get color | |
| r, g, b = 0, 0, 0 | |
| if isinstance(color_ref, plistlib.UID): | |
| color_data = objects[color_ref.data] | |
| if isinstance(color_data, dict): | |
| # Parse NSComponents | |
| ns_components = color_data.get('NSComponents') | |
| if ns_components: | |
| r, g, b = parse_ns_components(ns_components) | |
| colors.append((name, r, g, b)) | |
| return colors | |
| def write_gpl(colors, output_path, palette_name=None): | |
| """Write colors to GIMP/Inkscape .gpl palette file.""" | |
| if palette_name is None: | |
| palette_name = Path(output_path).stem | |
| lines = [ | |
| "GIMP Palette", | |
| f"Name: {palette_name}", | |
| "Columns: 4", | |
| "#", | |
| ] | |
| for name, r, g, b in colors: | |
| # Clamp values to 0-255 | |
| r = max(0, min(255, r)) | |
| g = max(0, min(255, g)) | |
| b = max(0, min(255, b)) | |
| lines.append(f"{r:3d} {g:3d} {b:3d}\t{name}") | |
| with open(output_path, 'w') as f: | |
| f.write('\n'.join(lines) + '\n') | |
| def main(): | |
| if len(sys.argv) < 2: | |
| print(f"Usage: {sys.argv[0]} <input.clr> [output.gpl]") | |
| print("Converts Apple .clr color palette to GIMP/Inkscape .gpl format") | |
| sys.exit(1) | |
| input_file = sys.argv[1] | |
| if len(sys.argv) >= 3: | |
| output_file = sys.argv[2] | |
| else: | |
| output_file = str(Path(input_file).with_suffix('.gpl')) | |
| palette_name = Path(input_file).stem | |
| try: | |
| colors = read_apple_clr(input_file) | |
| if not colors: | |
| print("No colors found in file!") | |
| sys.exit(1) | |
| write_gpl(colors, output_file, palette_name) | |
| print(f"Converted {len(colors)} colors: {input_file} -> {output_file}") | |
| except Exception as e: | |
| print(f"Error: {e}") | |
| import traceback | |
| traceback.print_exc() | |
| sys.exit(1) | |
| if __name__ == '__main__': | |
| main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment