PluginProbe
Sharing Image / 2.0
Sharing Image v2.0
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, at classes/class-generator.php

481 lines 11.5 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 * Compose image using picker data.
41 *
42 * @param array $picker Picker data from metabox.
43 *
44 * @return array|WP_Error Poster url, width and height or WP_Error on failure.
45 */
46 public function compose( $picker ) {
47 if ( ! isset( $picker['template'] ) ) {
48 return new WP_Error( 'validate', esc_html__( 'Template id cannot be empty', 'sharing-image' ) );
49 }
50
51 $id = absint( $picker['template'] );
52
53 // Get templates list from settings.
54 $templates = $this->settings->get_templates();
55
56 if ( ! isset( $templates[ $id ] ) ) {
57 return new WP_Error( 'validate', esc_html__( 'Wrong template id', 'sharing-image' ) );
58 }
59
60 $fieldset = array();
61
62 if ( isset( $picker['fieldset'][ $id ] ) ) {
63 $fieldset = $picker['fieldset'][ $id ];
64 }
65
66 $template = $this->prepare_template( $templates[ $id ], $fieldset );
67
68 if ( ! $this->check_required( $template ) ) {
69 return new WP_Error( 'generate', esc_html__( 'Wrong template settings', 'sharing-image' ) );
70 }
71
72 list( $path, $url ) = $this->get_upload_file();
73
74 // Generate image and save it to given path.
75 $poster = $this->create_poster( $template, $path );
76
77 if ( is_wp_error( $poster ) ) {
78 return $poster;
79 }
80
81 $result = array(
82 'poster' => $url,
83 'width' => $template['width'],
84 'height' => $template['height'],
85 );
86
87 return $result;
88 }
89
90 /**
91 * Show image for settings page using template data.
92 *
93 * @param array $template Templates data from settings page.
94 * @param int $index Template index.
95 *
96 * @return void|WP_Error WP_Error on failure.
97 */
98 public function show( $template, $index ) {
99 $template = $this->prepare_template( $template, null, $index );
100
101 if ( ! $this->check_required( $template ) ) {
102 return new WP_Error( 'generate', esc_html__( 'Wrong template settings', 'sharing-image' ) );
103 }
104
105 // Generate image and show it immediately.
106 $poster = $this->create_poster( $template );
107
108 if ( is_wp_error( $poster ) ) {
109 return $poster;
110 }
111
112 exit; // It's ok to exit here. Just cause we show an image above.
113 }
114
115 /**
116 * Save image for settings page using template data.
117 *
118 * @param array $template Templates data from settings page.
119 * @param int $index Template index.
120 *
121 * @return string|WP_Error Generated poster url or WP_Error on failure.
122 */
123 public function save( $template, $index ) {
124 $template = $this->prepare_template( $template, null, $index );
125
126 if ( ! $this->check_required( $template ) ) {
127 return new WP_Error( 'generate', esc_html__( 'Wrong template settings', 'sharing-image' ) );
128 }
129
130 list( $path, $url ) = $this->get_upload_file();
131
132 // Generate image and save it.
133 $poster = $this->create_poster( $template, $path );
134
135 if ( is_wp_error( $poster ) ) {
136 return $poster;
137 }
138
139 return $url;
140 }
141
142 /**
143 * Prepare template before creating poster.
144 * Used to fill fieldset texts and background image.
145 *
146 * @param array $template List of template data.
147 * @param array $fieldset Optional. Fieldset data from picker.
148 * @param integer $index Optional. Template index from editor.
149 *
150 * @return array List of template data.
151 */
152 private function prepare_template( $template, $fieldset = array(), $index = null ) {
153 $layers = array();
154
155 if ( isset( $template['layers'] ) ) {
156 $layers = $template['layers'];
157 }
158
159 foreach ( $layers as $i => &$layer ) {
160 if ( empty( $layer['type'] ) || 'text' !== $layer['type'] ) {
161 continue;
162 }
163
164 if ( empty( $layer['dynamic'] ) ) {
165 continue;
166 }
167
168 $layer['content'] = null;
169
170 if ( isset( $layer['sample'] ) ) {
171 $layer['content'] = $layer['sample'];
172 }
173
174 if ( isset( $fieldset['captions'][ $i ] ) ) {
175 $layer['content'] = $fieldset['captions'][ $i ];
176 }
177 }
178
179 $template['layers'] = $layers;
180
181 if ( 'dynamic' === $template['background'] ) {
182 if ( null !== $index ) {
183 $template['image'] = sprintf( SHARING_IMAGE_DIR . 'assets/images/%d.jpg', ( $index % 12 ) + 1 );
184 }
185
186 if ( ! empty( $fieldset['attachment'] ) ) {
187 $template['image'] = get_attached_file( $fieldset['attachment'] );
188 }
189 }
190
191 if ( 'permanent' === $template['background'] ) {
192 if ( ! empty( $template['attachment'] ) ) {
193 $template['image'] = get_attached_file( $template['attachment'] );
194 }
195 }
196
197 /**
198 * Filters template before generation.
199 *
200 * @param array $template List of template data.
201 * @param array $fieldset Fieldset data from picker.
202 * @param integer $index Template index from editor.
203 */
204 return apply_filters( 'sharing_image_prepare_template', $template, $fieldset, $index );
205 }
206
207 /**
208 * Create poster using template data.
209 *
210 * @param array $template List of template options.
211 * @param string $path Optional. File path to save.
212 *
213 * @return void|WP_Error WP_Error on failure.
214 */
215 private function create_poster( $template, $path = null ) {
216 try {
217 $poster = new PosterEditor();
218
219 // Set color canvas options.
220 $options = array( 'color' => $template['fill'] );
221
222 // Create empty poster.
223 $poster->canvas( $template['width'], $template['height'], $options );
224
225 // Recreate poster with image if exists.
226 if ( ! empty( $template['image'] ) ) {
227 $poster->make( $template['image'] )->fit( $template['width'], $template['height'] );
228 }
229
230 if ( ! empty( $template['layers'] ) ) {
231 $poster = $this->append_layers( $poster, $template['layers'] );
232 }
233
234 $settings = $this->settings;
235
236 if ( null === $path ) {
237 return $poster->show( $settings->get_file_format(), $settings->get_quality() );
238 }
239
240 $poster->save( $path, $settings->get_quality() );
241
242 } catch ( Exception $e ) {
243 return new WP_Error( 'generate', $e->getMessage() );
244 }
245 }
246
247 /**
248 * Append layers to image.
249 *
250 * @param PosterEditor $poster Instance of PosterEditor class.
251 * @param array $layers List of layers options.
252 *
253 * @return PosterEditor PosterEditor instance.
254 */
255 private function append_layers( $poster, $layers ) {
256 $layers = array_reverse( $layers );
257
258 foreach ( $layers as $layer ) {
259 if ( empty( $layer['type'] ) ) {
260 continue;
261 }
262
263 switch ( $layer['type'] ) {
264 case 'filter':
265 $poster = $this->draw_filter( $poster, $layer );
266 break;
267
268 case 'rectangle':
269 $poster = $this->draw_rectangle( $poster, $layer );
270 break;
271
272 case 'text':
273 $poster = $this->draw_text( $poster, $layer );
274 break;
275
276 case 'image':
277 $poster = $this->draw_image( $poster, $layer );
278 break;
279 }
280 }
281
282 return $poster;
283 }
284
285 /**
286 * Draw filter layer
287 *
288 * @param PosterEditor $poster Instance of PosterEditor class.
289 * @param array $layer Filter layer options.
290 *
291 * @return PosterEditor PosterEditor instance.
292 */
293 private function draw_filter( $poster, $layer ) {
294 if ( ! empty( $layer['grayscale'] ) ) {
295 $poster->grayscale();
296 }
297
298 if ( ! empty( $layer['blur'] ) ) {
299 $poster->blur();
300 }
301
302 if ( isset( $layer['contrast'] ) ) {
303 $poster->contrast( $layer['contrast'] );
304 }
305
306 if ( isset( $layer['brightness'] ) ) {
307 $poster->brightness( $layer['brightness'] );
308 }
309
310 if ( isset( $layer['blackout'] ) ) {
311 $poster->blackout( $layer['blackout'] );
312 }
313
314 return $poster;
315 }
316
317 /**
318 * Draw rectangle layer.
319 *
320 * @param PosterEditor $poster Instance of PosterEditor class.
321 * @param array $layer Rectangle layer options.
322 *
323 * @return PosterEditor PosterEditor instance.
324 */
325 private function draw_rectangle( $poster, $layer ) {
326 // Both x and y should be set.
327 if ( ! isset( $layer['x'], $layer['y'] ) ) {
328 return $poster;
329 }
330
331 $args = $this->prepare_args( $layer, array( 'color', 'opacity', 'thickness' ) );
332
333 if ( ! empty( $layer['outline'] ) ) {
334 $args['outline'] = true;
335 }
336
337 if ( ! isset( $layer['width'] ) ) {
338 $layer['width'] = 0;
339 }
340
341 if ( ! isset( $layer['height'] ) ) {
342 $layer['height'] = 0;
343 }
344
345 // Draw rectangle.
346 $poster->rectangle( $layer['x'], $layer['y'], $layer['width'], $layer['height'], $args );
347
348 return $poster;
349 }
350
351 /**
352 * Draw image layer
353 *
354 * @param PosterEditor $poster Instance of PosterEditor class.
355 * @param object $layer Option name.
356 *
357 * @return PosterEditor PosterEditor instance.
358 */
359 private function draw_image( $poster, $layer ) {
360 // Attachment id is required.
361 if ( ! isset( $layer['attachment'] ) ) {
362 return $poster;
363 }
364
365 $args = $this->prepare_args( $layer, array( 'x', 'y', 'width', 'height' ) );
366
367 // Get attachment file by id.
368 $image = get_attached_file( $layer['attachment'] );
369
370 // Insert image to poster.
371 $poster->insert( $image, $args );
372
373 return $poster;
374 }
375
376 /**
377 * Draw text layer.
378 *
379 * @param PosterEditor $poster Instance of PosterEditor class.
380 * @param array $layer Rectangle layer options.
381 *
382 * @return PosterEditor PosterEditor instance.
383 */
384 private function draw_text( $poster, $layer ) {
385 $args = $this->prepare_args( $layer, array( 'x', 'y', 'width', 'height', 'fontsize', 'color', 'lineheight', 'opacity', 'horizontal', 'vertical' ) );
386
387 // Try to set font file by name or attachment path.
388 $args['fontpath'] = $this->get_fontpath( $layer );
389
390 if ( ! empty( $layer['content'] ) ) {
391 $poster->text( $layer['content'], $args );
392 }
393
394 return $poster;
395 }
396
397 /**
398 * Get font file path by layer data.
399 *
400 * @param array $layer Layer data.
401 * @param string $path Default file path.
402 *
403 * @return string Filtered path to font file.
404 */
405 private function get_fontpath( $layer, $path = '' ) {
406 if ( isset( $layer['fontname'] ) ) {
407 $path = sprintf( SHARING_IMAGE_DIR . 'assets/fonts/%s.ttf', $layer['fontname'] );
408 }
409
410 if ( isset( $layer['fontfile'] ) ) {
411 $path = get_attached_file( $layer['fontfile'] );
412 }
413
414 /**
415 * Filters generator font file path.
416 *
417 * @param string $path Font file path.
418 * @param array $layer Layer data.
419 */
420 return apply_filters( 'sharing_image_get_fontpath', $path, $layer );
421 }
422
423 /**
424 * Generate upload file path and url.
425 *
426 * @return array Server file path and url to uploaded image.
427 */
428 private function get_upload_file() {
429 // Get uploads directory url and path array.
430 list( $path, $url ) = $this->settings->get_upload_dir();
431
432 // Create random file name with proper extension.
433 $name = wp_unique_filename( $path, uniqid() . '.' . $this->settings->get_file_format() );
434
435 $file = array(
436 trailingslashit( $path ) . $name,
437 trailingslashit( $url ) . $name,
438 );
439
440 /**
441 * Filters upload file path and url
442 *
443 * @param array $file Server file path and url to image.
444 * @param string $name Unique file name.
445 */
446 return apply_filters( 'sharing_image_get_upload_file', $file, $name );
447 }
448
449 /**
450 * Prepare args and remove not-allowed keys.
451 *
452 * @param array $args List of source arguments.
453 * @param array $allowed List of allowed keys.
454 *
455 * @return array List of prepared args.
456 */
457 private function prepare_args( $args, $allowed ) {
458 return wp_array_slice_assoc( $args, $allowed );
459 }
460
461 /**
462 * Check requred template values.
463 *
464 * @param array $template List of template settings.
465 *
466 * @return bool Whether all required values isset.
467 */
468 private function check_required( $template ) {
469 $required = array( 'width', 'height', 'fill' );
470
471 // Count intersected values.
472 $prepared = count( $this->prepare_args( $template, $required ) );
473
474 if ( count( $required ) === $prepared ) {
475 return true;
476 }
477
478 return false;
479 }
480 }
481