Search by

pecinaon / processwire-modern-images

pecinaon

Lets ProcessWire use WebP, AVIF and HEIC images as sources, not just as output. Adds an ImageSizer engine so modern originals can be resized, cropped and thumbnailed like a JPEG.

Package info

github.com/pecinaon/processwire-modern-images

Type:processwire-module

pkg:composer/pecinaon/processwire-modern-images

Statistics

Installs: 1

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v1.0.0 2026-09-01 16:43 UTC

This package is auto-updated.

Last update: 2026-09-01 16:59:17 UTC


README

Lets ProcessWire use WebP, AVIF and HEIC images as sources — resized, cropped and thumbnailed like a JPEG — instead of only writing them as output.

CI

The problem

Upload a .webp to an image field and ProcessWire accepts the file, then fails at everything afterwards:

blog-cover.0x260.webp - not a supported image type

No variation is produced. No crop. Not even the thumbnail in the page editor, which renders as a broken image with that message beside it.

This is not a bug in your site. ImageSizerEngine lists exactly four readable types — gif, jpg, jpeg, png (wire/core/ImageSizerEngine.php:274) — and WebP support in the core is output only: webpAdd and webpOnly write a .webp next to a JPEG, and $image->webp->url asks for one. Nothing reads one back.

The core Imagick engine does not help either. Its validSourceImageFormats() is the same four types.

So today a site that wants to work in modern formats has to keep a JPEG or PNG of every image purely so ProcessWire can resize it. That is the constraint this module removes.

What it does

Format Read Written Result
WebP GD or Imagick GD or Imagick variations stay WebP
AVIF GD or Imagick GD or Imagick variations stay AVIF
HEIC / HEIF Imagick only never original kept, variations written as WebP or AVIF

A WebP original produces WebP variations. An AVIF original produces AVIF. The format the author chose is the format that gets served, which is the whole point.

HEIC is the deliberate exception. Every iPhone shoots it by default, so it turns up in real uploads whether a site asks for it or not — but outside Safari no browser renders it. The module reads it so the upload just works, keeps the original at full quality as the archive copy, and writes the variations in a format the visitor's browser can actually display. It never writes HEIC.

It also adds the readable extensions to every image field on install, because a field checks uploads against its own list and would otherwise still refuse a .webp however capable the server is.

Install

composer require pecinaon/processwire-modern-images

Then Modules → Refresh → Install Modern Images.

Until the package is on Packagist, add it as a VCS repository first:

{
  "repositories": [
    { "type": "vcs", "url": "https://github.com/pecinaon/processwire-modern-images.git" }
  ],
  "require": { "pecinaon/processwire-modern-images": "^1.0" },
  "config": { "allow-plugins": { "composer/installers": true } }
}

Two things that bite people:

  • config.allow-plugins is mandatory from Composer 2.2 on. Without it the installer plugin never runs, the package lands in vendor/ instead of site/modules/, and ProcessWire never sees it.
  • Composer resolves this from git tags. Without one you need dev-main and a matching minimum-stability.

No template changes. Nothing to call. Existing code that does $image->size(800, 0)->url starts working on modern originals.

Requirements

  • ProcessWire 3.0.138 or newer
  • PHP 8.1 or newer
  • WebP and AVIF: GD built with libwebp / libavif, which is the common case, or the imagick extension
  • HEIC: the imagick extension, with ImageMagick built against libheif. GD has no HEIC decoder and there is no sign of one coming

Nothing is assumed from the PHP version. The module asks gd_info() and Imagick::queryFormats() at runtime, and a format whose library is missing is simply not offered — the site behaves exactly as it does without the module rather than failing on the first upload.

The module's configuration screen shows what the server can do, format by format. That table is the first thing to check when something is not working.

Settings

Setting Default What it is
WebP quality 90 used when the caller did not pass webpQuality to size()
AVIF quality 45 AVIF holds up at lower numbers than WebP; 45 is roughly WebP 80 by eye
AVIF encoder speed 6 0 is slowest and smallest, 10 fastest and largest
HEIC delivered as WebP what variations of a HEIC upload are written in
Update image fields on add the readable extensions to image fields when installing

A webpQuality passed to size() always beats the module setting. That is the caller being explicit, and an explicit request should never lose to a default.

How it works

The engine wraps ProcessWire's own pipeline rather than replacing it:

  1. decode the original into a temporary lossless PNG
  2. re-inspect, so the engine believes it is working on that PNG
  3. hand off to ImageSizerEngineGD::processResize() — unmodified
  4. encode the result back into the target format

The alternative was to copy processResize() and add two switch branches. That method is over 300 lines and carries gamma correction, sharpening, the memory guard, Exif rotation and IPTC handling. A copy forks all of it and rots at the next core release. The wrapper costs one extra decode and encode per variation, paid once because variations are cached, and cannot be broken by a core change short of a signature change.

PNG is the intermediate because it is lossless. A JPEG intermediate would quantise every variation a second time, and the point of accepting a WebP original is not to degrade it on the way through. Alpha is carried explicitly through both hops — without imagesavealpha() GD drops it, and a logo on a transparent ground comes back on black.

The engine only claims formats the core cannot read. A JPEG never enters the wrapper; supported() returns false and ProcessWire hands it to the default GD engine as before.

What it deliberately does not do

  • Convert your images behind your back. A WebP original stays WebP. The one exception is HEIC, which no browser would render, and the module says so on its settings screen rather than doing it silently.
  • Write HEIC. Reading it is a convenience for uploads. Writing it would produce files most visitors cannot open.
  • Replace $config->webpOptions. Core's ->webp derivative behaviour is untouched; this module is about what can be used as a source.
  • Add a <picture> helper or format negotiation. That is a template concern, and there are modules for it.

Contributing

See CONTRIBUTING.md. In short: composer install, then composer test, composer analyse and composer lint.

Licence

MIT. See LICENSE.