PluginProbe
Sharing Image / 2.0.10
Sharing Image v2.0.10
3.10 trunk 2.0 2.0.0 2.0.1 2.0.10 2.0.11 2.0.12 2.0.13 2.0.14 2.0.15 2.0.16 2.0.17 2.0.2 2.0.3 2.0.4 2.0.5 2.0.6 2.0.7 2.0.8 2.0.9 3.0 3.1 3.2 3.3 All 29 releases
sharing-image / classes / class-generator.php

class-generator.php in Sharing Image 2.0.10, at classes/class-generator.php

579 lines 13.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Poster generator class
4 *
5 * @package sharing-image
6 * @author Anton Lukin
7 */
8
9 namespace Sharing_Image;
10
11 use Exception;
12 use WP_Error;
13 use PosterEditor\PosterEditor;
14
15 if ( ! defined( 'ABSPATH' ) ) {
16 die;
17 }
18
19 /**
20 * Poster generator class
21 *
22 * @class Generator
23 */
24 class Generator {
25 /**
26 * The instance of Settings class.
27 *
28 * @var instance
29 */
30 private $settings;
31
32 /**
33 * Generator constructor.
34 */
35 public function __construct() {
36 $this->settings = new Settings();
37 }
38
39 /**
40 * Autogenerate new poster using post data for preset fields.
41 *
42 * @param integer $id Template id using for poster generator.
43 * @param integer $post_id Post id.
44 *
45 * @return array|false New poster url or false on failure.
46 */
47 public function autogenerate( $id, $post_id ) {
48 // Get templates list from settings.
49 $templates = $this->settings->get_templates();
50
51 if ( ! isset( $templates[ $id ] ) ) {
52 return false;
53 }
54
55 $template = $this->generate_template( $templates[ $id ], $post_id );
56
57 if ( ! $this->check_required( $template ) ) {
58 return false;
59 }
60
61 list( $path, $url ) = $this->get_upload_file();
62
63 // Generate image and save it.
64 $poster = $this->create_poster( $template, $path );
65
66 if ( is_wp_error( $poster ) ) {
67 return false;
68 }
69
70 $source = array(
71 'poster' => $url,
72 'width' => $template['width'],
73 'height' => $template['height'],
74 );
75
76 return $source;
77 }
78
79 /**
80 * Compose image using picker data.
81 *
82 * @param array $picker Picker data from metabox.
83 *
84 * @return array|WP_Error Poster url, width and height or WP_Error on failure.
85 */
86 public function compose( $picker ) {
87 if ( ! isset( $picker['template'] ) ) {
88 return new WP_Error( 'validate', esc_html__( 'Template id cannot be empty', 'sharing-image' ) );
89 }
90
91 $id = absint( $picker['template'] );
92
93 // Get templates list from settings.
94 $templates = $this->settings->get_templates();
95
96 if ( ! isset( $templates[ $id ] ) ) {
97 return new WP_Error( 'validate', esc_html__( 'Wrong template id', 'sharing-image' ) );
98 }
99
100 $fieldset = array();
101
102 if ( isset( $picker['fieldset'][ $id ] ) ) {
103 $fieldset = $picker['fieldset'][ $id ];
104 }
105
106 $template = $this->prepare_template( $templates[ $id ], $fieldset );
107
108 if ( ! $this->check_required( $template ) ) {
109 return new WP_Error( 'generate', esc_html__( 'Wrong template settings', 'sharing-image' ) );
110 }
111
112 list( $path, $url ) = $this->get_upload_file();
113
114 // Generate image and save it to given path.
115 $poster = $this->create_poster( $template, $path );
116
117 if ( is_wp_error( $poster ) ) {
118 return $poster;
119 }
120
121 $source = array(
122 'poster' => $url,
123 'width' => $template['width'],
124 'height' => $template['height'],
125 );
126
127 return $source;
128 }
129
130 /**
131 * Show image for settings page using template data.
132 *
133 * @param array $template Templates data from settings page.
134 * @param int $index Template index.
135 *
136 * @return void|WP_Error WP_Error on failure.
137 */
138 public function show( $template, $index ) {
139 $template = $this->prepare_template( $template, null, $index );
140
141 if ( ! $this->check_required( $template ) ) {
142 return new WP_Error( 'generate', esc_html__( 'Wrong template settings', 'sharing-image' ) );
143 }
144
145 // Generate image and show it immediately.
146 $poster = $this->create_poster( $template );
147
148 if ( is_wp_error( $poster ) ) {
149 return $poster;
150 }
151
152 exit; // It's ok to exit here. Just cause we show an image above.
153 }
154
155 /**
156 * Save image for settings page using template data.
157 *
158 * @param array $template Templates data from settings page.
159 * @param int $index Template index.
160 *
161 * @return string|WP_Error Generated poster url or WP_Error on failure.
162 */
163 public function save( $template, $index ) {
164 $template = $this->prepare_template( $template, null, $index );
165
166 if ( ! $this->check_required( $template ) ) {
167 return new WP_Error( 'generate', esc_html__( 'Wrong template settings', 'sharing-image' ) );
168 }
169
170 list( $path, $url ) = $this->get_upload_file();
171
172 // Generate image and save it.
173 $poster = $this->create_poster( $template, $path );
174
175 if ( is_wp_error( $poster ) ) {
176 return $poster;
177 }
178
179 return $url;
180 }
181
182 /**
183 * Update template with post data for preset fields.
184 *
185 * @param array $template Templates data from settings page.
186 * @param int $post_id Post id.
187 *
188 * @return array List of template data.
189 */
190 private function generate_template( $template, $post_id ) {
191 $layers = array();
192
193 if ( isset( $template['layers'] ) ) {
194 $layers = $template['layers'];
195 }
196
197 $fieldset = array();
198
199 foreach ( $layers as $i => $layer ) {
200 if ( empty( $layer['type'] ) || 'text' !== $layer['type'] ) {
201 continue;
202 }
203
204 if ( empty( $layer['dynamic'] ) || empty( $layer['preset'] ) ) {
205 continue;
206 }
207
208 if ( 'title' === $layer['preset'] ) {
209 $fieldset['captions'][ $i ] = get_the_title( $post_id );
210 }
211
212 if ( 'excerpt' === $layer['preset'] ) {
213 $fieldset['captions'][ $i ] = get_the_excerpt( $post_id );
214 }
215 }
216
217 $thumbnail_id = get_post_thumbnail_id( $post_id );
218
219 if ( ! empty( $thumbnail_id ) ) {
220 $fieldset['attachment'] = $thumbnail_id;
221 }
222
223 $template = $this->prepare_template( $template, $fieldset );
224
225 return $template;
226 }
227
228 /**
229 * Prepare template before creating poster.
230 * Used to fill fieldset texts and background image.
231 *
232 * @param array $template List of template data.
233 * @param array $fieldset Optional. Fieldset data from picker.
234 * @param integer $index Optional. Template index from editor.
235 *
236 * @return array List of template data.
237 */
238 private function prepare_template( $template, $fieldset = array(), $index = null ) {
239 $layers = array();
240
241 if ( isset( $template['layers'] ) ) {
242 $layers = $template['layers'];
243 }
244
245 foreach ( $layers as $i => &$layer ) {
246 if ( empty( $layer['type'] ) || 'text' !== $layer['type'] ) {
247 continue;
248 }
249
250 if ( empty( $layer['dynamic'] ) ) {
251 continue;
252 }
253
254 $layer['content'] = null;
255
256 if ( isset( $layer['sample'] ) ) {
257 $layer['content'] = $layer['sample'];
258 }
259
260 if ( isset( $fieldset['captions'][ $i ] ) ) {
261 $layer['content'] = $fieldset['captions'][ $i ];
262 }
263 }
264
265 $template['layers'] = $layers;
266
267 if ( 'dynamic' === $template['background'] ) {
268 if ( null !== $index ) {
269 $template['image'] = sprintf( SHARING_IMAGE_DIR . 'assets/images/%d.jpg', ( $index % 12 ) + 1 );
270 }
271
272 if ( ! empty( $fieldset['attachment'] ) ) {
273 $template['image'] = get_attached_file( $fieldset['attachment'] );
274 }
275 }
276
277 if ( 'permanent' === $template['background'] ) {
278 if ( ! empty( $template['attachment'] ) ) {
279 $template['image'] = get_attached_file( $template['attachment'] );
280 }
281 }
282
283 /**
284 * Filters template before generation.
285 *
286 * @param array $template List of template data.
287 * @param array $fieldset Fieldset data from picker.
288 * @param integer $index Template index from editor.
289 */
290 return apply_filters( 'sharing_image_prepare_template', $template, $fieldset, $index );
291 }
292
293 /**
294 * Create poster using template data.
295 *
296 * @param array $template List of template options.
297 * @param string $path Optional. File path to save.
298 *
299 * @return void|WP_Error WP_Error on failure.
300 */
301 private function create_poster( $template, $path = null ) {
302 try {
303 $poster = new PosterEditor();
304
305 // Set color canvas options.
306 $options = array( 'color' => $template['fill'] );
307
308 // Create empty poster.
309 $poster->canvas( $template['width'], $template['height'], $options );
310
311 // Recreate poster with image if exists.
312 if ( ! empty( $template['image'] ) ) {
313 $poster->make( $template['image'] )->fit( $template['width'], $template['height'] );
314 }
315
316 if ( ! empty( $template['layers'] ) ) {
317 $poster = $this->append_layers( $poster, $template['layers'] );
318 }
319
320 $settings = $this->settings;
321
322 if ( null === $path ) {
323 return $poster->show( $settings->get_file_format(), $settings->get_quality() );
324 }
325
326 $poster->save( $path, $settings->get_quality() );
327
328 } catch ( Exception $e ) {
329 return new WP_Error( 'generate', $e->getMessage() );
330 }
331 }
332
333 /**
334 * Append layers to image.
335 *
336 * @param PosterEditor $poster Instance of PosterEditor class.
337 * @param array $layers List of layers options.
338 *
339 * @return PosterEditor PosterEditor instance.
340 */
341 private function append_layers( $poster, $layers ) {
342 $layers = array_reverse( $layers );
343
344 foreach ( $layers as $layer ) {
345 if ( empty( $layer['type'] ) ) {
346 continue;
347 }
348
349 switch ( $layer['type'] ) {
350 case 'filter':
351 $poster = $this->draw_filter( $poster, $layer );
352 break;
353
354 case 'rectangle':
355 $poster = $this->draw_rectangle( $poster, $layer );
356 break;
357
358 case 'text':
359 $poster = $this->draw_text( $poster, $layer );
360 break;
361
362 case 'image':
363 $poster = $this->draw_image( $poster, $layer );
364 break;
365 }
366 }
367
368 return $poster;
369 }
370
371 /**
372 * Draw filter layer
373 *
374 * @param PosterEditor $poster Instance of PosterEditor class.
375 * @param array $layer Filter layer options.
376 *
377 * @return PosterEditor PosterEditor instance.
378 */
379 private function draw_filter( $poster, $layer ) {
380 if ( ! empty( $layer['grayscale'] ) ) {
381 $poster->grayscale();
382 }
383
384 if ( ! empty( $layer['blur'] ) ) {
385 $poster->blur();
386 }
387
388 if ( isset( $layer['contrast'] ) ) {
389 $poster->contrast( $layer['contrast'] );
390 }
391
392 if ( isset( $layer['brightness'] ) ) {
393 $poster->brightness( $layer['brightness'] );
394 }
395
396 if ( isset( $layer['blackout'] ) ) {
397 $poster->blackout( $layer['blackout'] );
398 }
399
400 return $poster;
401 }
402
403 /**
404 * Draw rectangle layer.
405 *
406 * @param PosterEditor $poster Instance of PosterEditor class.
407 * @param array $layer Rectangle layer options.
408 *
409 * @return PosterEditor PosterEditor instance.
410 */
411 private function draw_rectangle( $poster, $layer ) {
412 // Both x and y should be set.
413 if ( ! isset( $layer['x'], $layer['y'] ) ) {
414 return $poster;
415 }
416
417 $args = $this->prepare_args( $layer, array( 'color', 'opacity', 'thickness' ) );
418
419 if ( ! empty( $layer['outline'] ) ) {
420 $args['outline'] = true;
421 }
422
423 if ( ! isset( $layer['width'] ) ) {
424 $layer['width'] = 0;
425 }
426
427 if ( ! isset( $layer['height'] ) ) {
428 $layer['height'] = 0;
429 }
430
431 // Draw rectangle.
432 $poster->rectangle( $layer['x'], $layer['y'], $layer['width'], $layer['height'], $args );
433
434 return $poster;
435 }
436
437 /**
438 * Draw image layer
439 *
440 * @param PosterEditor $poster Instance of PosterEditor class.
441 * @param object $layer Option name.
442 *
443 * @return PosterEditor PosterEditor instance.
444 */
445 private function draw_image( $poster, $layer ) {
446 // Attachment id is required.
447 if ( ! isset( $layer['attachment'] ) ) {
448 return $poster;
449 }
450
451 $image = new PosterEditor();
452
453 // Prepare common layer args.
454 $args = $this->prepare_args( $layer, array( 'x', 'y' ) );
455
456 // Create new editor instance by attachment id.
457 $attachment = $image->make( get_attached_file( $layer['attachment'] ) );
458
459 if ( isset( $layer['width'], $layer['height'] ) ) {
460 return $poster->insert( $attachment->resize( $layer['width'], $layer['height'] ), $args );
461 }
462
463 if ( ! isset( $layer['width'] ) ) {
464 $layer['width'] = null;
465 }
466
467 if ( ! isset( $layer['height'] ) ) {
468 $layer['height'] = null;
469 }
470
471 return $poster->insert( $attachment->downsize( $layer['width'], $layer['height'] ), $args );
472 }
473
474 /**
475 * Draw text layer.
476 *
477 * @param PosterEditor $poster Instance of PosterEditor class.
478 * @param array $layer Rectangle layer options.
479 *
480 * @return PosterEditor PosterEditor instance.
481 */
482 private function draw_text( $poster, $layer ) {
483 $args = $this->prepare_args( $layer, array( 'x', 'y', 'width', 'height', 'fontsize', 'color', 'lineheight', 'opacity', 'horizontal', 'vertical' ) );
484
485 // Try to set font file by name or attachment path.
486 $args['fontpath'] = $this->get_fontpath( $layer );
487
488 if ( ! empty( $layer['content'] ) ) {
489 $poster->text( $layer['content'], $args );
490 }
491
492 return $poster;
493 }
494
495 /**
496 * Get font file path by layer data.
497 *
498 * @param array $layer Layer data.
499 * @param string $path Default file path.
500 *
501 * @return string Filtered path to font file.
502 */
503 private function get_fontpath( $layer, $path = '' ) {
504 if ( isset( $layer['fontname'] ) ) {
505 $path = sprintf( SHARING_IMAGE_DIR . 'assets/fonts/%s.ttf', $layer['fontname'] );
506 }
507
508 if ( isset( $layer['fontfile'] ) ) {
509 $path = get_attached_file( $layer['fontfile'] );
510 }
511
512 /**
513 * Filters generator font file path.
514 *
515 * @param string $path Font file path.
516 * @param array $layer Layer data.
517 */
518 return apply_filters( 'sharing_image_get_fontpath', $path, $layer );
519 }
520
521 /**
522 * Generate upload file path and url.
523 *
524 * @return array Server file path and url to uploaded image.
525 */
526 private function get_upload_file() {
527 // Get uploads directory url and path array.
528 list( $path, $url ) = $this->settings->get_upload_dir();
529
530 // Create random file name with proper extension.
531 $name = wp_unique_filename( $path, uniqid() . '.' . $this->settings->get_file_format() );
532
533 $file = array(
534 trailingslashit( $path ) . $name,
535 trailingslashit( $url ) . $name,
536 );
537
538 /**
539 * Filters upload file path and url
540 *
541 * @param array $file Server file path and url to image.
542 * @param string $name Unique file name.
543 */
544 return apply_filters( 'sharing_image_get_upload_file', $file, $name );
545 }
546
547 /**
548 * Prepare args and remove not-allowed keys.
549 *
550 * @param array $args List of source arguments.
551 * @param array $allowed List of allowed keys.
552 *
553 * @return array List of prepared args.
554 */
555 private function prepare_args( $args, $allowed ) {
556 return wp_array_slice_assoc( $args, $allowed );
557 }
558
559 /**
560 * Check requred template values.
561 *
562 * @param array $template List of template settings.
563 *
564 * @return bool Whether all required values isset.
565 */
566 private function check_required( $template ) {
567 $required = array( 'width', 'height', 'fill' );
568
569 // Count intersected values.
570 $prepared = count( $this->prepare_args( $template, $required ) );
571
572 if ( count( $required ) === $prepared ) {
573 return true;
574 }
575
576 return false;
577 }
578 }
579