sorl-thumbnail

GraphicsMagick examples

Every result below is made by sorl-thumbnail with the GraphicsMagick engine, from the template shown with it. Built with GraphicsMagick 1.3.46 2025-10-29 Q16 http://www.GraphicsMagick.org/.

Geometry

The geometry string sets the box the thumbnail must fit in: both sides, only the width, or only the height. The aspect ratio is kept.

Compare Pillow · libvips · ImageMagick · GraphicsMagick · Wand · pgmagick

Fit in a box

{% load thumbnail %}
{% thumbnail "images/caritas-romana.webp" "300x300" as im %}
  <img src="{{ im.url }}" loading="lazy"
       width="{{ im.width }}" height="{{ im.height }}"
       alt="Caritas Romana fit inside a 300 by 300 box">
{% endthumbnail %}
Caritas Romana fit inside a 300 by 300 box

JPEG · 300×239 · 30.3 KB

Fixed width

{% load thumbnail %}
{% thumbnail "images/kimono.avif" "300" as im %}
  <img src="{{ im.url }}" loading="lazy"
       width="{{ im.width }}" height="{{ im.height }}"
       alt="Kimono scaled to 300 pixels wide">
{% endthumbnail %}
Kimono scaled to 300 pixels wide

JPEG · 300×425 · 68.5 KB

Fixed height

{% load thumbnail %}
{% thumbnail "images/caritas-romana.webp" "x300" as im %}
  <img src="{{ im.url }}" loading="lazy"
       width="{{ im.width }}" height="{{ im.height }}"
       alt="Caritas Romana scaled to 300 pixels high">
{% endthumbnail %}
Caritas Romana scaled to 300 pixels high

JPEG · 377×300 · 45.0 KB

Crop

With crop the thumbnail fills the whole box and the overflow is cut. Position it with CSS-like keywords, percentages or pixels.

Compare Pillow · libvips · ImageMagick · GraphicsMagick · Wand · pgmagick

Center crop

{% load thumbnail %}
{% thumbnail "images/mona-lisa.jpg" "300x300" crop="center" as im %}
  <img src="{{ im.url }}" loading="lazy"
       width="{{ im.width }}" height="{{ im.height }}"
       alt="Mona Lisa cropped to a square around the center">
{% endthumbnail %}
Mona Lisa cropped to a square around the center

JPEG · 300×300 · 37.3 KB

Top crop

{% load thumbnail %}
{% thumbnail "images/mona-lisa.jpg" "300x300" crop="top" as im %}
  <img src="{{ im.url }}" loading="lazy"
       width="{{ im.width }}" height="{{ im.height }}"
       alt="Mona Lisa cropped to a square from the top">
{% endthumbnail %}
Mona Lisa cropped to a square from the top

JPEG · 300×300 · 37.3 KB

Bottom crop

{% load thumbnail %}
{% thumbnail "images/mona-lisa.jpg" "300x300" crop="bottom" as im %}
  <img src="{{ im.url }}" loading="lazy"
       width="{{ im.width }}" height="{{ im.height }}"
       alt="Mona Lisa cropped to a square from the bottom">
{% endthumbnail %}
Mona Lisa cropped to a square from the bottom

JPEG · 300×300 · 34.2 KB

Left crop

{% load thumbnail %}
{% thumbnail "images/caritas-romana.webp" "300x300" crop="left" as im %}
  <img src="{{ im.url }}" loading="lazy"
       width="{{ im.width }}" height="{{ im.height }}"
       alt="Caritas Romana cropped to a square from the left">
{% endthumbnail %}
Caritas Romana cropped to a square from the left

JPEG · 300×300 · 39.2 KB

Right crop

{% load thumbnail %}
{% thumbnail "images/caritas-romana.webp" "300x300" crop="right" as im %}
  <img src="{{ im.url }}" loading="lazy"
       width="{{ im.width }}" height="{{ im.height }}"
       alt="Caritas Romana cropped to a square from the right">
{% endthumbnail %}
Caritas Romana cropped to a square from the right

JPEG · 300×300 · 38.3 KB

Percentage offset

{% load thumbnail %}
{% thumbnail "images/mona-lisa.jpg" "300x150" crop="50% 20%" as im %}
  <img src="{{ im.url }}" loading="lazy"
       width="{{ im.width }}" height="{{ im.height }}"
       alt="Mona Lisa cropped to a wide strip around the face">
{% endthumbnail %}
Mona Lisa cropped to a wide strip around the face

JPEG · 300×150 · 20.1 KB

Pixel offset

{% load thumbnail %}
{% thumbnail "images/caritas-romana.webp" "300x300" crop="40px 0px" as im %}
  <img src="{{ im.url }}" loading="lazy"
       width="{{ im.width }}" height="{{ im.height }}"
       alt="Caritas Romana cropped 40 pixels in from the left">
{% endthumbnail %}
Caritas Romana cropped 40 pixels in from the left

