Auto-generate a social media image in Statamic
When writing blog posts you likely want to share your post on Twitter or any other social network to give your posts an audience. Let's stay with Twitter for this post. While writing blog posts might be the main task, the title and content is clear, there is often an image used to and pulled by social media.
Instead of searching the web for meaningful backgrounds, why not just putting the title of your post and the name of your blog on an uni-color background. For creating this image we need to hook into Statamics event system to generate the image after saving the blog post.
Creating the event listener
To create a new listener that actually runs the image generation we run
php artisan make:listener GenerateBlogPostImage
which creates a new listener at app/Listeners/GenerateBlogPostImage.php
. Next the listener needs to be run, when an entry is saved. For this the EventSaved event comes in handy. So let's register our listener by putting in it app/Providers/EventServiceProvider.php
.
use App\Listeners\GenerateBlogPostImage;
use Statamic\Events\EntrySaved;
class EventServiceProvider extends ServiceProvider
{
/**
* The event listener mappings for the application.
*
* @var array
*/
protected $listen = [
// Some other events
EntrySaved::class => [
GenerateBlogPostImage::class,
],
];
// Further stuff
}
Now, whenever an entry is saved, our GenerateBlogPostImage
listener is called and can do its magic, generating a 1200x627
pixel image for Twitter. For creating the images I am going to use the Intervention Image library. Back to our listener file we need to load the Intervention Image library and generate the image.
Here is complete file:
use Illuminate\Support\Str;
use Intervention\Image\ImageManager;
use Statamic\Events\EntrySaved;
use Statamic\Entries\Entry;
class GenerateBlogPostImage
{
const IMAGE_WIDTH = 1200;
const IMAGE_HEIGHT = 627;
protected string $baseImagePath;
protected $imageManager;
public function __construct()
{
$this->baseImagePath = public_path('assets/posts');
$this->imageManager = new ImageManager([
'driver' => 'imagick',
]);
}
public function handle(EntrySaved $event): void
{
/** @var Entry $entry */
$entry = $event->entry;
if($entry->collectionHandle() == 'posts' && $entry->published() === true) {
// Create the image
$image = $this->imageManager->canvas(self::IMAGE_WIDTH, self::IMAGE_HEIGHT, '#E5E7EB');
// Title
$image->text($entry->title, self::IMAGE_WIDTH/2, self::IMAGE_HEIGHT/2, function ($font) {
$font->size(46);
$font->color('#111827');
$font->file(storage_path('Rubik.ttf'));
$font->align('center');
$font->valign('middle');
});
// Website
$image->text('codedge.de', self::IMAGE_WIDTH/2, self::IMAGE_HEIGHT/1.25, function($font) {
$font->size(28);
$font->color('#ff6633');
$font->file(storage_path('Rubik.ttf'));
$font->align('center');
});
// Save the image
$imagesPath = $this->baseImagePath . '/' . Str::slug($entry->title).'.png';
$image->save($imagesPath, 100);
}
}
}
The requirement for Intervention is PHPs imagick
extension. Alternatively you can use gd
too. I restricted the generation to:
-
posts in the
posts
collections - only published items
Intervention Image library
I suspect there is more than one libray to use for this use-case, but I am pretty happy with Intervention. I never used it before, but it seems to be stable and does exactly what it should do.
Imagick or GD
First I started with using gd
but it turned out I cannot properly render text. When using gd
you can specify built-in fonts 1 to 5, which wasn't my intention. I wanted to use my own font. With imagick
I was able to render text properly with a custom .ttf
file.