minimal image generation example
This commit is contained in:
@@ -16,7 +16,26 @@ if ( ! defined( 'ABSPATH' ) ) {
|
||||
|
||||
final class Ruady_OG_Head {
|
||||
public static function init(): void {
|
||||
add_action( 'init', [ __CLASS__, 'register_endpoint' ] );
|
||||
add_action( 'template_redirect', [ __CLASS__, 'maybe_render_image' ] );
|
||||
add_action( 'wp_head', [ __CLASS__, 'render_head_markup' ], 20 );
|
||||
|
||||
register_activation_hook( __FILE__, [ __CLASS__, 'activate' ] );
|
||||
register_deactivation_hook( __FILE__, [ __CLASS__, 'deactivate' ] );
|
||||
}
|
||||
|
||||
public static function register_endpoint(): void {
|
||||
// Adds /og-image/ to singular permalinks.
|
||||
add_rewrite_endpoint( 'og-image', EP_PERMALINK | EP_PAGES );
|
||||
}
|
||||
|
||||
public static function activate(): void {
|
||||
self::register_endpoint();
|
||||
flush_rewrite_rules();
|
||||
}
|
||||
|
||||
public static function deactivate(): void {
|
||||
flush_rewrite_rules();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -84,6 +103,7 @@ final class Ruady_OG_Head {
|
||||
$site_url_p = parse_url($site_url);
|
||||
$site_host = $site_url_p['host'];
|
||||
|
||||
$og_image_url = user_trailingslashit( trailingslashit( $post_url ) . 'og-image' );
|
||||
$description = self::make_description(wp_strip_all_tags( $excerpt ));
|
||||
|
||||
// Example business logic:
|
||||
@@ -100,7 +120,7 @@ final class Ruady_OG_Head {
|
||||
<meta property="og:type" content="website">
|
||||
<meta property="og:title" content="<?php echo esc_attr( $post_title ); ?> – <?php echo esc_attr( $site_title ); ?>">
|
||||
<meta property="og:description" content="<?php echo esc_attr( $description ); ?>">
|
||||
<!--<meta property="og:image" content="">--><!-- Load error, please check URL -->
|
||||
<meta property="og:image" content="<?php echo esc_url( $og_image_url ) ?>">
|
||||
|
||||
<!-- Twitter Meta Tags -->
|
||||
<meta name="twitter:card" content="summary_large_image">
|
||||
@@ -108,16 +128,89 @@ final class Ruady_OG_Head {
|
||||
<meta property="twitter:url" content="<?php echo esc_url( $site_url ); ?>">
|
||||
<meta name="twitter:title" content="<?php echo esc_attr( $post_title ); ?> – <?php echo esc_attr( $site_title ); ?>">
|
||||
<meta name="twitter:description" content="<?php echo esc_attr( $description ); ?>">
|
||||
<!--<meta name="twitter:image" content="">-->
|
||||
<meta property="twitter:image" content="<?php echo esc_url( $og_image_url ) ?>">
|
||||
|
||||
<!-- Per-Post Head HTML plugin -->
|
||||
<meta name="x-post-id" content="<?php echo esc_attr( (string) $post->ID ); ?>">
|
||||
<meta name="x-post-type" content="<?php echo esc_attr( $post->post_type ); ?>">
|
||||
<link rel="canonical" href="<?php echo esc_url( $post_url ); ?>">
|
||||
<?php
|
||||
|
||||
return trim( (string) ob_get_clean() );
|
||||
}
|
||||
|
||||
public static function maybe_render_image(): void {
|
||||
// Endpoint absent.
|
||||
$endpoint_value = get_query_var( 'og-image', null );
|
||||
if ( null === $endpoint_value ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ! is_singular() ) {
|
||||
self::render_text_error( 404, 'Not found' );
|
||||
}
|
||||
|
||||
$post = get_queried_object();
|
||||
if ( ! $post instanceof WP_Post ) {
|
||||
self::render_text_error( 404, 'Not found' );
|
||||
}
|
||||
|
||||
self::render_png_for_post( $post );
|
||||
}
|
||||
|
||||
private static function render_png_for_post( WP_Post $post ): void {
|
||||
if ( ! function_exists( 'imagecreatetruecolor' ) ) {
|
||||
self::render_text_error( 500, 'GD extension not available' );
|
||||
}
|
||||
|
||||
$width = 1200;
|
||||
$height = 630;
|
||||
|
||||
$image = imagecreatetruecolor( $width, $height );
|
||||
if ( ! $image ) {
|
||||
self::render_text_error( 500, 'Could not create image' );
|
||||
}
|
||||
|
||||
$bg = imagecolorallocate( $image, 224, 241, 212 ); // theme background (light green)
|
||||
$fg = imagecolorallocate( $image, 0, 0, 0 ); // theme foreground (black)
|
||||
$tt = imagecolorallocate( $image, 72, 119, 40 ); // theme primary (dark green)
|
||||
$accent = imagecolorallocate( $image, 204, 235, 235 ); // theme tertiary (light blue)
|
||||
|
||||
imagefilledrectangle( $image, 0, 0, $width, $height, $bg );
|
||||
imagefilledrectangle( $image, 0, 0, $width, 12, $accent );
|
||||
|
||||
$title = wp_strip_all_tags( get_the_title( $post ) );
|
||||
$site = wp_strip_all_tags( get_bloginfo( 'name' ) );
|
||||
|
||||
// Minimal bitmap-font rendering. Fine for a skeleton.
|
||||
// For better typography, switch to imagettftext() with a bundled font.
|
||||
$title = self::truncate( $title, 90 );
|
||||
$site = self::truncate( $site, 90 );
|
||||
|
||||
imagestring( $image, 5, 40, 60, $title, $tt );
|
||||
imagestring( $image, 3, 40, 110, $site, $fg );
|
||||
imagestring( $image, 2, 40, 580, 'Post ID: ' . (string) $post->ID, $accent );
|
||||
|
||||
status_header( 200 );
|
||||
header( 'Content-Type: image/png' );
|
||||
header( 'Cache-Control: public, max-age=3600' );
|
||||
|
||||
imagepng( $image );
|
||||
imagedestroy( $image );
|
||||
exit;
|
||||
}
|
||||
|
||||
private static function render_text_error( int $status, string $message ): void {
|
||||
status_header( $status );
|
||||
header( 'Content-Type: text/plain; charset=utf-8' );
|
||||
echo $message;
|
||||
exit;
|
||||
}
|
||||
|
||||
private static function truncate( string $text, int $max_len ): string {
|
||||
if ( mb_strlen( $text ) <= $max_len ) {
|
||||
return $text;
|
||||
}
|
||||
|
||||
return mb_substr( $text, 0, $max_len - 1 ) . '…';
|
||||
}
|
||||
}
|
||||
|
||||
Ruady_OG_Head::init();
|
||||
|
||||
Reference in New Issue
Block a user