settings = $settings; $this->font_path = AITM_PLUGIN_DIR . '/fonts/arial.ttf'; } /** * Generate image for post * * @param int $post_id Post ID. * @return int|false Attachment ID or false on failure. */ public function generate_for_post( $post_id ) { $post = get_post( $post_id ); if ( ! $post ) { return false; } // Get settings with defaults $width = absint( $this->settings['title_image_width'] ?? 1200 ); $height = absint( $this->settings['title_image_height'] ?? 630 ); $bg_color = $this->settings['title_image_bg_color'] ?? '#2271b1'; $text_color = $this->settings['title_image_text_color'] ?? '#ffffff'; $font_size = absint( $this->settings['title_image_font_size'] ?? 48 ); // Generate unique filename $upload_dir = wp_upload_dir(); $filename = 'aitm-title-' . $post_id . '-' . time() . '.jpg'; $file_path = $upload_dir['path'] . '/' . $filename; // Create image $result = $this->create_image_with_text( $post->post_title, $file_path, $width, $height, $bg_color, $text_color, $font_size ); if ( ! $result ) { return false; } // Insert into media library return $this->insert_attachment( $file_path, $post_id ); } /** * Create image with text using GD Library * * @param string $text Text to render. * @param string $file_path Path to save image. * @param int $width Image width. * @param int $height Image height. * @param string $bg_color Background color (hex). * @param string $text_color Text color (hex). * @param int $font_size Font size. * @return bool Success status. */ private function create_image_with_text( $text, $file_path, $width, $height, $bg_color, $text_color, $font_size ) { // Check if GD is available if ( ! function_exists( 'imagecreatetruecolor' ) ) { return false; } // Check if font exists if ( ! file_exists( $this->font_path ) ) { return false; } // Create image $image = imagecreatetruecolor( $width, $height ); if ( ! $image ) { return false; } // Set background color $bg_rgb = $this->hex_to_rgb( $bg_color ); $bg = imagecolorallocate( $image, $bg_rgb['r'], $bg_rgb['g'], $bg_rgb['b'] ); imagefill( $image, 0, 0, $bg ); // Set text color $text_rgb = $this->hex_to_rgb( $text_color ); $text_alloc = imagecolorallocate( $image, $text_rgb['r'], $text_rgb['g'], $text_rgb['b'] ); // Prepare text (convert encoding) $text = mb_convert_encoding( $text, 'UTF-8' ); // Calculate padding $padding_x = (int) ( $width * 0.05 ); // 5% padding $padding_y = (int) ( $height * 0.05 ); // 5% padding $max_width = $width - ( $padding_x * 2 ); // Wrap text to fit width $lines = $this->wrap_text( $text, $font_size, $max_width ); // Calculate line height $line_height = (int) ( $font_size * 1.3 ); // Calculate total text height $total_height = count( $lines ) * $line_height; // Adjust font size if text is too tall while ( $total_height > ( $height - ( $padding_y * 2 ) ) && $font_size > 20 ) { $font_size --; $line_height = (int) ( $font_size * 1.3 ); $lines = $this->wrap_text( $text, $font_size, $max_width ); $total_height = count( $lines ) * $line_height; } // Calculate starting Y position (center vertically) $y = (int) ( ( $height - $total_height ) / 2 + $font_size ); // Draw each line centered foreach ( $lines as $line ) { $line = trim( $line ); // Get text bounding box $bbox = imagettfbbox( $font_size, 0, $this->font_path, $line ); $text_width = $bbox[2] - $bbox[0]; // Center horizontally $x = (int) ( ( $width - $text_width ) / 2 ); // Draw text imagettftext( $image, $font_size, 0, $x, $y, $text_alloc, $this->font_path, $line ); // Move to next line $y += $line_height; } // Save image as JPEG $result = imagejpeg( $image, $file_path, 90 ); // Free memory imagedestroy( $image ); return $result; } /** * Wrap text to multiple lines * * @param string $text Text to wrap. * @param int $font_size Font size. * @param int $max_width Maximum width in pixels. * @return array Array of text lines. */ private function wrap_text( $text, $font_size, $max_width ) { $words = explode( ' ', $text ); $lines = array(); $line = ''; foreach ( $words as $word ) { $test_line = empty( $line ) ? $word : $line . ' ' . $word; $bbox = imagettfbbox( $font_size, 0, $this->font_path, $test_line ); $width = $bbox[2] - $bbox[0]; if ( $width > $max_width && ! empty( $line ) ) { // Line is too long, save current line and start new one $lines[] = $line; $line = $word; } else { $line = $test_line; } } // Add last line if ( ! empty( $line ) ) { $lines[] = $line; } return $lines; } /** * Convert hex color to RGB * * @param string $hex Hex color code. * @return array RGB values. */ private function hex_to_rgb( $hex ) { $hex = ltrim( $hex, '#' ); if ( strlen( $hex ) === 3 ) { $hex = $hex[0] . $hex[0] . $hex[1] . $hex[1] . $hex[2] . $hex[2]; } list( $r, $g, $b ) = sscanf( $hex, '%02x%02x%02x' ); return array( 'r' => $r, 'g' => $g, 'b' => $b, ); } /** * Insert image into media library * * @param string $file_path Path to image file. * @param int $post_id Post ID to attach to. * @return int|false Attachment ID or false on failure. */ private function insert_attachment( $file_path, $post_id ) { // Check if file exists if ( ! file_exists( $file_path ) ) { return false; } // Get file type $file_type = wp_check_filetype( basename( $file_path ), null ); // Prepare attachment data $attachment = array( 'guid' => wp_upload_dir()['url'] . '/' . basename( $file_path ), 'post_mime_type' => $file_type['type'], 'post_title' => get_the_title( $post_id ), 'post_content' => '', 'post_status' => 'inherit', ); // Insert attachment $attachment_id = wp_insert_attachment( $attachment, $file_path, $post_id ); if ( is_wp_error( $attachment_id ) ) { return false; } // Generate attachment metadata require_once ABSPATH . 'wp-admin/includes/image.php'; $attach_data = wp_generate_attachment_metadata( $attachment_id, $file_path ); wp_update_attachment_metadata( $attachment_id, $attach_data ); return $attachment_id; } }