Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Image Workshop Laravel Package

sybio/image-workshop

ImageWorkshop is a PHP image manipulation library built on GD. It provides a layer-based, chainable API for resizing, cropping, rotating, watermarking, applying filters, and creating thumbnails, with support for transparent PNGs and GIFs.

View on GitHub
Deep Wiki
Context7

Getting Started

Install and verify GD first. ImageWorkshop is a thin layer over GD, so the extension — not the library — is what usually breaks in CI or on shared hosting:

composer require sybio/image-workshop
php -r "var_dump([extension_loaded('gd'), gd_info()['FreeType Support'], function_exists('imagewebp')]);"

You need the gd extension with at least PNG/JPEG support; FreeType is mandatory for any addText() call and WebP/AVIF support depends entirely on how PHP was compiled, not on the package version.

Where to look first. The repository's README.md and its examples/ folder are the actual documentation — there is no hosted manual. The two classes that matter:

  • PHPImageWorkshop\ImageWorkshop — static factory (initFromPath, initFromString, initBlankImage, initFromFont, initFromPathAndFormat).
  • PHPImageWorkshop\Core\ImageWorkshopLayer — the layer object returned by every factory, on which all manipulation methods live.

First use case — resize an uploaded avatar to 200×200 with a crop:

use PHPImageWorkshop\ImageWorkshop;

$layer = ImageWorkshop::initFromPath(Storage::path('tmp/upload.jpg'));
$layer->cropInPixel(200, 200, 0, 0, 'LT');       // top-left anchored crop
$layer->resizeInPixel(200, 200, true);           // true = preserve ratio
$layer->save(Storage::path('public/avatars'), 'user-42', 'jpg', true, null, 90);

save($dir, $filename, $extension, $createFolders, $backgroundColor, $quality) is the only output path — there is no getResource()-and-forget alternative that keeps metadata intact.

Implementation Patterns

Wrap it in a service, never call the facade inline. The layer object is mutable and every method returns it, so chained calls inside controllers become untestable. A small ImageProcessor service injected via the container keeps the package out of your application code:

class ImageProcessor
{
    public function open(string $absPath): ImageWorkshopLayer
    {
        return ImageWorkshop::initFromPath($absPath);
    }

    public function thumb(string $absPath, string $dir, string $name, int $w, int $h): void
    {
        $this->open($absPath)
            ->resizeInPixel($w, $h, true, 0, 0, 'MM')   // center-anchored
            ->save($dir, $name, 'jpg', true, '#ffffff', 85);
    }
}

Presets as an array, not as code branches. Day-to-day you'll have three or four target sizes (thumb, card, hero). Hold them in a config file and loop; resizeInPixel handles both upscaling and downscaling, so a single call covers both:

// config/images.php
return [
    'card' => ['w' => 640, 'h' => 360, 'anchor' => 'MM', 'quality' => 82],
    'hero' => ['w' => 1600, 'h' => 600, 'anchor' => 'MM', 'quality' => 88],
];

Watermarking and text overlays are a two-layer composite. This is the package's real differentiator over imagecopyresampled. Create a blank layer at your base size, paste the photo onto it, then paste a text watermark layer on top, and save only the bottom:

$canvas = ImageWorkshop::initBlankImage(1200, 630, '#ffffff');
$photo  = ImageWorkshop::initFromPath($src);
$canvas->pasteImageLayer($photo, 0, 0, 'LT');

$mark = ImageWorkshop::initBlankImage(400, 80);
$mark->addText('© acme', '/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf', 20, '#ffffff', 0, null, 10, 10);

$canvas->pasteImageLayer($mark, 0, 0, 'RB');
$canvas->save($dir, 'social', 'jpg', true, null, 90);

Queue heavy work. ImageWorkshop loads the entire GD resource into memory (RGBA at 4 bytes/px — a 4000×3000 JPEG is ~48 MB just in memory). Push transformations to a job with an explicit memory_limit bump, and never do batch processing inside a request.

Storage integration needs absolute paths. initFromPath() does not understand Laravel disks or storage:// URLs. Use Storage::path() (local) or download remote files to a tmp/ directory first:

$tmp = tempnam(sys_get_temp_dir(), 'iw_');
file_put_contents($tmp, Storage::disk('s3')->get($key));
$layer = ImageWorkshop::initFromPath($tmp);
// ... save, then @unlink($tmp);

Gotchas and Tips

Maintenance status is the headline gotcha. Last release 2022-05, zero dependents, and the original author's activity has been effectively static for years. It is not abandoned in the sense of being broken — it works fine on PHP 8.x — but there will be no WebP/AVIF improvements, no security fixes, and no modern-image-format work from upstream. For greenfield Laravel projects, intervention/image v3 is the better long-term bet; ImageWorkshop is appropriate when you're inheriting an existing codebase that already uses it, or when the layer/composite model saves you real work.

Namespace ≠ package name. Composer installs sybio/image-workshop, but every class lives under PHPImageWorkshop\. use Sybio\ImageWorkshop\... is a common mis-import that produces a confusing class-not-found.

The save() signature is positional and unforgiving. save($dir, $filename, $ext, $createFolders, $backgroundColor, $quality) — the third argument is the extension, not the destination path. Passing a full filename as $dir silently writes to the wrong place or fails on mkdir.

backgroundColor on save controls transparency. For PNG output with an alpha channel you must pass null (not 'transparent', not '#000000'). Passing a color string flattens the image. Conversely, saving a transparent PNG to JPEG without a background color produces a black background — a classic bug on white-themed sites.

Composites need a base layer. pasteImageLayer() pastes onto the calling layer. If you call it on a layer created with initFromPath(), you're pasting into the photo's bounds and anything outside gets clipped. Always initBlankImage() the target size first.

Rotation fills with the background color you give it. rotate(90, '#ffffff') — omit the second argument and corners become transparent (fine for PNG, black for JPEG).

Font paths are absolute and unresolvable. addText() takes a filesystem path to a .ttf, no base_path() resolution, no font discovery. Ship fonts in resources/fonts/ and resolve them yourself; missing fonts throw a RenderingException that's easy to misread as a GD compilation problem.

Format detection uses the file extension, not magic bytes. An uploaded photo.jpg that is actually a PNG will be opened by its extension and may fail or produce garbage. Validate uploads with mimetypes rules rather than trusting getClientOriginalExtension() before handing the path to initFromPath().

Wrap every call in a try/catch for \Exception. The library throws generic \Exception with messages like "The folder path given is not a valid directory" — it does not use typed exception classes, so you cannot distinguish user-input errors from disk errors programmatically.

You can reach the raw GD resource via $layer->getLayer() (a ImageWorkshopLayer exposes getResource()), which is the escape hatch for anything the library doesn't cover — imagecreatefromavif() on PHP 8.1+, custom filters, or reading pixel data. Use it before reaching for a second image library.

Test with initBlankImage() in unit tests. Because the package depends on GD, tests need ext-gd in CI. Generating a blank layer rather than loading a fixture image keeps the suite fast and avoids binary fixtures in the repo.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
elsayed85/laravel-api-exceptions
ouhssini/laravel-demo-mode
daworks/ncloud-outbound-mailer-driver
gp247/cms
ignibyte/aic-gates
nexa-merchant/blog
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
cgv/localized-resource