GumletProvider

Generates Gumlet transformation URLs. Gumlet is a query-string grammar close to imgix’s, on a source host that resolves relative paths against the source’s configured origin.

use Timber\Chainsaw\Provider\Gumlet\GumletProvider;
use Timber\Chainsaw\Signer\Algorithm;
use Timber\Chainsaw\Signer\SignatureEncoding;
use Timber\Chainsaw\Signer\HashUrlSigner;

$provider = new GumletProvider(
    host: 'https://demo.gumlet.io',
    // Optional URL signing — Gumlet's scheme is md5(token + path + query), hex:
    token: $_ENV['GUMLET_TOKEN'],
    signer: new HashUrlSigner(algorithm: Algorithm::Md5, encoding: SignatureEncoding::Hex),
);

Output: {host}/{source}?w=800&h=600&fit=crop&s={signature}

Prerequisites

Gumlet resolves the request path against the source’s configured origin, so a storage-relative path (PathSource) addresses a file on that origin. A fully-qualified UrlSource is supported through Gumlet’s web-proxy mode — it is URL-encoded whole (slashes and all) so it survives an origin URL that carries its own query or fragment.

Signing (secure URLs)

When a source has Secure URLs enabled, pass the source’s secret as token together with a HashUrlSigner. Gumlet’s scheme is byte-identical to imgix: md5(token + path + query) in hex, appended as the s parameter. Both token and signer must be set — with only one, URLs are emitted unsigned.

$provider = new GumletProvider(
    host: 'https://demo.gumlet.io',
    token: $_ENV['GUMLET_TOKEN'],
    signer: new HashUrlSigner(algorithm: Algorithm::Md5, encoding: SignatureEncoding::Hex),
);

Output: {host}/{source}?w=800&s=…

The cache-bust version is folded into the query before signing, so the signature always covers it. A same-origin watermark overlay is signed automatically too (see Notes).

Manipulation support

Legend: ✅ = implemented in Chainsaw, ⚠️ = supported with caveats, 🟦 = available on the CDN, not exposed by Chainsaw, ❌ = not available on this CDN.

Resize & crop

Manipulation Chainsaw Availability
Width w
Height h
Scale No scale-by-factor primitive; use width()/height()
Cover fit=crop + crop (compass, focal, smart/edges, entropy & face anchors)
ManualCrop rect=x,y,w,h
Contain fit=fit (default; preserves aspect)
CropToRatio ar + fit=crop + crop
Pad fit=fill + fill=solid + fill-color
Stretch fit=scale
PadToRatio No aspect-ratio-only pad; specify absolute dimensions via pad()

Filters & effects

Manipulation Chainsaw Availability
Blur ⚠️ blur (0…100, direct); dropped on WebP/AVIF output
Sharpen sharp
Brightness bri
Contrast ⚠️ contrast (0…100); increase-only — a negative value throws
Saturation sat
Greyscale via sat=-100
Hue shift hue (0…360)
Negate / Invert ⚠️ invert=true; inverts the alpha channel too, so it only works on sources with no alpha channel (see notes)
Trim trim (similarity percentage) + optional trim-color
Gamma No usable gamma slider
Sepia No sepia parameter
Pixelate No pixelate parameter

Decoration & orientation

Manipulation Chainsaw Availability
Background bg (pure white nudged a hair — see notes)
Border No border/canvas parameter
Flip flip=h / flip=v
Rotate ⚠️ rotate; 90/180/270 only — arbitrary angles throw UnsupportedManipulator
AutoOrient EXIF orientation applied implicitly by default
Watermark ⚠️ overlay grammar; several limitations — see notes

Encoding

Format Supported
JPEG
PNG
WebP
AVIF
GIF
JPEG XL
Auto format
Quality