JPEG · 300×300 · 39.1 KB

No-op crop

{% load thumbnail %}
{% thumbnail "images/mona-lisa.jpg" "300x300" crop="noop" as im %}
  <img src="{{ im.url }}" loading="lazy"
       width="{{ im.width }}" height="{{ im.height }}"
       alt="Mona Lisa scaled to fit, not cropped">
{% endthumbnail %}
Mona Lisa scaled to fit, not cropped

JPEG · 201×300 · 24.9 KB

Smart crop

{% load thumbnail %}
{% thumbnail "images/fixtures/white_border.jpg" "200x200" crop="smart" as im %}
  <img src="{{ im.url }}" loading="lazy"
       width="{{ im.width }}" height="{{ im.height }}"
       alt="Test image cropped to its busiest area">
{% endthumbnail %}
Not run: smart cropping is only implemented by Pillow and libvips.

Crop box

{% load thumbnail %}
{% thumbnail "images/mona-lisa.jpg" "300x300" cropbox="300,200,800,700" as im %}
  <img src="{{ im.url }}" loading="lazy"
       width="{{ im.width }}" height="{{ im.height }}"
       alt="The face of the Mona Lisa, cut out before scaling">
{% endthumbnail %}
The face of the Mona Lisa, cut out before scaling

JPEG · 300×300 · 41.9 KB

Upscale

Small sources are enlarged to the requested size unless upscale=False.

Compare Pillow · libvips · ImageMagick · GraphicsMagick · Wand · pgmagick

Upscaled (default)

{% load thumbnail %}
{% thumbnail "images/bouncing-ball.gif" "240x240" as im %}
  <img src="{{ im.url }}" loading="lazy"
       width="{{ im.width }}" height="{{ im.height }}"
       alt="Small animation enlarged to 240 pixels">
{% endthumbnail %}
Small animation enlarged to 240 pixels

JPEG · 240×240 · 4.4 KB

Not upscaled

{% load thumbnail %}
{% thumbnail "images/bouncing-ball.gif" "240x240" upscale=False as im %}
  <img src="{{ im.url }}" loading="lazy"
       width="{{ im.width }}" height="{{ im.height }}"
       alt="Small animation kept at its original size">
{% endthumbnail %}
Small animation kept at its original size

JPEG · 82×82 · 1.6 KB

Formats

The output format is JPEG unless you ask for another one. Which formats work depends on the codecs the engine was built with.

Compare Pillow · libvips · ImageMagick · GraphicsMagick · Wand · pgmagick

JPEG

{% load thumbnail %}
{% thumbnail "images/mona-lisa.jpg" "240" format="JPEG" as im %}
  <img src="{{ im.url }}" loading="lazy"
       width="{{ im.width }}" height="{{ im.height }}"
       alt="Mona Lisa as JPEG">
{% endthumbnail %}
Mona Lisa as JPEG

JPEG · 240×359 · 33.6 KB

PNG

{% load thumbnail %}
{% thumbnail "images/mona-lisa.jpg" "240" format="PNG" as im %}
  <img src="{{ im.url }}" loading="lazy"
       width="{{ im.width }}" height="{{ im.height }}"
       alt="Mona Lisa as PNG">
{% endthumbnail %}
Mona Lisa as PNG

PNG · 240×359 · 123.5 KB

WEBP

{% load thumbnail %}
{% thumbnail "images/mona-lisa.jpg" "240" format="WEBP" as im %}
  <img src="{{ im.url }}" loading="lazy"
       width="{{ im.width }}" height="{{ im.height }}"
       alt="Mona Lisa as WEBP">
{% endthumbnail %}
Mona Lisa as WEBP

WEBP · 240×359 · 23.8 KB

GIF

{% load thumbnail %}
{% thumbnail "images/mona-lisa.jpg" "240" format="GIF" as im %}
  <img src="{{ im.url }}" loading="lazy"
       width="{{ im.width }}" height="{{ im.height }}"
       alt="Mona Lisa as GIF">
{% endthumbnail %}
Mona Lisa as GIF

GIF · 240×359 · 61.3 KB

AVIF

{% load thumbnail %}
{% thumbnail "images/mona-lisa.jpg" "240" format="AVIF" as im %}
  <img src="{{ im.url }}" loading="lazy"
       width="{{ im.width }}" height="{{ im.height }}"
       alt="Mona Lisa as AVIF">
{% endthumbnail %}
Not run: GraphicsMagick has no AVIF encoder.

Quality

JPEG quality trades file size for detail. Progressive JPEGs load in passes; the final image is the same.

Compare Pillow · libvips · ImageMagick · GraphicsMagick · Wand · pgmagick

Quality 10

