Bidirectional colour name <-> hex lookup backed by ~30K curated colour names
Project description
colourmapper
A Python module plus convienence CLI tools for bidirectional colour name ↔ hex lookup, backed by a merged named colour (eg burnt orange) map built from multiple curated sources of colour name and their respective hex value. Contains approx 30K unique colours.
What is it?
colourmapper is yet another simple solution that solves a common problem in code, design and data work
given a colour name, what is the hex code? and/or,
given a hex code, what is the closest named colour for it?
As well as the ColourMapper Module itself there are also included two simple CLI tools/scripts
| Tool | Purpose |
|---|---|
cm |
Look up a colour by a name and/or hex value |
mapping-file-create |
Regenerate the bundled named-colour map from included CSV sources plus others |
Sample Module usage
from colourmapper.ColourMapper import ColourMapper, ColourResult
mapper = ColourMapper()
result = mapper.get_colour_name("burnt orange")
# ColourResult(found=True, hex_value='#c04e01', name='burnt orange')
result = mapper.get_colour_name("#ededed")
# ColourResult(found=True, hex_value='#ededed', name='white edgar')
result = mapper.get_colour_name("#fe0003")
# ColourResult(found=True, hex_value='#ff0002', name='fire engine red') -- nearest match
Getting Started & Installation
Prerequisites
- Python 3.12+
Quick Start
git clone https://github.com/rondomondo/colourmapper
cd colourmapper
make check-venv
source .venv/bin/activate
make install
make test
make examples
Using the Python API
The primary way to use colourmapper is to import ColourMapper.py directly in your own code.
from colourmapper.ColourMapper import ColourMapper, ColourResult
Quick usage example
from colourmapper.ColourMapper import ColourMapper, ColourResult
mapper = ColourMapper()
result = mapper.get_colour_name("burnt orange")
# ColourResult(found=True, hex_value='#c04e01', name='burnt orange')
result = mapper.get_colour_name("#ededed")
# ColourResult(found=True, hex_value='#ededed', name='white edgar')
result = mapper.get_colour_name("#fe0003")
# ColourResult(found=True, hex_value='#ff0002', name='fire engine red') -- nearest match
ColourMapper(mapping_file=None)
Instantiates the mapper. On first call the bundled named_colours_map.json is loaded and cached for all subsequent instances. Pass a custom mapping_file path (str or Path) to use your own colour map instead.
Note: you can always recreate the mapping file by running mapping-file-create
mapper = ColourMapper() # use bundled map
mapper = ColourMapper("my_colours.json") # use custom map
ColourResult
All lookup methods return a ColourResult dataclass:
| Field | Type | Description |
|---|---|---|
found |
bool |
True if the colour was matched (exact or nearest) |
hex_value |
str |
Canonical hex code e.g. #c04e01 |
name |
str |
Human-readable colour name, lower-cased |
get_colour_name(value: str) -> ColourResult
The main lookup method. Accepts a colour name or any valid hex code (with or without #, short or long form). Lookup order:
- Exact name match (case-insensitive, spaces optional)
- Exact hex match
- Nearest colour by Euclidean RGB distance
mapper.get_colour_name("DarkRed") # by name, case-insensitive
mapper.get_colour_name("darkred") # spaces are optional too
mapper.get_colour_name("#8b0000") # by hex
mapper.get_colour_name("8b0000") # hash prefix is optional
mapper.get_colour_name("#f00") # shorthand hex expanded automatically
mapper.get_colour_name("#FE0001") # no exact match -- returns nearest
Note: American-spelling alias: get_color_name(value) behaves identically
get_closest_colour(hex_colour: str) -> tuple[str, str]
Finds the closest named colour to any hex code by Euclidean RGB distance. Returns (hex_code, name). Useful when you know the input is a hex value and want the nearest named match regardless of whether an exact entry exists.
hex_code, name = ColourMapper.get_closest_colour("#7F007F")
# ('#7f007f', 'purple')
Note: American-spelling alias: get_closest_color(hex_colour).
hexify(value: str) -> str | None
Normalises any hex string to canonical #rrggbb lowercase form. Returns None for invalid input. Can be called without an instance.
ColourMapper.hexify("#FFF") # -> '#ffffff'
ColourMapper.hexify("FF0000") # -> '#ff0000'
ColourMapper.hexify("not-a-hex") # -> None
Custom colour map files
Supply your own JSON file -- a flat object of "name": "#hexvalue" pairs -- to extend or replace the bundled map:
# will extend the initial map
mapper = ColourMapper("path/to/my_colours.json")
result = mapper.get_colour_name("my custom colour")
Error handling
If the mapping file cannot be found or read, a ColourMapper.MissingMappingFile (subclass of IOError) is raised:
from colourmapper.ColourMapper import ColourMapper
try:
mapper = ColourMapper("missing.json")
except ColourMapper.MissingMappingFile as e:
print(e)
Available CLI Commands
Run make help to see all available commands.
cm — colour lookup CLI
Run cm -h to see all available options.
Look up a colour by name or hex value
cm "burnt orange"
{
"found": true,
"hex_value": "#c04e01",
"name": "burnt orange"
}
Look up a colour by hex
cm '#ededed'
{
"found": true,
"hex_value": "#ededed",
"name": "very light grey"
}
Short hex codes are expanded automatically:
cm '#fff'
Open a colour preview in the browser
Pass --url to include a direct link to a colour preview page (color-hex.com):
cm --url "burnt orange"
{
"found": true,
"hex_value": "#c04e01",
"name": "burnt orange",
"url": "https://www.color-hex.com/color/c04e01"
}
Open the URL in any browser to see a full colour swatch, complementary colours, RGB/HSL/HSV conversion values, and more.
Inspect the Colour Map
# Print path to the bundled map file
cm --map-file
# Dump the full map as JSON
cm --dump-map-file
Pipe to jq
All output is valid JSON:
cm "Sapphire" | jq '.hex_value'
# "#0f52ba"
cm --url '#3d9970' | jq '.url'
# "https://www.color-hex.com/color/3d9970"
mapping-file-create — rebuild the colour map
The bundled named_colours_map.json is pre-built, but you can regenerate it at any time from the four included CSV sources.
What is a named-colour map?
A named-colour map is a flat JSON object where every key is a human-readable colour name and every value is its canonical hex code:
{
"acid green": "#8ffe09",
"adobe": "#bd6c48",
"algae": "#54ac68",
"almond": "#efdecd",
"almost black": "#070d0d"
}
The cm tool loads this file on first use and uses it for all lookups.
CSV source format
Each CSV has exactly three comma separated fields per row:
display name, hex value, normalised name
xkcd.rgb.mapping.csv (>1K crowd sourced colour names):
"acid green","#8ffe09","acid green"
"adobe","#bd6c48","adobe"
"algae green","#21c36f","algae green"
crayola2.mapping.csv (>200 Crayola Crayon names):
"Absolute Zero","#0048ba","absolute zero"
"Alien Armpit","#84de02","alien armpit"
"Alloy Orange","#c46210","alloy orange"
colorNames.mapping.csv (CSS / Web Colour registry, >1K names):
"Abbey","4c4f56","abbey"
"Aero Blue","c9ffe5","aero blue"
"Acapulco","7cb0a1","acapulco"
ColourMapperValues.csv (>5k custom curated set):
"100 Mph","#c93f38","100 mph"
"1989 Miami Hotline","#dd3366","1989 miami hotline"
After merging all four CSV sources, matplotlib's full named-colour set is added, giving a final map of approx 30K unique names.
Running it
mapping-file-create
{
"count": 61051,
"format": "json",
"path": "/path/to/named_colours_map.json"
}
Options
mapping-file-create -h
| Flag | Default | Description |
|---|---|---|
-f / --file |
named_colours_map.json |
Output filename stem |
--format |
json |
Output format: json, dict, list, csv |
--print |
off | Emit the full map to stdout |
--dry-run |
off | Build but do not write (implies --print) |
-d / --delimiter |
, |
Delimiter for list/csv formats |
Output formats
| Format | Description | Example line |
|---|---|---|
json |
Indented JSON object (default) | "acid green": "#8ffe09" |
dict |
Python dict literal | 'acid green': '#8ffe09' |
list |
JSON array of "name,hex" strings |
"acid green,#8ffe09" |
csv |
Plain CSV lines | acid green,#8ffe09 |
Examples
# Run the examples from the Makefile
make examples
Testing
# Run all tests
make test
Tests cover the following:
- Argument parser configuration for both CLIs
ColourMappername→hex and hex→name lookup plus API- Nearest-colour matching by RGB distance
- Mapping file creation and formatting
Tool Architecture
classDiagram
class ColourMapper:::blue {
+MAPPING_FILE: Path
+NAME_TO_HEX: dict
+HEX_TO_NAME: dict
+get_colour_name(value) ColourResult
+get_closest_colour(hex) tuple
+hexify(value) str
+hex_to_rgb(hex_str) tuple
+calculate_colour_distance(c1, c2) float
-_ensure_loaded(mapping_file)
}
class ColourResult:::green {
+found: bool
+hex_value: str
+name: str
}
class cm_cli:::orange {
+map_colour(flags)
+setup_argument_parser()
}
class mapping_file_create:::orange {
+build_colour_map(flags)
+create_dicts_from_csv(filename)
+get_named_colours_all()
+format_results(data, format, delim)
}
cm_cli --> ColourMapper : uses
ColourMapper --> ColourResult : returns
mapping_file_create ..> ColourMapper : generates input for
classDef blue fill:#2196F3,stroke:#0066cc,color:#000
classDef green fill:#99ff99,stroke:#00ff00,color:#000
classDef orange fill:#ffaa66,stroke:#ff6600,color:#000
Various Diagrams
Core Components
---
title: mapping-file-create dataflow
---
flowchart
classDef orange fill:#ffaa66,stroke:#ff6600,color:#000
classDef teal fill:#66ffee,stroke:#00ccaa,color:#000
classDef blue fill:#2196F3,stroke:#0066cc,color:#000
classDef green fill:#99ff99,stroke:#00ff00,color:#000
classDef grey fill:#cccccc,stroke:#888888,color:#000
A1[xkcd<br>~950 colours]:::green
A2[Crayola<br>~200 colours]:::green
A3[CSS / Web colours<br>~900 colours]:::green
A4[ColourMapperValues<br>custom set]:::green
B[mapping-file-create]:::orange
C[named_colours_map.json]:::teal
D[ColourMapper]:::blue
E[cm CLI]:::blue
F[JSON output]:::green
A1 & A2 & A3 & A4 -->|"<b> _ merged _ </b>"| B
B -->|"<b> _ writes _ </b>"| C
C -->|"<b> _ loads _ </b>"| D
D -->|"<b> _ uses _ </b>"| E
E -->|"<b> _ emits _ </b>"| F
Colour lookup flow
---
title: colour lookup - cm dataflow
---
flowchart LR
classDef cyan fill:#66ccff,stroke:#0099cc,color:#000
classDef yellow fill:#ffff66,stroke:#ffcc00,color:#000
classDef green fill:#99ff99,stroke:#00ff00,color:#000
classDef red fill:#ff6666,stroke:#ff0000,color:#000
classDef orange fill:#ffaa66,stroke:#ff6600,color:#000
Input([Input string<br>Name or hex code]):::green
IsHex{Looks like<br>a hex code?}:::yellow
ExactHex{Exact match<br>in map?}:::yellow
ExactName{Exact match<br>in map?}:::yellow
Return1[Return name]:::green
Return2[Return hex]:::green
Return3[Return closest name]:::orange
Nearest[Find nearest<br>by Euclidean<br>RGB distance]:::orange
NotFound[Return not-found<br>result]:::red
Input -->|"<b> _ lookup _ </b>"| IsHex
IsHex -->|"<b> _ yes _ </b>"| ExactHex
IsHex -->|"<b> _ no _ </b>"| ExactName
ExactHex -->|"<b> _ yes _ </b>"| Return1
ExactHex -->|"<b> _ no _ </b>"| Nearest
ExactName -->|"<b> _ yes _ </b>"| Return2
ExactName -->|"<b> _ no _ </b>"| NotFound
Nearest -->|"<b> _ closest _ </b>"| Return3
Demo
Licence
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file colourmapper_dev-0.0.7.tar.gz.
File metadata
- Download URL: colourmapper_dev-0.0.7.tar.gz
- Upload date:
- Size: 1.1 MB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
34dc03f3f17d5d896727e267543ce5ed7de78cd44c731db85ba35ee774c6ad6a
|
|
| MD5 |
ce2c0250e6e388f7b49f7439db7b6192
|
|
| BLAKE2b-256 |
05b6ae5bd282d6c513d886c82ba30ae5078c07cc6d93125de603f04484fd0625
|
File details
Details for the file colourmapper_dev-0.0.7-py3-none-any.whl.
File metadata
- Download URL: colourmapper_dev-0.0.7-py3-none-any.whl
- Upload date:
- Size: 1.1 MB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b82ccaafce9fca4e9f08b066e06902c42364a270e37a766ec47927352db95c11
|
|
| MD5 |
5286175a92f116c22c5ae3ecc8415bf1
|
|
| BLAKE2b-256 |
a46c241ab8e4d02404820d09ac2ed9fa785590213cdf598a966ec6dd9316ed6a
|