Metadata-Version: 2.4
Name: quickthumb
Version: 0.3.0
Summary: Programmatic thumbnail and social image generation with layered Python and JSON APIs
Project-URL: Homepage, https://github.com/sjquant/quickthumb
Project-URL: Repository, https://github.com/sjquant/quickthumb
Project-URL: Issues, https://github.com/sjquant/quickthumb/issues
Project-URL: Documentation, https://github.com/sjquant/quickthumb#readme
Author: Seonu Jang
License: MIT
License-File: LICENSE
Keywords: ai,automation,graphics,image-generation,pillow,social-media,thumbnail,thumbnails
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Artistic Software
Classifier: Topic :: Multimedia :: Graphics
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.10
Requires-Dist: pillow<11.0.0,>=10.4.0
Requires-Dist: pydantic<3.0.0,>=2.12.5
Requires-Dist: typing-extensions<5.0.0,>=4.15.0
Provides-Extra: rembg
Requires-Dist: onnxruntime<2.0.0,>=1.24.1; extra == 'rembg'
Requires-Dist: rembg<3.0.0,>=2.0.50; extra == 'rembg'
Description-Content-Type: text/markdown

# QuickThumb

QuickThumb is a Python library for programmatic thumbnail, social card, and promo image generation.
It is designed for code-first and JSON-first workflows, with a layer-based API that works well for human-authored scripts and AI-generated specs.

## Gallery

| YouTube Thumbnail | Burnout Thumbnail | Instagram News Card |
| --- | --- | --- |
| ![YouTube thumbnail example](examples/youtube_thumbnail_01.png) | ![Burnout thumbnail example](examples/youtube_thumbnail_02.png) | ![Instagram news card example](examples/instagram_news_card.png) |

## Why QuickThumb

- Built for thumbnails and social graphics, not just generic image composition
- Works with Python method chaining and JSON serialization/deserialization
- Handles gradients, remote images, rich text, shapes, blend modes, and export helpers
- Good fit for AI-assisted workflows that need deterministic image specs

## Installation

```bash
uv pip install quickthumb
```

Optional background removal support:

```bash
uv pip install "quickthumb[rembg]"
```

## Quick Start

```python
from quickthumb import Background, Canvas, Filter, Shadow, Stroke, TextPart

canvas = (
    Canvas.from_aspect_ratio("16:9", base_width=1280)
    .background(
        image="https://images.unsplash.com/photo-1516321318423-f06f85e504b3",
        effects=[Filter(brightness=0.65)],
    )
    .background(color="#000000", opacity=0.45)
    .text(
        content=[
            TextPart(
                text="BUILD THUMBNAILS\nFAST\n",
                color="#B8FF00",
                effects=[Stroke(width=8, color="#000000")],
            ),
            TextPart(
                text="With Python or JSON specs",
                color="#F5F5F5",
                size=44,
                effects=[Shadow(offset_x=2, offset_y=2, color="#000000", blur_radius=4)],
            ),
        ],
        size=112,
        position=("8%", "50%"),
        align=("left", "middle"),
        weight=900,
    )
    .outline(width=14, color="#B8FF00")
)

canvas.render("thumbnail.png")
```

## Core API

### Create a canvas

```python
from quickthumb import Canvas

canvas = Canvas(1280, 720)
square = Canvas.from_aspect_ratio("1:1", base_width=1080)
vertical = Canvas.from_aspect_ratio("9:16", base_width=1080)
```

### Background layers

```python
from quickthumb import Canvas, Filter, FitMode, LinearGradient

canvas = (
    Canvas(1280, 720)
    .background(color="#101828")
    .background(
        gradient=LinearGradient(
            angle=120,
            stops=[("#0F172A", 0.0), ("#0F172A00", 1.0)],
        ),
    )
    .background(
        image="hero.jpg",
        fit=FitMode.COVER,
        blend_mode="multiply",
        effects=[Filter(blur=4, brightness=0.75, contrast=1.1, saturation=0.9)],
    )
)
```

### Text layers and rich text

```python
from quickthumb import Background, Canvas, Glow, Shadow, Stroke, TextPart

canvas = Canvas(1280, 720).text(
    content=[
        TextPart(text="5 ", color="#FBBF24", weight=900),
        TextPart(text="WARNING SIGNS", color="#FFFFFF", weight=900),
    ],
    size=72,
    position=(80, 540),
    effects=[
        Background(color="#111827CC", padding=(16, 22), border_radius=12),
        Stroke(width=2, color="#000000"),
        Shadow(offset_x=4, offset_y=4, color="#000000", blur_radius=8),
        Glow(color="#F59E0B", radius=14, opacity=0.35),
    ],
)
```

### Image layers