{% load thumbnail %}
{% thumbnail "images/mona-lisa.jpg" "240" quality=10 as im %}
  <img src="{{ im.url }}" loading="lazy"
       width="{{ im.width }}" height="{{ im.height }}"
       alt="Mona Lisa at JPEG quality 10">
{% endthumbnail %}
Mona Lisa at JPEG quality 10

JPEG · 240×359 · 2.1 KB

Quality 50

{% load thumbnail %}
{% thumbnail "images/mona-lisa.jpg" "240" quality=50 as im %}
  <img src="{{ im.url }}" loading="lazy"
       width="{{ im.width }}" height="{{ im.height }}"
       alt="Mona Lisa at JPEG quality 50">
{% endthumbnail %}
Mona Lisa at JPEG quality 50

JPEG · 240×359 · 6.0 KB

Quality 95 (default)

{% load thumbnail %}
{% thumbnail "images/mona-lisa.jpg" "240" as im %}
  <img src="{{ im.url }}" loading="lazy"
       width="{{ im.width }}" height="{{ im.height }}"
       alt="Mona Lisa at the default JPEG quality">
{% endthumbnail %}
Mona Lisa at the default JPEG quality

JPEG · 240×359 · 33.6 KB

Not progressive

{% load thumbnail %}
{% thumbnail "images/mona-lisa.jpg" "240" progressive=False as im %}
  <img src="{{ im.url }}" loading="lazy"
       width="{{ im.width }}" height="{{ im.height }}"
       alt="Mona Lisa as a baseline JPEG">
{% endthumbnail %}
Mona Lisa as a baseline JPEG

JPEG · 240×359 · 35.1 KB

Colorspace

Thumbnails are RGB by default. GRAY converts to grayscale.

Compare Pillow · libvips · ImageMagick · GraphicsMagick · Wand · pgmagick

RGB (default)

{% load thumbnail %}
{% thumbnail "images/kimono.avif" "240" as im %}
  <img src="{{ im.url }}" loading="lazy"
       width="{{ im.width }}" height="{{ im.height }}"
       alt="Kimono in color">
{% endthumbnail %}
Kimono in color

JPEG · 240×340 · 46.8 KB

Grayscale

{% load thumbnail %}
{% thumbnail "images/kimono.avif" "240" colorspace="GRAY" as im %}
  <img src="{{ im.url }}" loading="lazy"
       width="{{ im.width }}" height="{{ im.height }}"
       alt="Kimono in grayscale">
{% endthumbnail %}
Kimono in grayscale

JPEG · 240×340 · 29.0 KB

Padding

With padding the thumbnail is centered on a canvas of the exact requested size.

Compare Pillow · libvips · ImageMagick · GraphicsMagick · Wand · pgmagick

White padding (default color)

{% load thumbnail %}
{% thumbnail "images/caritas-romana.webp" "300x300" padding=True as im %}
  <img src="{{ im.url }}" loading="lazy"
       width="{{ im.width }}" height="{{ im.height }}"
       alt="Caritas Romana centered on a white square">
{% endthumbnail %}
Caritas Romana centered on a white square

JPEG · 300×300 · 32.0 KB

Colored padding

{% load thumbnail %}
{% thumbnail "images/caritas-romana.webp" "300x300" padding=True padding_color="#274c63" as im %}
  <img src="{{ im.url }}" loading="lazy"
       width="{{ im.width }}" height="{{ im.height }}"
       alt="Caritas Romana centered on a blue square">
{% endthumbnail %}
Caritas Romana centered on a blue square

JPEG · 300×300 · 32.6 KB

Effects

Rounded corners, blur and border removal.

Compare Pillow · libvips · ImageMagick · GraphicsMagick · Wand · pgmagick

Rounded corners

{% load thumbnail %}
{% thumbnail "images/earth-apollo-11.png" "300x300" rounded=40 format="PNG" as im %}
  <img src="{{ im.url }}" loading="lazy"
       width="{{ im.width }}" height="{{ im.height }}"
       alt="Earth with rounded corners on a transparent background">
{% endthumbnail %}
Not run: rounded corners are only implemented by Pillow and libvips.

Blur

{% load thumbnail %}
{% thumbnail "images/mona-lisa.jpg" "240" blur=6 as im %}
  <img src="{{ im.url }}" loading="lazy"
       width="{{ im.width }}" height="{{ im.height }}"
       alt="Mona Lisa blurred">
{% endthumbnail %}
Not run: blur is only implemented by Pillow and libvips.

Remove border

{% load thumbnail %}
{% thumbnail "images/fixtures/white_border.jpg" "200x200" remove_border=True as im %}
  <img src="{{ im.url }}" loading="lazy"
       width="{{ im.width }}" height="{{ im.height }}"
       alt="Test image with its white border removed">
{% endthumbnail %}
Not run: border removal is only implemented by Pillow and libvips.

