Preview: Image.php
Size: 14.94 KB
/home/certprox/zang.certproxywizard.com/wp/wp-content/plugins/auto-post-thumbnail/src/Models/Image.php
<?php
/**
* Image model.
*
* @package AutoPostThumbnail
*/
namespace AutoPostThumbnail\Models;
use AutoPostThumbnail\Utils\Sanitization;
use GdImage;
// Exit if accessed directly
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Class for image processing
*
* @version 1.0
*/
class Image {
/**
* Image width.
*
* @var integer
*/
public $width;
/**
* Image height.
*
* @var integer
*/
public $height;
/**
* Font file path.
*
* @var string
*/
private $font_path = WAPT_PLUGIN_DIR . '/fonts/arial.ttf';
/**
* Font size.
*
* @var integer
*/
public $font_size;
/**
* Font color.
*
* @var string|array
*/
public $font_color;
/**
* Text to render.
*
* @var string
*/
public $text;
/**
* Background color or image.
*
* @var string
*/
public $background;
/**
* Reference text for font size calculations.
*
* @var string
*/
private $reference_text = 'abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ-!?.,_"[]';
/**
* GD image resource.
*
* @var GdImage|resource|bool
*/
private $image = false;
/**
* Left padding.
*
* @var int
*/
public $padding_left = 0;
/**
* Top padding.
*
* @var int
*/
public $padding_top = 0;
/**
* Line spacing multiplier.
*
* @var float
*/
public $line_spacing = 1.5;
/**
* Image parameters.
*
* @var array
*/
public $params = [];
/**
* Get the GD image resource.
*
* @return GdImage|resource|bool
*/
public function get_image() {
return $this->image;
}
/**
* Set image padding.
*
* @param int $padding_left Left padding.
* @param int $padding_top Top padding.
*/
public function set_padding( $padding_left, $padding_top ) {
$this->padding_left = $padding_left;
$this->padding_top = $padding_top;
}
/**
* Set image width.
*
* @param int $width Image width.
*/
public function set_width( $width ) {
$this->width = $width;
}
/**
* Set image height.
*
* @param int $height Image height.
*/
public function set_height( $height ) {
$this->height = $height;
}
/**
* Set font file path.
*
* @param string $font_path Font file path.
*/
public function set_font_path( $font_path ) {
if ( file_exists( $font_path ) ) {
$this->font_path = $font_path;
}
}
/**
* {PLUGIN_DIR}/fonts/{font}.ttf
*
* @param string $font Font name.
*/
public function set_font( $font ) {
$this->font_path = WAPT_PLUGIN_DIR . "/fonts/{$font}.ttf";
}
/**
* Set font size.
*
* @param int $font_size Font size.
*/
public function set_font_size( $font_size ) {
$this->font_size = $font_size;
}
/**
* Set font color.
*
* @param array|string $font_color Font color.
*/
public function set_font_color( $font_color ) {
$this->font_color = $font_color;
}
/**
* Set text to render.
*
* @param string $text Text to render.
*/
public function set_text( $text ) {
$this->text = $text;
}
/**
* Set background color or image.
*
* @param array|string $background Background color or image.
*/
public function set_background( $background ) {
$this->background = $background;
}
/**
* Constructor.
*
* @param int $width Image width.
* @param int $height Image height.
* @param array|string $background Background color.
* @param string $font Font path.
* @param int $font_size Font size.
* @param string $font_color Font color.
*/
public function __construct( $width, $height, $background = '#ffffff', $font = '', $font_size = 0, $font_color = '#000000' ) {
$this->width = $width;
$this->height = $height;
$this->background = $background;
$this->font_path = $font;
$this->font_size = $font_size;
$this->font_color = $font_color;
$this->image = $this->create( $width, $height, $background );
}
/**
* Create image.
*
* @param int $width Image width.
* @param int $height Image height.
* @param string|int $background Background color or image ID.
*
* @return GdImage|resource|bool
*/
public function create( $width, $height, $background = '#ffffff' ) {
if ( is_numeric( $background ) ) { // image
$image = wp_get_attachment_metadata( $background );
if ( $image ) {
$upload_dir = wp_upload_dir();
$file_path = $upload_dir['basedir'] . '/' . $image['file'];
$file_type = wp_check_filetype( $file_path );
switch ( $file_type['type'] ) {
case 'image/jpeg':
$im = imagecreatefromjpeg( $file_path );
$this->set_width( $image['width'] );
$this->set_height( $image['height'] );
break;
case 'image/png':
$im = imagecreatefrompng( $file_path );
imagesavealpha( $im, true );
$this->set_width( $image['width'] );
$this->set_height( $image['height'] );
break;
default:
$im = $this->create( $width, $height );
break;
}
} else {
$im = $this->create( $width, $height );
}
} elseif ( is_array( $background ) && isset( $background['gradient_start'] ) ) { // gradient
$im = imagecreatetruecolor( $width, $height );
$this->draw_linear_gradient( $im, $background, $width, $height );
} else { // color
$im = imagecreatetruecolor( $width, $height );
$color = $this->color_hex_to_rgb( $background );
$bg_color = imagecolorallocate( $im, $color['r'], $color['g'], $color['b'] );
imagefill( $im, 0, 0, $bg_color );
}
return $im;
}
/**
* Convert hex color to RGB.
*
* @param string $hex Hex color string.
*
* @return array RGB color array.
*/
private function color_hex_to_rgb( $hex = '' ) {
if ( empty( $hex ) ) {
$hex = $this->font_color;
}
[$r, $g, $b] = sscanf( $hex, '#%02x%02x%02x' );
return [
'r' => $r,
'g' => $g,
'b' => $b,
];
}
/**
* Get width and height of a character in the font.
*
* @return array|false
*/
public function get_font_char_size() {
if ( '' !== $this->font_path && 0 !== $this->font_size ) {
$text = ! empty( $this->text ) ? $this->text : $this->reference_text;
$box = imagettfbbox( $this->font_size, 0, $this->font_path, $text );
$width = ceil( ( $box[2] - $box[0] ) / strlen( $text ) );
$height = $box[1] - $box[7];
$result = [
'width' => $width ? $width : 1, // average width of one character
'height' => $height ? $height : 1, // height of one character
];
return $result;
} else {
return false;
}
}
/**
* Write text on the image.
*
* @param string $text The text to write on the image.
* @param string $align The alignment of the text.
* @param string $valign The vertical alignment of the text.
* @param float $line_spacing The spacing between the lines of text.
* @param bool $enable_shadow Whether to enable the shadow.
* @param string $shadow_color The color of the shadow.
*
* @return bool
*/
public function write_text( $text, $align = 'left', $valign = 'top', $line_spacing = 1.5, $enable_shadow = false, $shadow_color = '' ) {
if ( empty( $text ) ) {
return false;
}
$text = Sanitization::sanitize_post_title_for_image_generation( $text );
$font = $this->font_path;
$font_size = $this->font_size;
$font_color = $this->font_color;
$this->set_text( $text );
$char_size = $this->get_font_char_size();
$pad_left = (int) $this->padding_left;
$pad_top = (int) $this->padding_top;
// Convert palette-based images to truecolor to ensure accurate color allocation
// Note: imagepalettetotruecolor modifies the image in place and returns bool
if ( false !== $this->image && function_exists( 'imageistruecolor' ) && function_exists( 'imagepalettetotruecolor' ) && ! imageistruecolor( $this->image ) ) {
$conversion_success = imagepalettetotruecolor( $this->image );
if ( $conversion_success ) {
// Preserve alpha channel support for PNG images
if ( function_exists( 'imagesavealpha' ) ) {
imagesavealpha( $this->image, true );
}
if ( function_exists( 'imagealphablending' ) ) {
imagealphablending( $this->image, true );
}
}
}
$color = $this->color_hex_to_rgb( $font_color );
$font_color = imagecolorallocate( $this->image, $color['r'], $color['g'], $color['b'] );
if ( ! empty( $shadow_color ) ) {
$color = $this->color_hex_to_rgb( $shadow_color );
$shadow_color = imagecolorallocate( $this->image, $color['r'], $color['g'], $color['b'] );
}
$line_spacing = (float) $line_spacing;
$width = $this->width - $pad_left * 2;
$height = $this->height - $pad_top * 2;
$chars_per_line = (int) ceil( $width / $char_size['width'] * 0.9 ); // count of chars per line
$text2 = wordwrap( $text, $chars_per_line, "\n", false );
$text2 = str_replace( '[br]', "\n", $text2 );
$line_count = count( explode( "\n", $text2 ) );
$lines = explode( "\n", $text2 );
for ( $i = 0; $i < $line_count; $i++ ) {
$box = imagettfbbox( $font_size, 0, $font, $this->commas_cut( $lines[ $i ] ) );
$w = $box[4] - $box[6];
if ( $w > $width ) {
--$font_size;
$i = 0;
}
}
$text_height = $line_count * $char_size['height'];
while ( $text_height > $height || ( $height - $text_height <= ( 2 * $pad_left ) ) ) {
--$this->font_size;
--$font_size;
$char_size = $this->get_font_char_size();
if ( ! $char_size ) {
break;
}
$line_width = ceil( $width / $char_size['width'] * 0.9 ); // count of chars per line
$text2 = wordwrap( $text, (int) $line_width, "\n", false );
$text2 = str_replace( '[br]', "\n", $text2 );
$line_count = count( explode( "\n", $text2 ) );
$text_height = $line_count * ( $char_size['height'] * $line_spacing );
}
$width = $this->width;
$height = $this->height;
$lines = explode( "\n", $text2 );
if ( 'bottom' === $valign ) {
$lines = array_reverse( $lines );
}
foreach ( $lines as $key => $line ) {
$box = imagettfbbox( $font_size, 0, $font, $this->commas_cut( $line ) );
$h = $char_size['height'] * count( $lines ) + ( $line_spacing - 1 ) * $char_size['height'] * count( $lines );
$w = $box[4] - $box[6];
$num = $line_spacing * $key;
$x = 0;
$y = 0;
switch ( $align . '-' . $valign ) {
case 'left-top':
$x = $pad_left;
$y = ceil( $pad_top + $char_size['height'] + ( $char_size['height'] * $num ) );
break;
case 'left-center':
$x = $pad_left;
$y = ceil( ( $height / 2 - $h / 2 ) + $char_size['height'] + ( $char_size['height'] * $num ) );
break;
case 'left-bottom':
$x = $pad_left;
$y = ceil( ( $height - $pad_top ) - ( $char_size['height'] * $num ) );
break;
// -------------------------
case 'center-top':
$x = ceil( $width / 2 - $w / 2 );
$y = ceil( $pad_top + $char_size['height'] + ( $char_size['height'] * $num ) );
break;
case 'center-center':
$x = ceil( $width / 2 - $w / 2 );
$y = ceil( ( $height / 2 - $h / 2 ) + $char_size['height'] + ( $char_size['height'] * $num ) );
break;
case 'center-bottom':
$x = ceil( $width / 2 - $w / 2 );
$y = ceil( ( $height - $pad_top ) - ( $char_size['height'] * $num ) );
break;
// -------------------------
case 'right-top':
$x = $width - $w - $pad_left;
$y = ceil( $pad_top + $char_size['height'] + ( $char_size['height'] * $num ) );
break;
case 'right-center':
$x = $width - $w - $pad_left;
$y = ceil( ( $height / 2 - $h / 2 ) + $char_size['height'] + ( $char_size['height'] * $num ) );
break;
case 'right-bottom':
$x = $width - $w - $pad_left;
$y = ceil( ( $height - $pad_top ) - ( $char_size['height'] * $num ) );
break;
}
// shadow
if ( $enable_shadow ) {
imagettftext( $this->image, $font_size, 0, $x + 2, $y + 2, $shadow_color, $font, trim( $line ) );
}
// text
imagettftext( $this->image, $font_size, 0, (int) $x, (int) $y, $font_color, $font, trim( $line ) );
}
return true;
}
/**
* Remove commas from text.
*
* @param string $text The text to process.
*
* @return string
*/
public function commas_cut( $text ) {
return str_replace( ',', '', $text );
}
/**
* Draw a linear gradient on the image.
*
* @param GdImage|resource $im GD image resource.
* @param array $gradient Array with gradient_start, gradient_end, gradient_angle.
* @param int $width Image width.
* @param int $height Image height.
*/
private function draw_linear_gradient( $im, $gradient, $width, $height ) {
$start = $this->color_hex_to_rgb( $gradient['gradient_start'] ?? '#ff6262' );
$end = $this->color_hex_to_rgb( $gradient['gradient_end'] ?? '#6262ff' );
$angle = (int) ( $gradient['gradient_angle'] ?? 180 );
switch ( $angle ) {
case 90: // left to right
for ( $x = 0; $x < $width; $x++ ) {
$ratio = $x / max( $width - 1, 1 );
$r = (int) ( $start['r'] + ( $end['r'] - $start['r'] ) * $ratio );
$g = (int) ( $start['g'] + ( $end['g'] - $start['g'] ) * $ratio );
$b = (int) ( $start['b'] + ( $end['b'] - $start['b'] ) * $ratio );
$color = imagecolorallocate( $im, $r, $g, $b );
imageline( $im, $x, 0, $x, $height - 1, $color );
}
break;
case 180: // top to bottom
for ( $y = 0; $y < $height; $y++ ) {
$ratio = $y / max( $height - 1, 1 );
$r = (int) ( $start['r'] + ( $end['r'] - $start['r'] ) * $ratio );
$g = (int) ( $start['g'] + ( $end['g'] - $start['g'] ) * $ratio );
$b = (int) ( $start['b'] + ( $end['b'] - $start['b'] ) * $ratio );
$color = imagecolorallocate( $im, $r, $g, $b );
imageline( $im, 0, $y, $width - 1, $y, $color );
}
break;
case 135: // top-left to bottom-right
case 225: // bottom-right to top-left
$s = $start;
$e = $end;
if ( 225 === $angle ) {
$s = $end;
$e = $start;
}
$max_dist = $width + $height - 2;
for ( $y = 0; $y < $height; $y++ ) {
for ( $x = 0; $x < $width; $x++ ) {
$ratio = ( $x + $y ) / max( $max_dist, 1 );
$r = (int) ( $s['r'] + ( $e['r'] - $s['r'] ) * $ratio );
$g = (int) ( $s['g'] + ( $e['g'] - $s['g'] ) * $ratio );
$b = (int) ( $s['b'] + ( $e['b'] - $s['b'] ) * $ratio );
$color = imagecolorallocate( $im, $r, $g, $b );
imagesetpixel( $im, $x, $y, $color );
}
}
break;
default: // fallback to top-to-bottom
for ( $y = 0; $y < $height; $y++ ) {
$ratio = $y / max( $height - 1, 1 );
$r = (int) ( $start['r'] + ( $end['r'] - $start['r'] ) * $ratio );
$g = (int) ( $start['g'] + ( $end['g'] - $start['g'] ) * $ratio );
$b = (int) ( $start['b'] + ( $end['b'] - $start['b'] ) * $ratio );
$color = imagecolorallocate( $im, $r, $g, $b );
imageline( $im, 0, $y, $width - 1, $y, $color );
}
break;
}
}
/**
* Save image.
*
* @param string $path File path.
* @param integer $quality Image quality.
* @param string $format Image format.
*/
public function save( $path, $quality = 100, $format = 'jpg' ) {
switch ( strtolower( $format ) ) {
case 'jpg':
case 'jpeg':
imagejpeg( $this->image, $path, $quality );
break;
case 'png':
imagepng( $this->image, $path );
break;
}
}
}
Directory Contents
Dirs: 0 × Files: 3