Using PHP GD to generate OpenGraph/Twitter Card images?

Using PHP GD to generate OpenGraph/Twitter Card images?

Adam

Mr. Webwide
Administrator
Local time
00:41
Joined
Sep 24, 2019
Messages
1,252
Pronouns
he/him

Has anyone here tried this before? Would love to be able to share threads on here and have a script generate an image using the thread title, author avatar, etc. for Twitter cards and FB OpenGraph.

Would be awesome if anyone has any experience with this. Can't find anything online!

 

AndrewDavidJ

Member
Local time
20:41
Joined
Oct 7, 2019
Messages
21
Pronouns
He/Him

Yeah, easy-peasy! You can generate the images live (which I would not recommend) or generate them when the post is created and store them in some sort of cache, cloud storage, or server folder.

Let me know how I can help exactly.

 

Adam

Mr. Webwide
Administrator
Local time
00:41
Joined
Sep 24, 2019
Messages
1,252
Pronouns
he/him

Awesome! Great idea. I have not really used GD before so that seems like my biggest hurdle. I can fire off a URL when a thread is posted so I guess having the URL accept a GET parameter with the thread ID should be fine. Can load the image in the template using thread ID as an identifier in the file name... thinking out loud here!

 

tom

Creator of StickerRun®
Community Team
Local time
01:41
Joined
Oct 13, 2019
Messages
258

@Adam did you enable the api for Xenforo here already? If so, is it publicly available?

EDIT: Ah, I just saw that I need an API key that you have to create to use the API... 😅

EDIT 2: This should point you into the right direction... You need to have 2 font files (regular.ttf and semibold.ttf) and a profile.jpg in the same directory as this script.

PHP:
<?php
/**
    This code uses parts of this: https://stackoverflow.com/questions/50446492/how-to-add-line-breaks-br-in-imagestring-text-to-image-in-php
    and parts of this: https://stackoverflow.com/questions/37096710/merge-two-images-and-round-corner-in-php

    Thanks to the original authors!

    @TODO: Fetch the content (title + text) from the API using the https://webwide.io/api/threads/235 url or likewise
    @TODO: Fetch user profile image using api calls and returned urls
    @TOOD: Add a little more details to the card and maybe make it better looking
    @TODO: Refactor code so it doesn't look like it is currently looking...
    @TODO: Fix bug for empty lines. Padding is not added for those lines so the image height may be too low.
**/

$title = 'Using PHP GD to generate OpenGraph/Twitter Card images?';
$text = 'Has anyone here tried this before? Would love to be able to share threads on here and have a script generate an image using the thread title, author avatar, etc. for Twitter cards and FB OpenGraph.

Would be awesome if anyone has any experience with this. Can\'t find anything online!

Powered by webwide.io :)';
$image_width = 650; // pixels

$font = 10;
$line_height = 15;
$padding = 15;
$angle = 0;

text_to_image($title, $text, $image_width);

function text_to_lines($text, $image_width) {
    // Wrap text by word
    $wrapped_text = wordwrap($text, ($image_width/8));
    $lines = explode("\n", $wrapped_text);

    return [
        'text' => $text,
        'wrapped_text' => $wrapped_text,
        'lines' => $lines,
        'line_count' => count($lines),
    ];
}

