PluginProbe ʕ •ᴥ•ʔ
Rank Math SEO – AI SEO Tools to Dominate SEO Rankings / 1.0.271.1
Rank Math SEO – AI SEO Tools to Dominate SEO Rankings v1.0.271.1
1.0.271 1.0.271.1 1.0.270 1.0.269 trunk 1.0.216 1.0.217 1.0.218 1.0.219 1.0.220 1.0.221 1.0.222 1.0.223 1.0.224 1.0.225 1.0.226 1.0.227 1.0.227.1 1.0.228 1.0.229 1.0.230 1.0.231 1.0.232 1.0.233 1.0.234 1.0.234.1 1.0.235 1.0.236 1.0.237 1.0.238 1.0.239 1.0.240 1.0.241 1.0.242 1.0.243 1.0.244 1.0.245 1.0.246 1.0.247 1.0.248 1.0.249 1.0.250 1.0.251 1.0.251.1 1.0.252 1.0.252.1 1.0.253 1.0.254 1.0.255 1.0.256 1.0.257 1.0.258 1.0.259 1.0.259.1 1.0.260 1.0.261 1.0.262 1.0.263 1.0.264 1.0.264.1 1.0.265 1.0.266 1.0.266.1 1.0.267 1.0.268
seo-by-rank-math / includes / class-thumbnail-overlay.php
seo-by-rank-math / includes Last commit date
3rdparty 6 days ago abilities 6 days ago admin 6 days ago cli 5 years ago frontend 2 weeks ago helpers 2 weeks ago metaboxes 2 years ago module 2 weeks ago modules 6 days ago opengraph 2 weeks ago replace-variables 2 weeks ago rest 6 days ago settings 2 weeks ago traits 2 weeks ago updates 2 weeks ago class-auto-updater.php 5 years ago class-cmb2.php 2 weeks ago class-common.php 5 months ago class-compatibility.php 1 year ago class-data-encryption.php 5 months ago class-defaults.php 6 years ago class-frontend-seo-score.php 2 weeks ago class-helper.php 10 months ago class-installer.php 2 weeks ago class-json-manager.php 1 year ago class-kb.php 7 months ago class-metadata.php 2 months ago class-post.php 1 year ago class-rewrite.php 5 months ago class-settings.php 1 year ago class-term.php 1 year ago class-thumbnail-overlay.php 1 year ago class-tracking.php 6 days ago class-update-email.php 2 weeks ago class-updates.php 3 months ago class-user.php 8 months ago index.php 7 years ago interface-runner.php 7 years ago template-tags.php 1 year ago
class-thumbnail-overlay.php
281 lines
1 <?php
2 /**
3 * Thumbnails with overlays.
4 *
5 * @since 1.0.82
6 * @package RankMath
7 * @subpackage RankMath\Core
8 * @author Rank Math <support@rankmath.com>
9 */
10
11 namespace RankMath;
12
13 use RankMath\Traits\Hooker;
14 use RankMath\Helpers\Param;
15 use RankMath\Helpers\Attachment;
16
17 defined( 'ABSPATH' ) || exit;
18
19 /**
20 * Thumbnail_Overlay class.
21 */
22 class Thumbnail_Overlay {
23
24 use Hooker;
25
26 /**
27 * Image module to be used (gd or imagick).
28 *
29 * @var string
30 */
31 private $image_module = '';
32
33 /**
34 * The Constructor.
35 */
36 public function __construct() {
37 $this->image_module = extension_loaded( 'imagick' ) ? 'imagick' : 'gd';
38
39 $this->action( 'wp_ajax_rank_math_overlay_thumb', 'generate_overlay_thumbnail' );
40 $this->action( 'wp_ajax_nopriv_rank_math_overlay_thumb', 'generate_overlay_thumbnail' );
41 }
42
43 /**
44 * AJAX function to generate overlay image. Used in social thumbnails.
45 */
46 public function generate_overlay_thumbnail() {
47 $thumbnail_id = Param::request( 'id', 0, FILTER_VALIDATE_INT );
48 $type = Param::request( 'type', 'play' );
49 $secret = Param::request( 'hash', '' );
50 if ( ! $secret ) {
51 $secret = Param::request( 'secret', '' );
52 }
53
54 $choices = Helper::choices_overlay_images();
55 if ( ! isset( $choices[ $type ] ) ) {
56 die();
57 }
58 $overlay_image = $choices[ $type ]['path'];
59 $image = Attachment::get_scaled_image_path( $thumbnail_id, 'large' );
60
61 if ( ! $this->is_secret_valid( $thumbnail_id, $type, $secret ) ) {
62 die();
63 }
64
65 // If 'large' thumbnail is not found, fall back to full size.
66 if ( empty( $image ) ) {
67 $image = Attachment::get_scaled_image_path( $thumbnail_id, 'full' );
68 }
69
70 $position = $choices[ $type ]['position'];
71 $this->create_overlay_image( $image, $overlay_image, $position );
72
73 die();
74 }
75
76 /**
77 * Calculate margins for a GD resource based on position string.
78 *
79 * @param string $position Position string.
80 * @param resource $image GD image resource identifier.
81 * @param resource $stamp GD image resource identifier.
82 *
83 * @return array
84 */
85 private function get_position_margins_gd( $position, $image, $stamp ) {
86 $margins = [
87 'middle_center' => [],
88 ];
89
90 $margins['middle_center']['top'] = round( abs( imagesy( $image ) - imagesy( $stamp ) ) / 2 );
91 $margins['middle_center']['left'] = round( abs( imagesx( $image ) - imagesx( $stamp ) ) / 2 );
92
93 $default_margins = $margins['middle_center'];
94 $margins = $this->do_filter( 'social/overlay_image_positions', $margins, $image, $stamp, 'gd' );
95
96 if ( ! isset( $margins[ $position ] ) ) {
97 return $default_margins;
98 }
99
100 return $margins[ $position ];
101 }
102
103 /**
104 * Calculate margins for an Imagick object based on position string.
105 *
106 * @param string $position Position string.
107 * @param object $image Imagick object.
108 * @param object $stamp Imagick object.
109 *
110 * @return array
111 */
112 private function get_position_margins_imagick( $position, $image, $stamp ) {
113 $margins = [
114 'middle_center' => [],
115 ];
116
117 $margins['middle_center']['top'] = round( abs( $image->getImageHeight() - $stamp->getImageHeight() ) / 2 );
118 $margins['middle_center']['left'] = round( abs( $image->getImageWidth() - $stamp->getImageWidth() ) / 2 );
119
120 $default_margins = $margins['middle_center'];
121 $margins = $this->do_filter( 'social/overlay_image_positions', $margins, $image, $stamp, 'imagick' );
122
123 if ( ! isset( $margins[ $position ] ) ) {
124 return $default_margins;
125 }
126
127 return $margins[ $position ];
128 }
129
130 /**
131 * Get correct imagecreatef based on image file.
132 *
133 * @param string $image_file Image file.
134 *
135 * @return string New generated image
136 */
137 private function get_imagecreatefrom_method( $image_file ) {
138 $image_format = pathinfo( $image_file, PATHINFO_EXTENSION );
139 if ( ! in_array( $image_format, [ 'jpg', 'jpeg', 'gif', 'png' ], true ) ) {
140 return '';
141 }
142 if ( 'jpg' === $image_format ) {
143 $image_format = 'jpeg';
144 }
145
146 return 'imagecreatefrom' . $image_format;
147 }
148
149 /**
150 * Create Overlay Image.
151 *
152 * @param string $image_file The permalink generated for this post by WordPress.
153 * @param string $overlay_image The ID of the post.
154 * @param string $position Image position.
155 */
156 private function create_overlay_image( $image_file, $overlay_image, $position ) {
157 wp_raise_memory_limit( 'image' );
158
159 /**
160 * Filter: 'rank_math/social/create_overlay_image' - Change the create_overlay_image arguments.
161 */
162 $args = $this->do_filter( 'social/create_overlay_image', compact( 'image_file', 'overlay_image', 'position' ) );
163 extract( $args ); // phpcs:ignore
164
165 if ( empty( $image_file ) || empty( $overlay_image ) ) {
166 return;
167 }
168
169 $method = 'generate_image_' . $this->image_module;
170 $this->$method( $image_file, $overlay_image, $position );
171 die();
172 }
173
174 /**
175 * Generate image using the GD module.
176 *
177 * @param string $image_file The permalink generated for this post by WordPress.
178 * @param string $overlay_image The ID of the post.
179 * @param string $position Image position.
180 */
181 private function generate_image_gd( $image_file, $overlay_image, $position ) {
182 $imagecreatefrom = $this->get_imagecreatefrom_method( $image_file );
183 $overlay_imagecreatefrom = $this->get_imagecreatefrom_method( $overlay_image );
184 if ( ! $imagecreatefrom || ! $overlay_imagecreatefrom ) {
185 return;
186 }
187
188 $stamp = $overlay_imagecreatefrom( $overlay_image );
189 $image = $imagecreatefrom( $image_file );
190
191 if ( ! $image || ! $stamp ) {
192 return;
193 }
194
195 $stamp_width = imagesx( $stamp );
196 $stamp_height = imagesy( $stamp );
197
198 $img_width = imagesx( $image );
199
200 if ( $stamp_width > $img_width ) {
201 $stamp = imagescale( $stamp, $img_width );
202 }
203
204 $margins = $this->get_position_margins_gd( $position, $image, $stamp );
205
206 // Copy the stamp image onto our photo using the margin offsets and the photo width to calculate positioning of the stamp.
207 imagecopy( $image, $stamp, $margins['left'], $margins['top'], 0, 0, $stamp_width, $stamp_height );
208
209 // Output and free memory.
210 header( 'Content-type: image/png' );
211 imagepng( $image );
212 imagedestroy( $image );
213 }
214
215 /**
216 * Generate image using the Imagick module.
217 *
218 * @param string $image_file The permalink generated for this post by WordPress.
219 * @param string $overlay_image The ID of the post.
220 * @param string $position Image position.
221 *
222 * @return void
223 */
224 private function generate_image_imagick( $image_file, $overlay_image, $position ) {
225 try {
226 $stamp = new \Imagick( $overlay_image );
227 $image = new \Imagick( $image_file );
228
229 if ( ! $image->valid() || ! $stamp->valid() || ! $image->getImageFormat() || ! $stamp->getImageFormat() ) {
230 return;
231 }
232
233 // Select the first frame to handle animated images properly.
234 if ( is_callable( [ $stamp, 'setIteratorIndex' ] ) ) {
235 $stamp->setIteratorIndex( 0 );
236 }
237 if ( is_callable( [ $image, 'setIteratorIndex' ] ) ) {
238 $image->setIteratorIndex( 0 );
239 }
240 } catch ( \Exception $e ) {
241 return;
242 }
243
244 $stamp_width = $stamp->getImageWidth();
245 $img_width = $image->getImageWidth();
246
247 if ( $stamp_width > $img_width ) {
248 $stamp->resizeImage( $img_width, 0, \Imagick::FILTER_LANCZOS, 1 );
249 }
250
251 $margins = $this->get_position_margins_imagick( $position, $image, $stamp );
252
253 // Copy the stamp image onto our photo using the margin offsets and the photo width to calculate positioning of the stamp.
254 $image->compositeImage( $stamp, \Imagick::COMPOSITE_OVER, $margins['left'], $margins['top'] );
255
256 // Output.
257 header( 'Content-type: image/png' );
258 echo $image->getImageBlob(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
259
260 // Free memory.
261 $image->clear();
262 $image->destroy();
263
264 $stamp->clear();
265 $stamp->destroy();
266 }
267
268 /**
269 * Check if secret key is valid.
270 *
271 * @param int $id The ID of the attachment.
272 * @param string $type Overlay type.
273 * @param string $secret Secret key.
274 *
275 * @return boolean
276 */
277 private function is_secret_valid( $id, $type, $secret ) {
278 return md5( $id . $type . wp_salt( 'nonce' ) ) === $secret;
279 }
280 }
281