# Sirv Image Transformation - Python

A fluent URL builder for [Sirv](https://sirv.com) dynamic image transformations. Build transformed image URLs with an intuitive, chainable API — no API credentials required.

## Installation

```bash
pip install sirv-image
```

## Quick Start

```python
from sirv_image import SirvImage

url = (SirvImage('https://demo.sirv.com/image.jpg')
       .resize(width=300, height=200)
       .format('webp')
       .quality(80)
       .to_url())

# https://demo.sirv.com/image.jpg?w=300&h=200&format=webp&q=80
```

## Constructor

```python
# Full image URL
SirvImage('https://demo.sirv.com/image.jpg')

# Base URL + path
SirvImage('https://demo.sirv.com', '/image.jpg')
```

## API Reference

All methods return `self` for chaining. Call `.to_url()` to get the final URL. `str(image)` also works.

### Resize

| Method | Parameters | Description |
|--------|-----------|-------------|
| `resize()` | `width=, height=, option=` | Set dimensions. Option: `fit`, `fill`, `ignore`, `noup` |
| `width(w)` | `int` | Set width |
| `height(h)` | `int` | Set height |
| `scale_by_longest(s)` | `int` | Resize by longest dimension |
| `thumbnail(size=256)` | `int` | Create square thumbnail |

### Crop

| Method | Parameters | Description |
|--------|-----------|-------------|
| `crop()` | `width=, height=, x=, y=, type=, pad_width=, pad_height=` | Crop image. Type: `trim`, `poi`, `face` |
| `clip_path(name)` | `str` | Apply Photoshop clipping path |

### Rotation

| Method | Parameters | Description |
|--------|-----------|-------------|
| `rotate(degrees)` | `int/float` (-180 to 180) | Rotate image |
| `flip()` | — | Flip vertically |
| `flop()` | — | Flip horizontally |

### Format & Quality

| Method | Parameters | Description |
|--------|-----------|-------------|
| `format(fmt)` | `str` | Output format: `jpg`, `png`, `webp`, `optimal`, `original` |
| `quality(q)` | `int` (0-100) | JPEG quality |
| `webp_fallback(fmt)` | `str` | Fallback for non-WebP browsers |
| `subsampling(value)` | `str` | Chroma subsampling |
| `png_optimize(enabled=True)` | `bool` | PNG optimization |
| `gif_lossy(level)` | `int` (0-100) | GIF lossy compression |

### Color Adjustments

| Method | Parameters | Description |
|--------|-----------|-------------|
| `brightness(v)` | `int` (-100 to 100) | Adjust brightness |
| `contrast(v)` | `int` (-100 to 100) | Adjust contrast |
| `exposure(v)` | `int` (-100 to 100) | Adjust exposure |
| `hue(v)` | `int` (-100 to 100) | Adjust hue |
| `saturation(v)` | `int` (-100 to 100) | Adjust saturation |
| `lightness(v)` | `int` (-100 to 100) | Adjust lightness |
| `shadows(v)` | `int` (-100 to 100) | Adjust shadows |
| `highlights(v)` | `int` (-100 to 100) | Adjust highlights |
| `grayscale()` | — | Convert to grayscale |
| `color_level()` | `black=, white=` | Set color levels |
| `histogram(channel)` | `str` | Show histogram: `r`, `g`, `b` |

### Color Effects

| Method | Parameters | Description |
|--------|-----------|-------------|
| `colorize()` | `color=, opacity=` | Apply color overlay |
| `colortone(preset)` | `str` | Preset: `sepia`, `warm`, `cold`, etc. |
| `colortone()` | `color=, level=, mode=` | Custom colortone |

### Effects

| Method | Parameters | Description |
|--------|-----------|-------------|
| `blur(v)` | `int` (0-100) | Apply blur |
| `sharpen(v)` | `int` (0-100) | Apply sharpening |
| `vignette()` | `value=, color=` | Apply vignette |
| `opacity(v)` | `int` (0-100) | Set opacity (PNG) |

### Text Overlay

```python
.text(content,
      size=,             # Text area width (%)
      font_size=,        # Fixed size (px)
      font_family=,      # Google Fonts name
      font_style=,       # 'normal' | 'italic'
      font_weight=,      # 300-800
      color=,            # Hex or color name
      opacity=,          # 0-100
      outline_width=,    # px
      outline_color=,
      outline_opacity=,
      outline_blur=,
      background_color=,
      background_opacity=,
      align=,            # 'left' | 'center' | 'right'
      position=,         # 'center', 'north', etc.
      position_x=,
      position_y=,
      position_gravity=)
```

Call `.text()` multiple times for multiple text layers.

### Watermark

```python
.watermark(path,
           position=,         # 'center', 'north', 'tile', etc.
           position_x=,
           position_y=,
           position_gravity=,
           scale_width=,      # px or %
           scale_height=,
           scale_option=,     # 'noup' | 'fit' | 'fill' | 'ignore'
           rotate=,           # -180 to 180
           opacity=,          # 0-100
           layer=,            # 'front' | 'back'
           canvas_color=,
           canvas_opacity=,
           canvas_width=,
           canvas_height=,
           crop_x=, crop_y=,
           crop_width=, crop_height=)
```

Call `.watermark()` multiple times for multiple watermarks.

### Canvas & Frame

```python
.canvas(width=, height=, color=, position=, opacity=,
        aspect_ratio=, border_width=, border_height=,
        border_color=, border_opacity=)

.frame(style=, color=, width=, rim_color=, rim_width=)
```

### Other

| Method | Parameters | Description |
|--------|-----------|-------------|
| `page(num)` | `int` | PDF page to render |
| `profile(name)` | `str` | Apply saved profile |

## Examples

### E-commerce Product Image

```python
product = (SirvImage('https://demo.sirv.com/products/shoe.jpg')
           .resize(width=800, height=600, option='fill')
           .format('webp')
           .quality(85)
           .watermark('/brand-logo.png', position='southeast', opacity=30)
           .to_url())
```

### Vintage Photo Effect

```python
vintage = (SirvImage('https://demo.sirv.com/photo.jpg')
           .colortone('sepia')
           .vignette(value=40)
           .frame(style='solid', color='f5e6d3', width=20)
           .to_url())
```

## Testing

```bash
pytest
```

## License

MIT