Animation

With GIF output, Wand and libvips keep every frame, and so does Pillow after 13.2.0 (#687). ImageMagick, GraphicsMagick and every other format take the first frame.

Compare Pillow · libvips · ImageMagick · GraphicsMagick · Wand · pgmagick

GIF output

{% load thumbnail %}
{% thumbnail "images/bouncing-ball.gif" "160x160" format="GIF" as im %}
  <img src="{{ im.url }}" loading="lazy"
       width="{{ im.width }}" height="{{ im.height }}"
       alt="Animated bouncing ball">
{% endthumbnail %}
Animated bouncing ball

GIF · 160×160 · 1.3 KB

PNG output

{% load thumbnail %}
{% thumbnail "images/bouncing-ball.gif" "160x160" format="PNG" as im %}
  <img src="{{ im.url }}" loading="lazy"
       width="{{ im.width }}" height="{{ im.height }}"
       alt="First frame of the bouncing ball">
{% endthumbnail %}
First frame of the bouncing ball

PNG · 160×160 · 2.5 KB

Orientation

Photos are rotated following their EXIF orientation unless orientation=False. This test image is stored sideways.

Compare Pillow · libvips · ImageMagick · GraphicsMagick · Wand · pgmagick

Follow EXIF (default)

{% load thumbnail %}
{% thumbnail "images/fixtures/aspect_test.jpg" "200x200" as im %}
  <img src="{{ im.url }}" loading="lazy"
       width="{{ im.width }}" height="{{ im.height }}"
       alt="Test image rotated upright">
{% endthumbnail %}
Test image rotated upright

JPEG · 100×200 · 5.1 KB

Ignore EXIF

{% load thumbnail %}
{% thumbnail "images/fixtures/aspect_test.jpg" "200x200" orientation=False as im %}
  <img src="{{ im.url }}" loading="lazy"
       width="{{ im.width }}" height="{{ im.height }}"
       alt="Test image left as stored">
{% endthumbnail %}
Test image left as stored

JPEG · 200×100 · 5.1 KB

Filters

is_portrait tells you if an image is taller than wide, before making a thumbnail. margin centers a thumbnail in a fixed box.

Compare Pillow · libvips · ImageMagick · GraphicsMagick · Wand · pgmagick

is_portrait

{% load thumbnail %}
{% if "images/kimono.avif"|is_portrait %}
  {% thumbnail "images/kimono.avif" "x200" as im %}
    <img src="{{ im.url }}" loading="lazy"
         width="{{ im.width }}" height="{{ im.height }}"
         alt="Portrait kimono, limited by height">
  {% endthumbnail %}
{% else %}
  {% thumbnail "images/kimono.avif" "200" as im %}
    <img src="{{ im.url }}" loading="lazy"
         width="{{ im.width }}" height="{{ im.height }}"
         alt="Landscape kimono, limited by width">
  {% endthumbnail %}
{% endif %}
Portrait kimono, limited by height

JPEG · 141×200 · 19.3 KB

margin

{% load thumbnail %}
{% thumbnail "images/caritas-romana.webp" "200x200" as im %}
  <div style="display: inline-block; background: #e6e1d6">
    <img src="{{ im.url }}" loading="lazy"
         width="{{ im.width }}" height="{{ im.height }}"
         style="display: block; margin: {{ im|margin:"200x200" }}"
         alt="Caritas Romana centered in a 200 by 200 frame">
  </div>
{% endthumbnail %}
Caritas Romana centered in a 200 by 200 frame

JPEG · 200×159 · 15.0 KB

Not shown here

Alternative resolutions for high-density screens, dummy images, remote URLs as sources, and the density and authenticate options for PDF and other vector sources. See the settings and template references.

Sources

The original files the examples start from.

ImageWorkAuthorLicenseFile
Mona Lisa, original Mona Lisa Leonardo da Vinci Public domain JPEG · 1071×1600 · 390.4 KB
Earth from Apollo 11, original Earth from Apollo 11 NASA Public domain PNG · 1600×1440 · 630.8 KB
Caritas Romana, original Caritas Romana Artemisia Gentileschi Public domain WEBP · 1240×986 · 38.8 KB
Bouncing ball, original Bouncing ball Branko at English Wikipedia Public domain GIF · 82×82 · 6 frames · 2.1 KB
Kimono, original Kimono Momiji Jinzamomi and Kaede Fujisaki CC BY-SA 4.0 AVIF · 722×1024 · 83.4 KB
White border test image, original White border test image sorl-thumbnail tests BSD-3-Clause JPEG · 100×100 · 1.6 KB
EXIF orientation test image, original EXIF orientation test image sorl-thumbnail tests BSD-3-Clause JPEG · 200×100 · 5.6 KB