You are here

protected function TextToWrapper::execute in Image Effects 8

Same name in this branch
  1. 8 src/Plugin/ImageToolkit/Operation/gd/TextToWrapper.php \Drupal\image_effects\Plugin\ImageToolkit\Operation\gd\TextToWrapper::execute()
  2. 8 src/Plugin/ImageToolkit/Operation/imagemagick/TextToWrapper.php \Drupal\image_effects\Plugin\ImageToolkit\Operation\imagemagick\TextToWrapper::execute()
Same name and namespace in other branches
  1. 8.3 src/Plugin/ImageToolkit/Operation/gd/TextToWrapper.php \Drupal\image_effects\Plugin\ImageToolkit\Operation\gd\TextToWrapper::execute()
  2. 8.2 src/Plugin/ImageToolkit/Operation/gd/TextToWrapper.php \Drupal\image_effects\Plugin\ImageToolkit\Operation\gd\TextToWrapper::execute()

Performs the actual manipulation on the image.

Image toolkit operation implementers must implement this method. This method is responsible for actually performing the operation on the image. When this method gets called, the implementer may assume all arguments, also the optional ones, to be available, validated and corrected.

Parameters

array $arguments: An associative array of arguments to be used by the toolkit operation.

Return value

bool TRUE if the operation was performed successfully, FALSE otherwise.

Overrides ImageToolkitOperationBase::execute

File

src/Plugin/ImageToolkit/Operation/gd/TextToWrapper.php, line 33

Class

TextToWrapper
Defines GD Text Overlay text-to-wrapper operation.

Namespace

Drupal\image_effects\Plugin\ImageToolkit\Operation\gd

Code

protected function execute(array $arguments) {

  // Determine if outline/shadow is required.
  $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;
  }

  // Add stroke to padding to ensure inner box includes entire font space.
  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;
  }

  // Perform text wrapping, if necessary.
  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']);
  }

  // Load text lines to array elements.
  $text_lines = explode("\n", $arguments['text_string']);
  $num_lines = count($text_lines);

  // Calculate bounding boxes.
  // ---------------------------------------
  // Inner box - the exact bounding box of the text.
  // Outer box - the box where the inner box is - can be different because
  // of padding.
  // Wrapper - the canvas where the outer box is laid.
  // ---------------------------------------
  // Get inner box details, for horizontal text, unpadded.
  // If fixed width, set to configuration, otherwise get width from the font
  // bounding box.
  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']);
  }

  // Determine line height.
  $height_info = $this
    ->getTextHeightInfo($arguments['font_size'], $arguments['font_uri']);
  $line_height = $height_info['height'];

  // Manage leading (line spacing), adding total line spacing to height.
  $inner_box_height = $height_info['height'] * $num_lines + $arguments['text_line_spacing'] * ($num_lines - 1);

  // Get outer box.
  $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());

  // Get inner box.
  $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());

  // Set image dimensions to allow fitting the text. Explicitly setting
  // extension to 'png' to ensure wrapper is full transparent alpha channel
  // enabled.
  $data = [
    'width' => $outer_rect
      ->getBoundingWidth(),
    'height' => $outer_rect
      ->getBoundingHeight(),
    'extension' => 'png',
    'is_temp' => FALSE,
  ];
  if (!$this
    ->getToolkit()
    ->apply('create_new', $data)) {
    return FALSE;
  }

  // Draw and fill the outer text box, if required.
  if ($arguments['layout_background_color']) {
    $data_rectangle = [
      'rectangle' => $outer_rect,
      'fill_color' => $arguments['layout_background_color'],
    ];
    $this
      ->getToolkit()
      ->apply('draw_rectangle', $data_rectangle);
  }

  // In debug mode, visually display the text boxes.
  if ($arguments['debug_visuals']) {

    // Inner box.
    $data = [
      'rectangle' => $inner_rect,
      'border_color' => $arguments['layout_background_color'] ?: '#FFFFFF',
      'border_color_luma' => TRUE,
    ];
    $this
      ->getToolkit()
      ->apply('draw_rectangle', $data);

    // Outer box.
    $data = [
      'rectangle' => $outer_rect,
      'border_color' => $arguments['layout_background_color'] ?: '#FFFFFF',
      'border_color_luma' => TRUE,
    ];
    $this
      ->getToolkit()
      ->apply('draw_rectangle', $data);

    // Wrapper.
    $data = [
      'rectangle' => new PositionedRectangle($this
        ->getToolkit()
        ->getWidth(), $this
        ->getToolkit()
        ->getHeight()),
      'border_color' => '#000000',
    ];
    $this
      ->getToolkit()
      ->apply('draw_rectangle', $data);
  }

  // Process each of the text lines.
  $current_y = 0;
  foreach ($text_lines as $text_line) {

    // This text line's width.
    $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']);

    // Manage text alignment within the line.
    $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;
    }

    // Get details for the rotated/translated text line box.
    $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());

    // Overlay the text onto the image.
    $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);

    // In debug mode, display a polygon enclosing the text line.
    if ($arguments['debug_visuals']) {
      $this
        ->drawDebugBox($text_line_rect, $arguments['layout_background_color'], TRUE);
    }

    // Add interline spacing (leading) before next iteration.
    $current_y += $arguments['text_line_spacing'];
  }

  // Finalise image.
  imagealphablending($this
    ->getToolkit()
    ->getResource(), TRUE);
  imagesavealpha($this
    ->getToolkit()
    ->getResource(), TRUE);

  // Resize the wrapper if needed.
  if ($arguments['layout_overflow_action'] == 'scaletext') {
    $this
      ->resizeWrapper($arguments);
  }
  return TRUE;
}