minimal image generation example
This commit is contained in:
@@ -16,7 +16,26 @@ if ( ! defined( 'ABSPATH' ) ) {
|
|||||||
|
|
||||||
final class Ruady_OG_Head {
|
final class Ruady_OG_Head {
|
||||||
public static function init(): void {
|
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 );
|
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_url_p = parse_url($site_url);
|
||||||
$site_host = $site_url_p['host'];
|
$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 ));
|
$description = self::make_description(wp_strip_all_tags( $excerpt ));
|
||||||
|
|
||||||
// Example business logic:
|
// Example business logic:
|
||||||
@@ -100,7 +120,7 @@ final class Ruady_OG_Head {
|
|||||||
<meta property="og:type" content="website">
|
<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: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: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 -->
|
<!-- Twitter Meta Tags -->
|
||||||
<meta name="twitter:card" content="summary_large_image">
|
<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 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: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: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 ); ?>">
|
<link rel="canonical" href="<?php echo esc_url( $post_url ); ?>">
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
return trim( (string) ob_get_clean() );
|
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();
|
Ruady_OG_Head::init();
|
||||||
|
|||||||
Reference in New Issue
Block a user