Notes

  • imgix-grammar, calibrated on its own — Gumlet’s parameters look like imgix’s, but several diverge in ways that were measured against rendered pixels rather than read from docs. The provider is standalone (not a shared base with imgix), so each behaviour below is Gumlet-specific.
  • URL signing — optional; requires both token (the source secret) and a signer. Gumlet’s scheme is md5(token + path + query) in hex, so the signer is HashUrlSigner(algorithm: Algorithm::Md5, encoding: SignatureEncoding::Hex). With only one of the two configured, URLs are emitted unsigned.
  • Gamma — refused. Neither slider is usable: gam is fully inert, and gamma only shifts a few units non-monotonically in the wrong direction, so gamma() throws UnsupportedManipulator rather than emit an unreliable parameter.
  • Contrast — Gumlet’s parameter is the long contrast (0…100); the imgix con alias is silently ignored. It only ever increases contrast — a negative value renders identically to its positive twin — so there is no contrast-reduction primitive and a negative contrast() throws. Use a local provider to reduce contrast.
  • Pad fill — the pad bar colour is fill=solid + fill-color, not bg. A bare bg leaves the pad bars white. (bg is still the parameter for Background compositing — the two do not share a slot.)
  • Pure-white background — an exact pure-white background('#ffffff') is nudged to #fffffe (a hair off white, visually indistinguishable). Gumlet silently drops a pure-white background on alpha-preserving output — it keeps the transparency instead of compositing — and fffffe composites correctly. Only exact pure white is touched; every other colour passes through unchanged. Pad bars (fill-color=ffffff) are not affected.
  • Trim — the tolerance is the value of trim itself (a 1…99 similarity percentage). Chainsaw’s 0…100 tolerance clamps into that range (0 lifts to 1). An optional custom match colour is emitted as trim-color.
  • Blurblur = radius (Gumlet’s 0…100 slider renders a gaussian sigma of about radius/2). It is silently dropped when the output codec is WebP or AVIF — the parameter is ignored and the hard edge stays — so blur only renders for JPEG/PNG output.
  • Invert and the alpha channelnegate() (Gumlet’s invert=true) inverts the alpha channel along with the colours. A source that carries an alpha channel is destroyed: its transparency is swapped (opaque areas turn transparent and transparent areas turn opaque). The discriminator is the presence of an alpha channel, not visual opacity — a fully-opaque RGBA image would have its all-opaque alpha inverted to fully transparent. Sources with no alpha channel (JPEG, plain RGB PNG) invert fine. No parameter both inverts the colours and keeps the alpha channel, so use a local provider to invert an image that has one.
  • Format negotiationformat=auto negotiates a modern codec (AVIF/WebP) from the request’s Accept header. Gumlet varies on Accept even without a format parameter, so a modern browser can receive AVIF/WebP by default. Concrete formats pass through by value, including JPEG XL (format=jxlimage/jxl).
  • Source formats — Gumlet decodes the full raster set, AVIF input included (transcoded to JPEG by default).
  • Watermark — Gumlet uses its overlay grammar (not imgix’s mark-*). All nine anchor positions are supported. Sizing is percent-only (a 0…1 fraction of the output); pixel sizing throws.
    • Opacity (alpha) — supported for a same-origin PathSource overlay. The overlay URL is itself a Gumlet URL, so a non-default alpha is baked into it as a nested opacity (Chainsaw’s 0…100 maps directly), and the mark composites semi-transparently. A UrlSource overlay is external and cannot be transformed, so a non-default alpha on it is refused.
    • Padding needs the top-left anchoroverlay_top/overlay_left are absolute pixel offsets from the top-left that override the position, so padding is only expressible with Position::TopLeft; padding at any other anchor throws.
    • No inter-tile gap — a tiled overlay covers the whole output and has no gap control, so a requested tile gap throws.
    • Absolute URL required — the overlay is fetched from an absolute URL. A same-origin PathSource overlay is resolved against the provider host, and a UrlSource passes through as-is. On a secure source the same-origin overlay is signed automatically when a token + signer are configured (the signature covers the nested opacity when present); an external UrlSource overlay is left verbatim.