View source
<?php
namespace Drupal\image_effects\Plugin\ImageToolkit\Operation\gd;
use Drupal\system\Plugin\ImageToolkit\Operation\gd\GDImageToolkitOperationBase;
use Drupal\image_effects\Component\ColorUtility;
use Drupal\image_effects\Component\PositionedRectangle;
use Drupal\image_effects\Component\TextUtility;
use Drupal\image_effects\Plugin\ImageToolkit\Operation\FontOperationTrait;
use Drupal\image_effects\Plugin\ImageToolkit\Operation\TextToWrapperTrait;
class TextToWrapper extends GDImageToolkitOperationBase {
use FontOperationTrait;
use GDOperationTrait;
use TextToWrapperTrait;
protected function execute(array $arguments) {
$outline = $shadow = FALSE;
if ($arguments['font_stroke_mode'] == 'outline' && ($arguments['font_outline_top'] || $arguments['font_outline_right'] || $arguments['font_outline_bottom'] || $arguments['font_outline_left']) && $arguments['font_stroke_color']) {
$outline = TRUE;
}
elseif ($arguments['font_stroke_mode'] == 'shadow' && ($arguments['font_shadow_x_offset'] || $arguments['font_shadow_y_offset'] || $arguments['font_shadow_width'] || $arguments['font_shadow_height']) && $arguments['font_stroke_color']) {
$shadow = TRUE;
}
if ($outline) {
$arguments['layout_padding_top'] += $arguments['font_outline_top'];
$arguments['layout_padding_right'] += $arguments['font_outline_right'];
$arguments['layout_padding_bottom'] += $arguments['font_outline_bottom'];
$arguments['layout_padding_left'] += $arguments['font_outline_left'];
}
elseif ($shadow) {
$arguments['layout_padding_top'] += $arguments['font_shadow_y_offset'] < 0 ? -$arguments['font_shadow_y_offset'] : 0;
$arguments['layout_padding_right'] += $arguments['font_shadow_x_offset'] > 0 ? $arguments['font_shadow_x_offset'] : 0;
$arguments['layout_padding_bottom'] += $arguments['font_shadow_y_offset'] > 0 ? $arguments['font_shadow_y_offset'] : 0;
$arguments['layout_padding_left'] += $arguments['font_shadow_x_offset'] < 0 ? -$arguments['font_shadow_x_offset'] : 0;
$shadow_width = $arguments['font_shadow_x_offset'] != 0 ? $arguments['font_shadow_width'] + 1 : $arguments['font_shadow_width'];
$shadow_height = $arguments['font_shadow_y_offset'] != 0 ? $arguments['font_shadow_height'] + 1 : $arguments['font_shadow_height'];
$net_right = $shadow_width + ($arguments['font_shadow_x_offset'] >= 0 ? 0 : $arguments['font_shadow_x_offset']);
$arguments['layout_padding_right'] += $net_right > 0 ? $net_right : 0;
$net_bottom = $shadow_height + ($arguments['font_shadow_y_offset'] >= 0 ? 0 : $arguments['font_shadow_y_offset']);
$arguments['layout_padding_bottom'] += $net_bottom > 0 ? $net_bottom : 0;
}
if ($arguments['text_maximum_width'] > 0) {
$arguments['text_string'] = $this
->wrapText($arguments['text_string'], $arguments['font_size'], $arguments['font_uri'], $arguments['text_maximum_width'] - $arguments['layout_padding_left'] - $arguments['layout_padding_right'] - 1, $arguments['text_align']);
}
$text_lines = explode("\n", $arguments['text_string']);
$num_lines = count($text_lines);
if ($arguments['text_fixed_width'] && !empty($arguments['text_maximum_width'])) {
$inner_box_width = $arguments['text_maximum_width'] - $arguments['layout_padding_left'] - $arguments['layout_padding_right'];
}
else {
$inner_box_width = $this
->getTextWidth($arguments['text_string'], $arguments['font_size'], $arguments['font_uri']);
}
$height_info = $this
->getTextHeightInfo($arguments['font_size'], $arguments['font_uri']);
$line_height = $height_info['height'];
$inner_box_height = $height_info['height'] * $num_lines + $arguments['text_line_spacing'] * ($num_lines - 1);
$outer_rect = new PositionedRectangle($inner_box_width + $arguments['layout_padding_right'] + $arguments['layout_padding_left'], $inner_box_height + $arguments['layout_padding_top'] + $arguments['layout_padding_bottom']);
$outer_rect
->rotate($arguments['font_angle']);
$outer_rect
->translate($outer_rect
->getRotationOffset());
$inner_rect = new PositionedRectangle($inner_box_width, $inner_box_height);
$inner_rect
->translate([
$arguments['layout_padding_left'],
$arguments['layout_padding_top'],
]);
$inner_rect
->rotate($arguments['font_angle']);
$inner_rect
->translate($outer_rect
->getRotationOffset());
$data = [
'width' => $outer_rect
->getBoundingWidth(),
'height' => $outer_rect
->getBoundingHeight(),
'extension' => 'png',
'is_temp' => FALSE,
];
if (!$this
->getToolkit()
->apply('create_new', $data)) {
return FALSE;
}
if ($arguments['layout_background_color']) {
$data_rectangle = [
'rectangle' => $outer_rect,
'fill_color' => $arguments['layout_background_color'],
];
$this
->getToolkit()
->apply('draw_rectangle', $data_rectangle);
}
if ($arguments['debug_visuals']) {
$data = [
'rectangle' => $inner_rect,
'border_color' => $arguments['layout_background_color'] ?: '#FFFFFF',
'border_color_luma' => TRUE,
];
$this
->getToolkit()
->apply('draw_rectangle', $data);
$data = [
'rectangle' => $outer_rect,
'border_color' => $arguments['layout_background_color'] ?: '#FFFFFF',
'border_color_luma' => TRUE,
];
$this
->getToolkit()
->apply('draw_rectangle', $data);
$data = [
'rectangle' => new PositionedRectangle($this
->getToolkit()
->getWidth(), $this
->getToolkit()
->getHeight()),
'border_color' => '#000000',
];
$this
->getToolkit()
->apply('draw_rectangle', $data);
}
$current_y = 0;
foreach ($text_lines as $text_line) {
$text_line_width = $this
->getTextWidth($text_line, $arguments['font_size'], $arguments['font_uri']);
$text_line_rect = new PositionedRectangle($text_line_width, $line_height);
$text_line_rect
->setPoint('basepoint', $height_info['basepoint']);
$x_delta = $inner_rect
->getWidth() - $text_line_rect
->getWidth();
$current_y += $line_height;
switch ($arguments['text_align']) {
case 'center':
$x_offset = round($x_delta / 2);
break;
case 'right':
$x_offset = $x_delta;
break;
case 'left':
default:
$x_offset = 0;
break;
}
$text_line_rect
->translate([
$arguments['layout_padding_left'] + $x_offset,
$arguments['layout_padding_top'] + $current_y - $line_height,
]);
$text_line_rect
->rotate($arguments['font_angle']);
$text_line_rect
->translate($outer_rect
->getRotationOffset());
$data = [
'text' => $text_line,
'basepoint' => $text_line_rect
->getPoint('basepoint'),
'font_uri' => $arguments['font_uri'],
'font_size' => $arguments['font_size'],
'font_angle' => $arguments['font_angle'],
'font_color' => $arguments['font_color'],
'font_stroke_mode' => $arguments['font_stroke_mode'],
'font_stroke_color' => $arguments['font_stroke_color'],
'font_outline_top' => $arguments['font_outline_top'],
'font_outline_right' => $arguments['font_outline_right'],
'font_outline_bottom' => $arguments['font_outline_bottom'],
'font_outline_left' => $arguments['font_outline_left'],
'font_shadow_x_offset' => $arguments['font_shadow_x_offset'],
'font_shadow_y_offset' => $arguments['font_shadow_y_offset'],
'font_shadow_width' => $arguments['font_shadow_width'],
'font_shadow_height' => $arguments['font_shadow_height'],
];
$this
->getToolkit()
->apply('text_overlay', $data);
if ($arguments['debug_visuals']) {
$this
->drawDebugBox($text_line_rect, $arguments['layout_background_color'], TRUE);
}
$current_y += $arguments['text_line_spacing'];
}
imagealphablending($this
->getToolkit()
->getResource(), TRUE);
imagesavealpha($this
->getToolkit()
->getResource(), TRUE);
if ($arguments['layout_overflow_action'] == 'scaletext') {
$this
->resizeWrapper($arguments);
}
return TRUE;
}
protected function resizeWrapper(array $arguments) {
$original_wrapper_width = $this
->getToolkit()
->getWidth();
$original_wrapper_height = $this
->getToolkit()
->getHeight();
$wrapper_xpos = ceil(image_filter_keyword($arguments['layout_x_pos'], $arguments['canvas_width'], $original_wrapper_width)) + $arguments['layout_x_offset'];
$wrapper_ypos = ceil(image_filter_keyword($arguments['layout_y_pos'], $arguments['canvas_height'], $original_wrapper_height)) + $arguments['layout_y_offset'];
$xc_pos = $wrapper_xpos + $original_wrapper_width;
$yc_pos = $wrapper_ypos + $original_wrapper_height;
$wrapper_xpos = max(0, $wrapper_xpos);
$wrapper_ypos = max(0, $wrapper_ypos);
$xc_pos = min($arguments['canvas_width'], $xc_pos);
$yc_pos = min($arguments['canvas_height'], $yc_pos);
$wrapper_width = $xc_pos - $wrapper_xpos;
$wrapper_height = $yc_pos - $wrapper_ypos;
if ($wrapper_width < 0 || $wrapper_height < 0) {
return;
}
$width_resize_index = $wrapper_width / $original_wrapper_width;
$height_resize_index = $wrapper_height / $original_wrapper_height;
if ($width_resize_index < 1 || $height_resize_index < 1) {
if ($width_resize_index < $height_resize_index) {
$wrapper_height = NULL;
}
else {
$wrapper_width = NULL;
}
$this
->getToolkit()
->apply('scale', [
'width' => $wrapper_width,
'height' => $wrapper_height,
]);
}
}
protected function drawDebugBox(PositionedRectangle $rect, $rgba, $luma = FALSE) {
if (!$rgba) {
$rgba = '#000000FF';
}
elseif ($luma) {
$rgba = ColorUtility::matchLuma($rgba);
}
$points = $this
->getRectangleCorners($rect);
$data = [
'rectangle' => $rect,
'border_color' => $rgba,
];
$this
->getToolkit()
->apply('draw_rectangle', $data);
$data = [
'x1' => $points[0],
'y1' => $points[1],
'x2' => $points[4],
'y2' => $points[5],
'color' => $rgba,
];
$this
->getToolkit()
->apply('draw_line', $data);
$orange = '#FF6400FF';
$yellow = '#FFFF00FF';
$green = '#00FF00FF';
$dotsize = 6;
for ($i = 0; $i < 8; $i += 2) {
$col = $i < 4 ? $orange : $yellow;
$data = [
'cx' => $points[$i],
'cy' => $points[$i + 1],
'width' => $dotsize,
'height' => $dotsize,
'color' => $col,
];
$this
->getToolkit()
->apply('draw_ellipse', $data);
}
$basepoint = $rect
->getPoint('basepoint');
$data = [
'cx' => $basepoint[0],
'cy' => $basepoint[1],
'width' => $dotsize,
'height' => $dotsize,
'color' => $green,
];
$this
->getToolkit()
->apply('draw_ellipse', $data);
}
protected function wrapText($text, $font_size, $font_uri, $maximum_width) {
$end = 0;
$begin = 0;
$fit = $begin;
while (TRUE) {
$match = [];
if (TextUtility::unicodePregMatch('/[' . TextUtility::PREG_CLASS_PUNCTUATION . '][' . TextUtility::PREG_CLASS_SEPARATOR . ']*|[' . TextUtility::PREG_CLASS_SEPARATOR . ']+/u', $text, $match, PREG_OFFSET_CAPTURE, $end)) {
$end = $match[0][1] + mb_strlen($match[0][0]);
}
else {
$end = mb_strlen($text);
}
$line = preg_replace('/[' . TextUtility::PREG_CLASS_SEPARATOR . ']+$/u', '', mb_substr($text, $begin, $end - $begin));
$width = $this
->getTextWidth($line, $font_size, $font_uri);
if ($width > $maximum_width) {
if ($fit == $begin) {
while (mb_strlen($line) > 0 && $width > $maximum_width) {
$line = mb_substr($line, 0, -1);
$width = $this
->getTextWidth($line, $font_size, $font_uri);
}
$fit = mb_strlen($line) ? $begin + mb_strlen($line) : $end;
}
if (mb_substr($text, $fit - 1, 1) == ' ') {
$first_part = mb_substr($text, 0, $fit - 1);
}
else {
$first_part = mb_substr($text, 0, $fit);
}
$last_part = mb_substr($text, $fit);
$text = $first_part . "\n" . $last_part;
$begin = ++$fit;
$end = $begin;
}
else {
$fit = $end;
}
if ($end == mb_strlen($text)) {
break;
}
}
return $text;
}
protected function getTextWidth($text, $font_size, $font_uri) {
if (!($font_file = $this
->getFontPath($font_uri))) {
return NULL;
}
$points = $this
->imagettfbboxWrapper($font_size, 0, $font_file, $text);
return abs($points[4] - $points[6]) + 1;
}
protected function getTextHeightInfo($font_size, $font_uri) {
if (!($font_file = $this
->getFontPath($font_uri))) {
return NULL;
}
$points = $this
->imagettfbboxWrapper($font_size, 0, $font_file, 'bdfhkltgjpqyBDFHKLTGJPQY§@çÅÀÈÉÌÒÇ');
$height = abs($points[5] - $points[1]) + 1;
return [
'height' => $height,
'basepoint' => [
$points[6],
-$points[7],
],
];
}
}