```python
from quickthumb import Canvas, Filter

canvas = Canvas(1280, 720).image(
    path="portrait.png",
    position=("73%", "52%"),
    width=420,
    height=520,
    fit="cover",
    align=("center", "middle"),
    border_radius=24,
    remove_background=True,
    blend_mode="normal",
    effects=[Filter(contrast=1.1, saturation=1.05)],
)
```

### Shape layers

```python
from quickthumb import Canvas, Shadow, Stroke

canvas = Canvas(1280, 720).shape(
    shape="rectangle",
    position=(64, 60),
    width=320,
    height=88,
    color="#CC0000",
    border_radius=10,
    effects=[
        Stroke(width=2, color="#FFFFFF"),
        Shadow(offset_x=0, offset_y=6, color="#000000", blur_radius=12),
    ],
)
```

### Export helpers

```python
png_base64 = canvas.to_base64(format="PNG")
jpeg_data_url = canvas.to_data_url(format="JPEG", quality=90)
canvas.render("output.webp", format="WEBP", quality=90)
```

## JSON-First Workflow

QuickThumb can round-trip most canvases through JSON:

```python
from quickthumb import Canvas

config = """
{
  "width": 1280,
  "height": 720,
  "layers": [
    {
      "type": "background",
      "color": "#111827"
    },
    {
      "type": "text",
      "content": "Hello QuickThumb",
      "size": 72,
      "color": "#FFFFFF",
      "align": "center",
      "position": ["50%", "50%"]
    },
    {
      "type": "outline",
      "width": 10,
      "color": "#22C55E"
    }
  ]
}
"""

canvas = Canvas.from_json(config)
canvas.render("hello.png")

serialized = canvas.to_json()
```

Notes:

- JSON uses top-level `width`, `height`, and `layers`
- Custom layers added with `canvas.custom(fn)` are not JSON-serializable
- Enum-like values such as `blend_mode`, `fit`, and `align` can be passed as strings

## AI-Friendly Workflows

QuickThumb is a good target when you want an LLM to generate image specs that are deterministic and easy to validate.

Prompt pattern for Python generation:

```text
Generate QuickThumb Python code for a 1280x720 YouTube thumbnail.
Use layered composition only.
Keep text on the left, subject image on the right, and use high-contrast typography.
Return runnable code that ends with canvas.render("thumbnail.png").
```

Prompt pattern for JSON generation:

```text
Generate a QuickThumb JSON config with top-level width, height, and layers.
Use one background image layer, one dark overlay background layer, two text layers, and one outline layer.
Only use valid QuickThumb layer types and effect names.
```

Recommended workflow:

1. Have the model produce QuickThumb Python or JSON.
2. Validate or render it locally.
3. Adjust only the content, colors, and assets instead of rewriting layout logic from scratch.

## Environment Variables

QuickThumb looks for fonts using these environment variables:

- `QUICKTHUMB_FONT_DIR`: directory that contains font files
- `QUICKTHUMB_DEFAULT_FONT`: default font family/name to use when `font` is omitted

Example:

```python
import os

os.environ["QUICKTHUMB_FONT_DIR"] = "assets/fonts"
os.environ["QUICKTHUMB_DEFAULT_FONT"] = "Roboto"
```

## Feature Matrix

| Area | Supported |
| --- | --- |
| Canvas sizing | Explicit width/height, `from_aspect_ratio()` |
| Backgrounds | Solid colors, linear gradients, radial gradients, local/remote images |
| Background controls | Opacity, blend modes, fit modes, blur, brightness, contrast, saturation |
| Text | Positioning, alignment, wrapping, letter spacing, line height, rotation, auto-scale |
| Rich text | Per-segment `TextPart` styling |
| Text effects | Stroke, shadow, glow, background fill |
| Fonts | Local fonts, CSS-style weights, italic/bold flags, webfont URLs, fallback mapping |
| Images | Local/remote images, sizing, fit modes, alignment, opacity, rotation |
| Image effects | Stroke, shadow, glow, filter effects, border radius, background removal |
| Shapes | Rectangle and ellipse primitives with stroke/shadow/glow support |
| Export | PNG, JPEG, WebP, file output, base64, data URLs |
| Serialization | `to_json()` / `from_json()` for built-in layer types |

## Real Example Scripts

See the shipped examples in [`examples/README.md`](examples/README.md):

- `examples/youtube_thumbnail_01.py`
- `examples/youtube_thumbnail_02.py`
- `examples/instagram_news_card.py`

## Gotchas

- `weight` and `bold=True` are mutually exclusive on text layers and `TextPart`
- `auto_scale=True` requires `max_width`
- `position` percentage values must be strings like `"50%"`
- `canvas.custom(fn)` runs during render order and cannot be serialized to JSON

## Development

```bash
uv sync
uv run pytest
uv run ruff check .
uv run ty quickthumb/
```

## Reference

- Design notes: [DESIGN.md](DESIGN.md)
- License: [LICENSE](LICENSE)