function text_to_image($title, $text, $image_width, $colour = array(128, 128, 128), $background = array(255, 255, 255))
{
    global $font, $line_height, $padding, $angle;

    $title_options = text_to_lines($title, $image_width);
    $text_options = text_to_lines($text, $image_width);

    // Create blank image to print onto
    $image = imagecreate($image_width, (($text_options['line_count'] * $line_height) + (($title_options['line_count'] * $line_height) + ($padding * 3))) + ($padding * 2));
    $background = imagecolorallocate($image, $background[0], $background[1], $background[2]);
    $colour = imagecolorallocate($image,$colour[0],$colour[1],$colour[2]);
    imagefill($image, 0, 0, $background);
    $i = $padding + 5;

    // Add title line by line
    foreach($title_options['lines'] as $line){
        imagettftext($image, $font, $angle, $padding, $i, $colour, 'semibold.ttf', trim($line));
        $i += $line_height + 5;
    }

    $i += $padding;

    // Add text line by line
    foreach($text_options['lines'] as $line){
        imagettftext($image, $font, $angle, $padding, $i, $colour, 'regular.ttf', trim($line));
        $i += $line_height + 5;
    }

    // Create round image from profile picture
    $src = imagecreatefromjpeg('profile.jpg');
    $src_width = imagesx($src);
    $src_height = imagesy($src);
    $dstX = $image_width - $src_width - $padding;
    $dstY = (imagesy($image) / 2) - ($src_height / 2);
    $srcX = 0;
    $srcY = 0;
    $pct = 100;
    imagecolortransparent($src, imagecolorallocate($src, 255, 0, 255));

    // Create image mask
    $mask = imagecreatetruecolor($src_width, $src_height);
    $black = imagecolorallocate($mask, 0, 0, 0);
    $magenta = imagecolorallocate($mask, 255, 0, 255);
    imagefill($mask, 0, 0, $magenta);
    $r = min($src_width, $src_height);
    imagefilledellipse($mask, ($src_width / 2), ($src_height / 2), $r, $r, $black);
    imagecolortransparent($mask, $black);
    imagecopymerge($src, $mask, 0, 0, 0, 0, $src_width, $src_height, 100);
    imagedestroy($mask);

    // merge the two images to create the result.
    imagecopymerge($image, $src, $dstX, $dstY, $srcX, $srcY, $src_width, $src_height, $pct);

    // Print image to browser
    header("Content-type: image/jpeg");
    imagejpeg($image);
    imagedestroy($image);
    exit;
}

1571331490837.png

EDIT 3: Sorry for using my profile picture for your text... 🤦‍♂️

 
Last edited:

Adam

Mr. Webwide
Administrator
Local time
00:41
Joined
Sep 24, 2019
Messages
1,252
Pronouns
he/him

@Adam did you enable the api for Xenforo here already? If so, is it publicly available?

EDIT: Ah, I just saw that I need an API key that you have to create to use the API... 😅

EDIT 2: This should point you into the right direction... You need to have 2 font files (regular.ttf and semibold.ttf) and a profile.jpg in the same directory as this script.

PHP:
<?php
/**
    This code uses parts of this: https://stackoverflow.com/questions/50446492/how-to-add-line-breaks-br-in-imagestring-text-to-image-in-php
    and parts of this: https://stackoverflow.com/questions/37096710/merge-two-images-and-round-corner-in-php

    Thanks to the original authors!

    @TODO: Fetch the content (title + text) from the API using the https://webwide.io/api/threads/235 url or likewise
    @TODO: Fetch user profile image using api calls and returned urls
    @TOOD: Add a little more details to the card and maybe make it better looking
    @TODO: Refactor code so it doesn't look like it is currently looking...
    @TODO: Fix bug for empty lines. Padding is not added for those lines so the image height may be too low.
**/

$title = 'Using PHP GD to generate OpenGraph/Twitter Card images?';
$text = 'Has anyone here tried this before? Would love to be able to share threads on here and have a script generate an image using the thread title, author avatar, etc. for Twitter cards and FB OpenGraph.

Would be awesome if anyone has any experience with this. Can\'t find anything online!

Powered by webwide.io :)';
$image_width = 650; // pixels

$font = 10;
$line_height = 15;
$padding = 15;
$angle = 0;

text_to_image($title, $text, $image_width);

function text_to_lines($text, $image_width) {
    // Wrap text by word
    $wrapped_text = wordwrap($text, ($image_width/8));
    $lines = explode("\n", $wrapped_text);

    return [
        'text' => $text,
        'wrapped_text' => $wrapped_text,
        'lines' => $lines,
        'line_count' => count($lines),
    ];
}

