No description
  • Python 92.9%
  • Just 7.1%
Find a file
2025-11-21 09:04:27 -08:00
src/webimage typos 2025-11-21 08:57:58 -08:00
test add test for passing dir 2025-11-21 08:57:18 -08:00
.gitignore first commit 2025-11-14 16:31:33 -08:00
.python-version first commit 2025-11-14 16:31:33 -08:00
CONTRIBUTING.md first commit 2025-11-14 16:31:33 -08:00
justfile version bump 2025-11-14 16:44:49 -08:00
LICENSE.md first commit 2025-11-14 16:31:33 -08:00
pyproject.toml v0.3.1 bump version 2025-11-21 09:04:27 -08:00
README.md fix example closer to default values 2025-11-21 08:57:08 -08:00
uv.lock v0.3.1 bump version 2025-11-21 09:04:27 -08:00

🧙‍♀️ WebImage 🧙‍♂️

A tool to create responsive images for the web.

And Webby Mage makes it all happen under the hood!

Webify Your Pics

I always seem to have a hard time dealing with images on my blog. The pictures I take on my phone are great and all, but they are really big! And I don't really have a good system for getting them on a post.

It's so bad that sometimes I'll have an entire post about a vacation that has no pictures because it's kind of pain to resize those images, find some sort of naming convention, adding them to the correct directory, and then making sure that the HTML is responsive. (Are we even using srcset?)

This package hopes to ease the burden just a little bit by automating some of that process. Dump your images in a directory. Then use this package to automatically resize and optimize your images for the web, also optionally generating an HTML snippet you can drop in your code.

Getting Started

Keep in mind, WebImage is a work in progress.

However, you are welcome to check out the contribution guide if you wish to help!

From PyPI

You can install WebImage as a dependency.

# With pip
python -m pip install webimage

# or uv
uv add webimage

Install Using git

You can work on, contribute to, or use WebImage by using git:

git clone https://git@codeburg.com:pythonbynight/webimage
cd webimage

Install Dependencies

I recommend using uv for dependency management. You can use other package managers that allow you to install from pyproject.toml, but your mileage may vary.

If you don't have uv installed, you can use a curl command:

curl -LsSf https://astral.sh/uv/install.sh | sh

If you already have it installed, you may want to make sure that you are using the latest version.

uv self update

Once uv is installed or updated, you can load the project dependencies.

uv sync

This will create a virtual environment and install all project dependencies in there.

Note: Currently, the only two dependencies are attrs and pillow

Usage

You can use WebImage as any other Python package.

from webimage import WebImage

src_path: Path = path/to/img.jpg

webimg = WebImage(src_path=src_path)

# Creates default set (3 total) of responsive images
webimg.render()

# Creates a single image based predefined options of "small" (350px width), "medium" (600px width), "large" (1200 px width), and "thumbnail" (150px width)
webimg.render(size=small)

These are just two methods, with more options available. But first, let's look at what we're trying to achieve.

Output

WebImage comes configured with some sensible defaults to create a set of images for responsive websites.

The goal is to take an image and create several copies at different scale/sizes, which will then be selected by the browser based on certain conditions.

The target html will follow this pattern.

<img
  srcset=""
  src=""
  sizes=""
  alt=""
/>
  • srcset contains possible image sources for the browser's User Agent to use.
  • src is the image URL that serves as a fallback.
  • sizes represent one or more values separated by commas, which helps a browser choose which image to render.
  • alt is the textual replacement for the image (value not rendered by WebImage).

sizes

The trickiest of these is generally the sizes element, because it can be dependent on a user's specific CSS layout. The sizes attribute is meant to provide a set of media conditions (screen widths) to indicate which image defined in srcset should be displayed.

WebImage defaults* the sizes attribute as follows:

<img
  ...
  sizes="(max-width: 600px) 96vw, 800px>"
  ...
/>

What this snippet means is that if the viewport width is less than 600px (usually a tablet or mobile), then the image should use 96vw width (which is nearly 100% of the viewport width). Otherwise, if the viewport is wider than 600px, the image should be 800px wide.

These values can be configured/modified when defining the WebImage object.

*defaults: This benefits a layout with a "content" block that is approximately 800px wide. If the style of your website demands a wider "content" area, or if your site has two or three columns (and different media queries), this default may not work.

srcset

The images generated by WebImage should be referenced in the srcset attribute. It should contain a path to the image, the image name, and the actual width (w) in pixels. By default, WebImage will produce three images:

<img
  srcset="
    image_200w.webp 200w,
    image_500w.webp 500w,
    image_800w.webp 800w
  "
  ...
/>

By default, the images generated will be *.webp format, but this can be configured for jpg or png formats through the WebImage object.

