| name | technical-poster-generation |
| description | Guide for generating technical exploded-view posters using Python (PIL/Pillow). Use this skill when creating engineering diagrams, hardware breakdowns, or product teardowns that require precise layer visualization, annotation leader lines, and technical accuracy. Includes methods for drawing components, layering, annotation, and exporting high-quality images. |
Technical Poster Generation
Overview
This skill provides techniques for generating clean, professional technical exploded-view posters using Python's PIL/Pillow library. It covers:
- Setting up canvas and background
- Drawing hardware layers with precise positioning
- Creating annotation leader lines and callout labels
- Implementing component separation/explosion effect
- Exporting to PNG with proper resolution
Prerequisites
pip install Pillow
Ensure Python 3.8+ is available.
Architecture: Exploded View Layout
Coordinate System
- Origin (0, 0): Top-left corner
- Canvas dimensions: Typically 1200x1600px for A3-equivalent poster at 150 DPI
- Safe margins: 60px on all sides (avoid edge clipping)
- Component spacing: 80-120px vertical separation between layers for clarity
Layer Order (Bottom to Top, as Drawn)
- Background: Solid color fill (entire canvas)
- Title block: Top-left positioning
- Main component assembly: Center-aligned, with layers separated vertically
- Annotation elements: Leader lines and labels (drawn last, on top)
Drawing Techniques
1. Creating a Clean Canvas
from PIL import Image, ImageDraw, ImageFont
WIDTH, HEIGHT = 1200, 1600
BACKGROUND_HEX = "#F8F8F8"
image = Image.new("RGB", (WIDTH, HEIGHT), BACKGROUND_HEX)
draw = ImageDraw.Draw(image)
2. Drawing Hardware Layers
Technique: Rectangle-based components
Each layer should be drawn as a simple rectangle (or composite rectangles). Use filled rectangles for solid components, outlined rectangles for transparent/exposed views.
casing_color = "#1A1A1A"
x, y = 300, 200
width, height = 600, 100
draw.rectangle([x, y, x + width, y + height], fill=casing_color)
draw.rectangle([x, y, x + width, y + height], fill=None, outline=casing_color, width=2)
3. Annotation Leader Lines
Technique: Thin lines connecting labels to components
Leader lines should be:
- Color: Muted Mid Gray (#9E9E9E) at 40-60% opacity
- Width: 1-2 pixels
- Style: Straight lines (no curves unless absolutely necessary)
def draw_leader_line(draw, start_x, start_y, end_x, end_y, gray_hex="#9E9E9E"):
"""Draw a thin annotation leader line."""
draw.line([(start_x, start_y), (end_x, end_y)], fill=gray_hex, width=1)
For opacity support, convert to RGBA:
image = image.convert("RGBA")
draw = ImageDraw.Draw(image, "RGBA")
gray_with_opacity = (158, 158, 158, 128)
draw.line([(x1, y1), (x2, y2)], fill=gray_with_opacity, width=1)
image = image.convert("RGB")
4. Text & Labels
from PIL import ImageFont
try:
title_font = ImageFont.truetype("Inter-Bold.ttf", 56)
except IOError:
title_font = ImageFont.load_default()
draw.text((80, 60), "NOVA", font=title_font, fill="#1A1A1A")
label_font = ImageFont.truetype("Inter-Regular.ttf", 12)
draw.text((850, 250), "Thermal Management Unit", font=label_font, fill="#9E9E9E")
Exploded View Structure Example
Layout Pattern
┌──────────────────────────────────────────┐
│ NOVA │ <- Title block (top-left)
│ │
├──────────────────────────────────────────┤
│ │
│ Casing │ <- Layer 1 (top)
│ [Rectangle] │
│ │
│ Thermal Unit │ <- Layer 2 (separated)
│ [Rectangle] │
│ │
│ PCB Substrate │ <- Layer 3 (separated)
│ [Rectangle] │
│ │
│ Battery │ <- Layer 4 (separated)
│ [Rectangle] │
│ │
│ Interface │ <- Layer 5 (bottom)
│ [Rectangle] │
│ │
└──────────────────────────────────────────┘
Annotations with leader lines point to specific components
Step-by-Step Implementation Guide
Step 1: Define Color Palette
COLORS = {
"background": "#F8F8F8",
"corporate_dark": "#1A1A1A",
"primary_accent": "#0D47A1",
"secondary_accent": "#1565C0",
"tertiary_accent": "#42A5F5",
"muted_gray": "#9E9E9E"
}
Step 2: Calculate Layer Positions
LAYER_SPACING = 100
LAYERS = [
{"name": "Casing", "y": 250, "color": COLORS["corporate_dark"], "height": 80},
{"name": "Thermal Unit", "y": 380, "color": COLORS["secondary_accent"], "height": 70},
{"name": "PCB", "y": 500, "color": COLORS["tertiary_accent"], "height": 60},
{"name": "Battery", "y": 610, "color": COLORS["primary_accent"], "height": 50},
{"name": "Interface", "y": 710, "color": COLORS["corporate_dark"], "height": 40},
]
Step 3: Draw Layers & Components
for layer in LAYERS:
x = 300
y = layer["y"]
width = 600
height = layer["height"]
draw.rectangle([x, y, x + width, y + height], fill=layer["color"])
draw.rectangle([x, y, x + width, y + height], outline="#1A1A1A", width=1)
Step 4: Add Annotations
ANNOTATIONS = [
("Titanium Casing", 950, 290),
("Active Cooling", 950, 415),
("Mainboard PCB", 950, 530),
("Li-Ion Battery Pack", 950, 645),
("USB-C Interface", 950, 750),
]
for label, label_x, label_y in ANNOTATIONS:
component_x = 900
component_y = label_y
draw.line([(component_x, component_y), (label_x - 10, label_y)],
fill=COLORS["muted_gray"], width=1)
draw.text((label_x, label_y - 8), label, font=label_font, fill=COLORS["muted_gray"])
Step 5: Export
image.save("/root/nova_technical_poster.png", "PNG", quality=95, dpi=(150, 150))
Design Checklist
Common Pitfalls
-
Overcrowding: Space layers too close together
- Fix: Use 100-120px vertical spacing between components
-
Low contrast on labels: Text blends into background
- Fix: Use Corporate Dark (#1A1A1A) for body labels, muted gray only for secondary callouts
-
Thick leader lines: Makes design look cluttered
- Fix: Keep leader lines to 1px width
-
Neon colors or high saturation: Violates brand minimalism
- Fix: Use only the approved hex palette; avoid brightness adjustments
-
Missing margins: Text/components clip at edges
- Fix: Maintain 60px safe margin on all sides