Dynamically generate ogimages (opengraph) for a multi-lingual site and add them to the Kirby-SEO plugin automatically

Good afternoon,

based on the cookbook recipe of how to use content representation to dynamically generate opengraph images (ogimages), I want to share with the forum my solution which has a few advanced steps:

  • First, I am using the GitHub - tobimori/kirby-seo: 🔎 SEO for Kirby CMS – done right. plugin to manage my SEO needs (I researched all current ones and found this works best). While the plugin supports ogimages for each page, it does not generate them automatically. And ogimages need to be stored on the filesystem to be supported so content representation is out the windows
  • Second, I am running a multi-language setup
  • Third, I am extremely cautious of pagespeed so having the images already pre-generated sounds like a good idea to me

Now the solution to this is very easy:

  • Since my ogimage is based on the title of the page, every time the title of a page is changed, the image is generated temporarily - simply by calling a method in the appropriate hook
  • Once generated, we create a Kirby File object out of it, rename it properly and move it to the location where we need it. Then we delete it
  • This way we can access the UUID for this image and store it directly in the content file of the page - which is the trick to dynamically insert the ogimage into the SEO plugin
  • If an ogimage exists, we replace it with the updated version
  • A tiny detail which bothered me in the cookbook example was the fixed positioning of the rectangle - since my text can be 1…n lines long, I have fix this so the rectangle would always be relative to the text iteself

This is my solution:

In config.php, add botht he hook and the method:

        'page.changeTitle:after' => function (Kirby\Cms\Page $newPage, Kirby\Cms\Page $oldPage) {
            $ogImageURL = generateOgImage($newPage);
            $newPage->update([
                'Ogimage' => $ogImageURL,
              ]);

        },
function generateOgImage(Kirby\Cms\Page $page){

        // Load the blank og image background as the image basis
        $baseImgPath = './assets/images/ogimage_blank.png';
        $canvas = imagecreatefrompng($baseImgPath);

        // Define colors
        $brandColor      = imagecolorallocate($canvas, 0, 5, 226);
        $textColor       = imagecolorallocate($canvas, 0, 17, 71);

        // Path to .ttf font file
        $fontFile = './assets/css/font/your-font.ttf';

        // Write page title to canvas
        $title  = $page->title()->toString();
        $title  = wordwrap($title, 25);
        $printedText = imagefttext($canvas, 50, 0, 150, 185, $textColor, $fontFile, $title);

        // Draw rectangle - relatively seen below the printed text
        imagefilledrectangle($canvas, $printedText[0], $printedText[1]+30, $printedText[0]+365, $printedText[1]+40, $brandColor);

        // Save image to the disk (temporarily)
        $tempImageFile = './content/'.$page->diruri().'/'.$page->slug().'.png';
        $ogImageFile = $page->slug().'_ogimage.png';

        // If writing the PNG is successful
        if(imagepng($canvas, $tempImageFile)){

            // Check if the ogImage already exists. If yes, replace it...
            if(F::exists(kirby()->root().'/content/'.$page->diruri().'/'. $ogImageFile)){

                $file = $page->file($ogImageFile)->replace(kirby()->root().'/content/'.$page->diruri().'/'.$page->slug().'.png', true);

                return $file->uuid()->toString();

            } else {

                // ... if not, create a Kirby File object, save as ogimage and remove the original image file created via PHP
                $file = File::create(['filename' => $ogImageFile, 'parent' => $page, 'source' => $tempImageFile], true);

                return $file->uuid()->toString();
            }

        } else {

            // Generation did not work - lets not save any file reference then
            return '';
        }   

}

I wanted to share this in hope of saving other people time while thinking up with the same problem.

Andreas

3 Likes

There was a bug in my ogimage generation method - it forgot to save the image language specific. I also cleaned it up a bit. Here goes:

/**
 * This function generates dynamic opengraph images depending on the title
 * of a page. It then writes the UUID into the appropriate content-file of
 * each page for use in the tobimori.seo plugin
 *
 * @param Kirby\Cms\Page $page
 * @return void
 */
function generateOgImage(Kirby\Cms\Page $page){

        // Load the blank og image background as the image basis
        $baseImgPath = './assets/images/ogimage_blank.png';
        $canvas = imagecreatefrompng($baseImgPath);

        // Define colors
        $brandColor      = imagecolorallocate($canvas, 0, 5, 226);
        $textColor       = imagecolorallocate($canvas, 0, 17, 71);

        // Path to .ttf font file
        $fontFile = './assets/css/font/your-font.ttf';

        // Write page title to canvas
        $title  = $page->title()->toString();
        $title  = wordwrap($title, 25);
        $printedText = imagefttext($canvas, 50, 0, 150, 185, $textColor, $fontFile, $title);

        // Draw rectangle - relatively seen below the printed text
        imagefilledrectangle($canvas, $printedText[0], $printedText[1]+30, $printedText[0]+365, $printedText[1]+40, $brandColor);

        // Save image to the disk (temporarily)
        $tempImageFile = './content/'.$page->diruri().'/'.$page->slug().'.'.$page->translation()->code().'.png';
        $ogImageFile = $page->slug().'.'.$page->translation()->code().'.ogimage.png';

        // If writing the PNG is successful
        if(imagepng($canvas, $tempImageFile)){

            // Check if the ogImage already exists. If yes, replace it...
            if(F::exists(kirby()->root().'/content/'.$page->diruri().'/'. $ogImageFile)){

                $file = $page->file($ogImageFile)->replace($tempImageFile, true);

                return $file->uuid()->toString();

            } else {

                // ... if not, create a Kirby File object, save as ogimage and remove the original image file created via PHP
                $file = File::create(['filename' => $ogImageFile, 'parent' => $page, 'source' => $tempImageFile], true);

                return $file->uuid()->toString();
            }

        } else {

            // Generation did not work - lets not save any file reference then
            return '';
        }   

}
1 Like