function text_to_image($title, $text, $image_width, $colour = array(128, 128, 128), $background = array(255, 255, 255))
{
    global $font, $line_height, $padding, $angle;

    $title_options = text_to_lines($title, $image_width);
    $text_options = text_to_lines($text, $image_width);

    // Create blank image to print onto
    $image = imagecreate($image_width, (($text_options['line_count'] * $line_height) + (($title_options['line_count'] * $line_height) + ($padding * 3))) + ($padding * 2));
    $background = imagecolorallocate($image, $background[0], $background[1], $background[2]);
    $colour = imagecolorallocate($image,$colour[0],$colour[1],$colour[2]);
    imagefill($image, 0, 0, $background);
    $i = $padding + 5;

    // Add title line by line
    foreach($title_options['lines'] as $line){
        imagettftext($image, $font, $angle, $padding, $i, $colour, 'semibold.ttf', trim($line));
        $i += $line_height + 5;
    }

    $i += $padding;

    // Add text line by line
    foreach($text_options['lines'] as $line){
        imagettftext($image, $font, $angle, $padding, $i, $colour, 'regular.ttf', trim($line));
        $i += $line_height + 5;
    }

    // Create round image from profile picture
    $src = imagecreatefromjpeg('profile.jpg');
    $src_width = imagesx($src);
    $src_height = imagesy($src);
    $dstX = $image_width - $src_width - $padding;
    $dstY = (imagesy($image) / 2) - ($src_height / 2);
    $srcX = 0;
    $srcY = 0;
    $pct = 100;
    imagecolortransparent($src, imagecolorallocate($src, 255, 0, 255));

    // Create image mask
    $mask = imagecreatetruecolor($src_width, $src_height);
    $black = imagecolorallocate($mask, 0, 0, 0);
    $magenta = imagecolorallocate($mask, 255, 0, 255);
    imagefill($mask, 0, 0, $magenta);
    $r = min($src_width, $src_height);
    imagefilledellipse($mask, ($src_width / 2), ($src_height / 2), $r, $r, $black);
    imagecolortransparent($mask, $black);
    imagecopymerge($src, $mask, 0, 0, 0, 0, $src_width, $src_height, 100);
    imagedestroy($mask);

    // merge the two images to create the result.
    imagecopymerge($image, $src, $dstX, $dstY, $srcX, $srcY, $src_width, $src_height, $pct);

    // Print image to browser
    header("Content-type: image/jpeg");
    imagejpeg($image);
    imagedestroy($image);
    exit;
}


EDIT 3: Sorry for using my profile picture for your text... 🤦‍♂️

@tom that's incredibly helpful thank you! You nailed it exactly what I was going for. Is it ok if I use this code to start with?

 

Gummibeer

Astroneer
Moderator
Local time
01:41
Joined
Oct 5, 2019
Messages
1,161
Pronouns
he/him

I really recommend you to use one of the wrapping libraries. Doing imagick/gd in plain PHP is like connecting to MySQL without ORM.^^

 

Adam

Mr. Webwide
Administrator
Local time
00:41
Joined
Sep 24, 2019
Messages
1,252
Pronouns
he/him

I really recommend you to use one of the wrapping libraries. Doing imagick/gd in plain PHP is like connecting to MySQL without ORM.^^
Looking in to those now @Gummibeer! Certainly looks like it could make things a bit smoother. Thank you for those links. Your brain is like a handy tool/service directory. 😀

 
Last edited:

tom

Creator of StickerRun®
Community Team
Local time
01:41
Joined
Oct 13, 2019
Messages
258

Is it ok if I use this code to start with?

Yes sure, that's why "I" put it together, so you'll have an easier start. This includes some of the basic functions that are needed to work with GD.
I didn't find a library but @Gummibeer has some nice links that could help you a lot with all the stuff. Doing it manually is, like he said...
Doing imagick/gd in plain PHP is like connecting to MySQL without ORM.^^

It's so 2000 😂

Side story: I like doing it the "manual" way without libraries as it remembers me of my first time working with it, was around 2003 while building my first CMS. Nice memories 🥰

 

Adam

Mr. Webwide
Administrator
Local time
00:41
Joined
Sep 24, 2019
Messages
1,252
Pronouns
he/him

@Adam is this the result? 😊
Getting there! Do you like?


Just come in to a weird issue though. So... for some bizarre reason even if someones avatar is a PNG (like yours @Gummibeer) it will show as a .jpg file extension and work fine. But when grabbing it with imagefromjpeg in PHP it is failing. So need to figure out a way around that next.

Edit: Fixed it! if(exif_imagetype($avatar) == IMAGETYPE_PNG) { } else { }

You are now compatible. 😅 https://socialimage.server2.webwide.io/twitterCard.php?id=282

 
Last edited:

Gummibeer

Astroneer
Moderator
Local time
01:41
Joined
Oct 5, 2019
Messages
1,161
Pronouns
he/him

Getting there! Do you like?


Just come in to a weird issue though. So... for some bizarre reason even if someones avatar is a PNG (like yours @Gummibeer) it will show as a .jpg file extension and work fine. But when grabbing it with imagefromjpeg in PHP it is failing. So need to figure out a way around that next.

Edit: Fixed it! if(exif_imagetype($avatar) == IMAGETYPE_PNG) { } else { }

You are now compatible. 😅 https://socialimage.server2.webwide.io/twitterCard.php?id=282

Never trust the extension. 😉 Mimetypes are the only thing.

 
Top