Generate dwarf portraits from Dwarf Fortress sprite sheets
Project description
df-portrait-compositor
Generate dwarf portrait images from Dwarf Fortress (Steam/Premium) sprite sheets by parsing the game's graphics definition files, evaluating layer conditions against appearance data, and compositing the matching tiles with palette recoloring.
Features
- Parses DF's
graphics_creatures_portrait_dwarf.txtinto structured layer rules - Evaluates 10+ condition types: caste, tissue color/length/shaping/curliness, body part modifiers (head broadness, nose shape, eye shape), equipment, syndromes (vampire, zombie, necromancer, ghost), and randomized part selection
- Implements DF's "first match wins" layer group logic for mutually exclusive alternatives
- Composites selected tiles onto an RGBA canvas with proper alpha blending
- Palette recoloring for skin and hair colors using DF's palette PNGs
- Appearance-hash-based caching so portraits only regenerate when the dwarf's look changes
- Deterministic random seed (from unit ID) for consistent clothing/feature variation
Installation
pip install df-portrait-compositor
Or install from source:
git clone https://github.com/Been012/df-portrait-compositor.git
cd df-portrait-compositor
pip install -e .
Requirements
- Python 3.11+
- Pillow
- A Dwarf Fortress Premium (Steam) installation (for the sprite sheet PNGs and graphics definition file)
Quick Start
CLI
The fastest way to see it in action:
# Generate a random demo portrait
df-portrait demo
# Generate 10 random portraits in a directory
df-portrait demo -n 10 -o my_portraits/
# Generate with a specific seed for reproducibility
df-portrait demo --seed 42
# Generate from JSON appearance data
df-portrait generate '{"sex":"female","skin_color":"PEACH","hair_color":"BROWN","hair_length":200,"hair_shaping":"BRAIDED"}'
# Generate from a df-storyteller snapshot file
df-portrait from-snapshot path/to/snapshot.json -o portraits/
# Generate for a specific dwarf from a snapshot
df-portrait from-snapshot path/to/snapshot.json --unit-id 7116
# Specify DF install path (auto-detected on common Steam paths)
df-portrait --df-path "C:\path\to\Dwarf Fortress" demo
Python API
from df_portrait_compositor import compose_portrait, DwarfAppearanceData
# Point to your DF install directory
df_install = r"C:\Program Files (x86)\Steam\steamapps\common\Dwarf Fortress"
# Build appearance data (all fields have sensible defaults)
appearance = DwarfAppearanceData(
sex="female",
skin_color="PEACH",
hair_color="BROWN",
hair_length=150,
hair_shaping="BRAIDED",
beard_length=0,
head_broadness=120,
eye_round_vs_narrow=80,
nose_upturned=130,
nose_broadness=90,
random_seed=42,
)
# Compose and save
img = compose_portrait(df_install, appearance, scale=2)
img.save("portrait.png")
Using generate_portrait for Caching
If you are generating portraits for many dwarves, generate_portrait handles caching automatically. It takes a raw appearance dict and a cache directory, and only regenerates when appearance data changes:
from pathlib import Path
from df_portrait_compositor import generate_portrait
appearance_dict = {
"sex": "male",
"skin_color": "PEACH",
"hair_color": "BLACK",
"beard_color": "BLACK",
"hair_length": 200,
"beard_length": 300,
"beard_shaping": "DOUBLE_BRAIDS",
"head_broadness": 140,
"nose_length": 160,
"eyebrow_density": 130,
"random_seed": 1001,
}
portrait_path = generate_portrait(
df_install=r"C:\Program Files (x86)\Steam\steamapps\common\Dwarf Fortress",
unit_id=1001,
appearance=appearance_dict,
cache_dir=Path("./portrait_cache"),
)
# Returns Path to the PNG, or None on failure
Getting Appearance Data from DFHack
The appearance fields map to data extracted from DF's internal structures via DFHack. You need a Lua script running inside DFHack to read each dwarf's:
- Sex:
unit.sex(0 = female, 1 = male) - Skin/hair/beard colors: Resolved from
unit.appearance.colorsviacaste.color_modifiersanddescriptor_colorlookups - Hair/beard length: From
unit.appearance.tissue_lengthindexed throughcaste.bp_appearance.style_part_idx - Hair/beard shaping: From
unit.appearance.tissue_style(0=NEATLY_COMBED, 1=BRAIDED, 2=DOUBLE_BRAIDS, 3=PONY_TAIL, 4=CLEAN_SHAVEN) - Body part modifiers (head broadness, nose shape, eye shape): From
unit.appearance.bp_modifiersindexed throughcaste.bp_appearance.modifier_idx/part_idx - Equipment: From
unit.inventoryitems
The dfhack_scripts/ directory contains debug scripts that demonstrate how to read these values:
| Script | Purpose |
|---|---|
storyteller-debug-colors.lua |
Dump color modifier values for all citizens |
storyteller-debug-hair.lua |
Identify tissue types and lengths for each styled tissue |
storyteller-debug-bpmod.lua |
Show body part appearance modifiers (density, broadness, etc.) |
Tissue Length Index Layout
The indices into unit.appearance.tissue_length and unit.appearance.tissue_style vary by sex. The mapping is determined by caste.bp_appearance.style_part_idx and style_layer_idx, but for the default dwarf creature definition:
Male (12 tissue entries):
| Index | Tissue |
|---|---|
| 0, 1 | SIDEBURNS |
| 2 | CHIN_WHISKERS (beard) |
| 3 | MOUSTACHE |
| 4, 5 | EYEBROW |
| 6, 7 | HAIR (scalp) |
| 8, 9 | CHEEK_WHISKERS (on CHEEK body parts) |
| 10, 11 | N/A |
Female (6 tissue entries):
| Index | Tissue |
|---|---|
| 0, 1 | EYEBROW |
| 2, 3 | HAIR (scalp) |
| 4, 5 | N/A |
HEAD body part layer indices (for bp_appearance.layer_idx):
| Layer | Tissue |
|---|---|
| 0, 1 | SIDEBURNS |
| 2 | CHIN_WHISKERS |
| 3 | MOUSTACHE |
| 4, 5 | EYEBROW |
| 6, 7 | HAIR |
| 8 | SKIN |
API Reference
Core Functions
compose_portrait(df_install, appearance, scale=2)
Compose a portrait from sprite sheet layers.
- df_install (
str): Path to the Dwarf Fortress installation directory. - appearance (
DwarfAppearanceData): Dwarf appearance data for condition matching. - scale (
int): Upscale factor. 1 = 96px native, 2 = 192px (default). - Returns:
PIL.Image.Image(RGBA).
generate_portrait(df_install, unit_id, appearance, cache_dir=None)
Generate and cache a portrait PNG for a dwarf.
- df_install (
str): Path to the DF installation directory. - unit_id (
int): Dwarf unit ID (used for filename and random seed). - appearance (
dict): Raw appearance dict (keys matchDwarfAppearanceDatafields). - cache_dir (
Path | None): Directory to store cached PNGs. Required for output. - Returns:
Pathto the generated PNG, orNoneon failure.
Data Classes
DwarfAppearanceData
Dataclass holding all appearance attributes used for condition evaluation. All fields have defaults, so partial data still produces a portrait (with fewer layers).
Key fields: sex, skin_color, hair_color, beard_color, hair_length, hair_shaping, hair_curly, beard_length, beard_shaping, head_broadness, eye_round_vs_narrow, eye_deep_set, eyebrow_density, nose_upturned, nose_length, nose_broadness, is_vampire, is_zombie, is_necromancer, is_ghost, equipment, random_seed, age.
SelectedLayer
Dataclass representing a layer selected for rendering: tile_page, tile_x, tile_y, palette_name, palette_index, use_item_palette.
LayerRule
Dataclass representing a parsed layer rule from the graphics definition file, including all condition types.
Parser and Evaluator
parse_portrait_graphics(filepath)
Parse a DF portrait graphics definition file into a list of LayerRule objects.
evaluate_layers(rules, appearance)
Evaluate layer rules against appearance data and return matching SelectedLayer objects in render order.
Constants
TILE_SIZE
The native tile size in pixels (96).
License
MIT
Project details
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 df_portrait_compositor-0.2.0.tar.gz.
File metadata
- Download URL: df_portrait_compositor-0.2.0.tar.gz
- Upload date:
- Size: 19.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
fb83ac4f7ea741da26895adf8f66d3a06e420ddd83f00edb00128c42e6905a09
|
|
| MD5 |
04ccb67011b233fc37fcbc4ea6de9d62
|
|
| BLAKE2b-256 |
3ff1808b47079b067f1aa184e7d077994cadfaf9e677c1777439a2b45011e6e9
|
File details
Details for the file df_portrait_compositor-0.2.0-py3-none-any.whl.
File metadata
- Download URL: df_portrait_compositor-0.2.0-py3-none-any.whl
- Upload date:
- Size: 20.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
55c3f3cf07bfb6e0bf040baff6e9109fabe78c445de2a2d203496b9cb9dae2fd
|
|
| MD5 |
c88f7cf272c1b2ebacc519b49b7cbbb3
|
|
| BLAKE2b-256 |
cde789b7a21356ddcf809ee37da2d6ade0bf6e526e8b89aa84a4b110750ac585
|