Optionally, srscet items can be generated through a SrcsetItem property, which will also allow you to prepend the image name with a specific path (i.e., to instead render something like /static/img/image_200w.webp 200w)

src

Lastly, the src attribute serves as the default image the browser will try to load. This is generally for older browsers that don't support the srcset attribute. For our purposes, the default will be the "largest" of the images generated.

Example

Like mentioned above, there are several configuration elements to help render the images you need. WebImage will also optionally create a file with the html markup that corresponds to each image.

from webimage import WebImage

src_path = "path/to/image.jpg"

webimg = WebImage(
    src_path=src_path,
    destination_path="/static/images",
    max_width=1000, 
    img_format="png",
    html_output=True
)

webimg.render()

The src_path is the only required attribute and self explanatory (the location of the file to be resized).

By default, the resulting images would be created in the same directory of the source image. However, you may prefer to have these rendered elsewhere. That is what destination_path is for. Above, the rendered images also go into a static images directory.

The above code will render three images in the /static/images directory and a txt file that contains the html snippet for these images (be aware that the rendered file will be in the destination_path). In image.txt, you will find the following snippet:

<img
  srcset=
  "
    image_200w.png 200w,
    image_600w.png 600w,
    image_1000w.png 1000w
  "
  src="image_1000w.png"
  sizes="(max-width: 1000px) 96vw, 1000px>"
  alt="..."
/>
<!-- Remember to add alt text -->

Details

When creating your WebImage object, you can include any of the following attributes.

  • source_path: The path to the source image.
  • destination_path: The path to the destination directory where the rendered images will be saved.
  • max_width: The maximum width of the rendered images in pixels.
  • min_width: The minimum width of the rendered images in pixels.
  • total_images: The total number of images to be rendered.
  • img_format: The format of the rendered images (e.g., "jpg", "png").
  • quality: The quality of the rendered images (1-100).
  • html_output: Whether to generate an HTML snippet for the rendered images.
  • html_srcset_path: The string that will be concatenated in front of the image name in the srcset attribute.

This allows you to control how WebImage generates images.

For example, setting a min_width and max_width will determine the widths of the smallest and largest images referenced in the srcset attribute.

By default, WebImage will generate 3 images, but this can be any value from 1 to 5.

The img_format determines the output image format, while the quality is used by PIL. The lower the quality, the lower the filesize.

Lastly, the html_output controls whether a txt, md, or html file is created with the html snippet appropriate for a set of images. (Configurable through an HTMLWriter class.)

Webi ... Mage?

The transformation of the images is handled by another class called Mage. Get it? Mage? Because the images are transformed? 🧙 Hillarious, right?

When you call render on the WebImage class, it uses Mage under the hood to do the transformations.

But you can also access the mage class directly:

from webimage import WebImage

src_path: Path = path/to/img.jpg

webimg = WebImage(src_path=src_path)

# Inspect the properties of the image
webimg.mage.inspect_image()

This will return an object with the filename, size, img_format, and mode of the image file.

webimg.mage.transform(size=(200, 200), operation="contain")

This will transfrom the image to a size of 200x200 pixels, and it will resize the image with max width/height within the 200x200 size, maintaining aspect ratio and cropping if necessary.

The other operations supported (from PIL) are as follows:

  • scale: Resizes image given a specific ratio (target width / source width).
  • contain: Resizes image with max width/height within given size, maintaining aspect ratio.
  • cover: Resizes image to fill the given size while maintaining aspect ratio, cropping if necessary.
  • fit: Resized and cropped version, based on given size.
  • pad: Resizes and pads the image, expanded to fill the requested aspect ratio and size.

Other Stuff

As refrenced earlier, an HTMLWriter handles the output to HTML.

This is still a work in progress, but for now, it supports adding a CSS class to the srcset output, by passing arguments on the render command.

from webimage import WebImage

src_path: Path = path/to/img.jpg

webimg = WebImage(max_width=800, src_path=src_path)

webimg.render(css_class="webimage")

This will produce something like this:

<img class="webimage"
  srcset=
  "
    image_200w.png 200w,
    image_600w.png 600w,
    image_800w.png 800w
  "
  src="image_800w.png"
  sizes="(max-width: 800px) 96vw, 800px"
  alt="..."
/>

What Next?

This is still a bit rough around the edges.

The fact is, I have been looking for ways to make it easier to blog about stuff and add some cool pictures, but dealing with them always feels like a chore.

If I could automate some of that work of resizing and generating responsive markup, it may encourage me to include more pics!

But at the same time, I would like for this library to be flexible and stable enough that others could use it as well.

I'll be working on ironing out some of those rough edges and adding any features that seem useful.

If you're up to it...

Contributing

Well, you made it down this far, and that's saying something!

If you think you can help in any way, shape, or form, look at the contribution guide for details.

Thanks!