diff --git a/README.txt b/README.txt
index 4d00920..4fe062c 100644
--- a/README.txt
+++ b/README.txt
@@ -125,11 +125,6 @@ Installation instructions (long version)
     that specify a format conversion like e.g. Convert or Textimage
     Background. This setting does not affect image derivatives created by the
     Drupal core Image module.
-  - Fonts manager - select the plugin that will manage fonts in Textimage, and
-    a default font to use. Make sure at least one font file is available.
-  - Background images managers - select the plugin that will manage background
-    images in Textimage.
-  - Color manager - select the plugin for selecting colors in Textimage.
   - URL generation - select whether to enable direct URL generation (see below)
     and the string to be used to separate text elements that need to be pushed
     to separate Textimage Text effects during generation.
diff --git a/src/Component/Rectangle.php b/src/Component/Rectangle.php
deleted file mode 100644
index d6f3f93..0000000
--- a/src/Component/Rectangle.php
+++ /dev/null
@@ -1,310 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\textimage\Component\Rectangle.
- */
-
-namespace Drupal\textimage\Component;
-
-/**
- * Rectangle algebra class.
- */
-class Rectangle {
-
-  /**
-   * An array of point coordinates, keyed by an id.
-   *
-   * Canonical points are:
-   * 'c_a' - bottom left corner of the rectangle
-   * 'c_b' - bottom right corner of the rectangle
-   * 'c_c' - top right corner of the rectangle
-   * 'c_d' - top left corner of the rectangle
-   * 'o_a' - bottom left corner of the bounding rectangle, once the rectangle
-   *         is rotated
-   * 'o_c' - top right corner of the bounding rectangle, once the rectangle
-   *         is rotated
-   * Additional points can be added through the setPoint() method. These will
-   * be subject to translation/rotation with the rest of the points when
-   * getTranslatedRectangle() method is executed.
-   *
-   * @var array
-   */
-  protected $points = [];
-
-  /**
-   * The width of the rectangle.
-   *
-   * The width is not influenced by rotation/translation.
-   *
-   * @var int
-   */
-  protected $width = 0;
-
-  /**
-   * The height of the rectangle.
-   *
-   * The height is not influenced by rotation/translation.
-   *
-   * @var int
-   */
-  protected $height = 0;
-
-  /**
-   * The angle at which the rectangle has been rotated.
-   *
-   * @var float
-   */
-  protected $angle = 0;
-
-  /**
-   * The offset needed to reposition the rectangle fully into first quadrant.
-   *
-   * Rotating a rectangle which is sticking to axes in the first quadrant
-   * results in some of its corners to shift to other quadrants. The x/y
-   * offset required to reposition it fully in the first quadrant is stored
-   * here.
-   *
-   * @var array
-   */
-  protected $rotationOffset = [0, 0];
-
-  /**
-   * Constructs a new Rectangle object.
-   *
-   * @param int $width
-   *   (Optional) The width of the rectangle.
-   * @param int $height
-   *   (Optional) The height of the rectangle.
-   */
-  public function __construct($width = 0, $height = 0) {
-    if ($width !== 0 && $height !== 0) {
-      $this->setFromDimensions($width, $height);
-    }
-  }
-
-  /**
-   * Sets a rectangle from its width and height.
-   *
-   * @param int $width
-   *   The width of the rectangle.
-   * @param int $height
-   *   The height of the rectangle.
-   *
-   * @return $this
-   */
-  public function setFromDimensions($width, $height) {
-    $this->setFromCorners([
-      'c_a' => [0, 0],
-      'c_b' => [$width - 1, 0],
-      'c_c' => [$width - 1, $height - 1],
-      'c_d' => [0, $height - 1],
-    ]);
-    return $this;
-  }
-
-  /**
-   * Sets a rectangle from the coordinates of its corners.
-   *
-   * @param array $corners
-   *   An associative array of point coordinates. The keys 'c_a', 'c_b',
-   *   'c_c' and 'c_d' represent each of the four a, b, c, d corners of the
-   *   rectangle in the format
-   *   D +-----------------+ C
-   *     |                 |
-   *     |                 |
-   *   A +-----------------+ B
-   *
-   * @return $this
-   */
-  public function setFromCorners(array $corners) {
-    $this
-      ->setPoint('c_a', $corners['c_a'])
-      ->setPoint('c_b', $corners['c_b'])
-      ->setPoint('c_c', $corners['c_c'])
-      ->setPoint('c_d', $corners['c_d'])
-      ->determineBoundingCorners();
-    $this->width = $this->getBoundingWidth();
-    $this->height = $this->getBoundingHeight();
-    return $this;
-  }
-
-  /**
-   * Sets a point and its coordinates.
-   *
-   * @param string $id
-   *   The point ID.
-   * @param array $coords
-   *   An array of x, y coordinates.
-   *
-   * @return $this
-   */
-  public function setPoint($id, array $coords = [0, 0]) {
-    $this->points[$id] = $coords;
-    return $this;
-  }
-
-  /**
-   * Gets the coordinates of a point.
-   *
-   * @param string $id
-   *   The point ID.
-   *
-   * @return array
-   *   An array of x, y coordinates.
-   */
-  public function getPoint($id) {
-    return $this->points[$id];
-  }
-
-  /**
-   * Gets the width of the rectangle.
-   *
-   * @return int
-   *   The width of the rectangle.
-   */
-  public function getWidth() {
-    return $this->width;
-  }
-
-  /**
-   * Gets the height of the rectangle.
-   *
-   * @return int
-   *   The height of the rectangle.
-   */
-  public function getHeight() {
-    return $this->height;
-  }
-
-  /**
-   * Gets the rotation offset of the rectangle.
-   *
-   * @return array
-   *   The x/y offset required to reposition the rectangle fully in the first
-   *   quadrant after it has been rotated.
-   */
-  public function getRotationOffset() {
-    return $this->rotationOffset;
-  }
-
-  /**
-   * Gets the bounding width of the rectangle.
-   *
-   * @return int
-   *   The bounding width of the rotated rectangle.
-   */
-  public function getBoundingWidth() {
-    return $this->points['o_c'][0] - $this->points['o_a'][0] + 1;
-  }
-
-  /**
-   * Gets the bounding height of the rectangle.
-   *
-   * @return int
-   *   The bounding height of the rotated rectangle.
-   */
-  public function getBoundingHeight() {
-    return $this->points['o_c'][1] - $this->points['o_a'][1] + 1;
-  }
-
-  /**
-   * Translates a point by an offset.
-   *
-   * @param array $point
-   *   An array of x, y coordinates.
-   * @param array $offset
-   *   Offset array (x, y).
-   *
-   * @return $this
-   */
-  protected function translatePoint(array &$point, array $offset) {
-    $point[0] += $offset[0];
-    $point[1] += $offset[1];
-    return $this;
-  }
-
-  /**
-   * Rotates a point, by an offset and a rotation angle.
-   *
-   * @param array $point
-   *   An array of x, y coordinates.
-   * @param float $angle
-   *   Rotation angle.
-   * @param array $offset
-   *   Offset array (x, y).
-   *
-   * @return $this
-   */
-  protected function rotatePoint(&$point, $angle) {
-    $rad = deg2rad($angle);
-    $sin = sin($rad);
-    $cos = cos($rad);
-    list($x, $y) = $point;
-    $tx = round(($x * $cos + $y * -$sin), 3);
-    $ty = round(($y * $cos - $x * -$sin), 3);
-    $point[0] = ($tx >= 0) ? ceil($tx) : -ceil(-$tx);
-    $point[1] = ($ty >= 0) ? ceil($ty) : -ceil(-$ty);
-    return $this;
-  }
-
-  /**
-   * Rotates the rectangle and any additional point.
-   *
-   * @param float $angle
-   *   Rotation angle.
-   */
-  public function rotate($angle) {
-    if ($angle) {
-      $this->angle = $angle;
-      foreach ($this->points as &$point) {
-        $this->rotatePoint($point, $angle);
-      }
-      $this->determineBoundingCorners();
-      $this->rotationOffset = [-$this->points['o_a'][0], -$this->points['o_a'][1]];
-    }
-    return $this;
-  }
-
-  /**
-   * Translates the rectangle and any additional point.
-   *
-   * @param array $offset
-   *   Offset array (x, y).
-   *
-   * @return $this
-   */
-  public function translate($offset) {
-    if ($offset[0] || $offset[1]) {
-      foreach ($this->points as &$point) {
-        $this->translatePoint($point, $offset);
-      }
-    }
-    return $this;
-  }
-
-  /**
-   * Calculates the corners of the bounding rectangle.
-   *
-   * The bottom left ('o_a') and top right ('o_c') corners of the bounding
-   * rectangle of a rotated rectangle are needed to determine the bounding
-   * width and height, and to calculate rotation-induced offest.
-   *
-   * @return $this
-   */
-  protected function determineBoundingCorners() {
-    $this
-      ->setPoint('o_a', [
-          min($this->points['c_a'][0], $this->points['c_b'][0], $this->points['c_c'][0], $this->points['c_d'][0]),
-          min($this->points['c_a'][1], $this->points['c_b'][1], $this->points['c_c'][1], $this->points['c_d'][1])
-        ]
-      )
-      ->setPoint('o_c', [
-          max($this->points['c_a'][0], $this->points['c_b'][0], $this->points['c_c'][0], $this->points['c_d'][0]),
-          max($this->points['c_a'][1], $this->points['c_b'][1], $this->points['c_c'][1], $this->points['c_d'][1])
-        ]
-      );
-    return $this;
-  }
-
-}
diff --git a/src/Plugin/ImageEffect/TextimageBackground.php b/src/Plugin/ImageEffect/TextimageBackground.php
index f655a07..b906f19 100644
--- a/src/Plugin/ImageEffect/TextimageBackground.php
+++ b/src/Plugin/ImageEffect/TextimageBackground.php
@@ -387,26 +387,29 @@ class TextimageBackground extends TextimageEffectBase {
       }
     }
 
-    // If resizing, apply textimage_define_canvas to finalise layout.
+    // If resizing, apply set_canvas to finalise layout.
     if ($this->configuration['exact']['width'] || $this->configuration['exact']['height'] || $this->configuration['relative']['leftdiff'] || $this->configuration['relative']['rightdiff'] || $this->configuration['relative']['topdiff'] || $this->configuration['relative']['bottomdiff']) {
-      list($xpos, $ypos) = explode('-', $this->configuration['exact']['position']);
-      $canvas_data = array(
-        'background_color' => $this->configuration['background']['color'],
-        'exact' => array(
+      if ($this->configuration['exact']['width'] || $this->configuration['exact']['height']) {
+        list($x_pos, $y_pos) = explode('-', $this->configuration['exact']['position']);
+        $canvas_data = [
+          'canvas_color' => $this->configuration['background']['color'],
           'width' => $this->configuration['exact']['width'],
           'height' => $this->configuration['exact']['height'],
-          'xpos' => $xpos,
-          'ypos' => $ypos,
-        ),
-        'relative' => array(
-          'leftdiff' => $this->configuration['relative']['leftdiff'],
-          'rightdiff' => $this->configuration['relative']['rightdiff'],
-          'topdiff' => $this->configuration['relative']['topdiff'],
-          'bottomdiff' => $this->configuration['relative']['bottomdiff'],
-        ),
-      );
+          'x_pos' => image_filter_keyword($x_pos, $this->configuration['exact']['width'], $image->getWidth()),
+          'y_pos' => image_filter_keyword($y_pos, $this->configuration['exact']['height'], $image->getHeight()),
+        ];
+      }
+      else {
+        $canvas_data = [
+          'canvas_color' => $this->configuration['background']['color'],
+          'width' => $image->getWidth() + $this->configuration['relative']['leftdiff'] + $this->configuration['relative']['rightdiff'],
+          'height' => $image->getHeight() + $this->configuration['relative']['topdiff'] + $this->configuration['relative']['bottomdiff'],
+          'x_pos' => $this->configuration['relative']['leftdiff'],
+          'y_pos' => $this->configuration['relative']['topdiff'],
+        ];
+      }
 
-      $success = $image->apply('textimage_define_canvas', $canvas_data);
+      $success = $image->apply('set_canvas', $canvas_data);
     }
 
     if (!$success) {
diff --git a/src/Plugin/ImageEffect/TextimageText.php b/src/Plugin/ImageEffect/TextimageText.php
index b6bc077..366b5bc 100644
--- a/src/Plugin/ImageEffect/TextimageText.php
+++ b/src/Plugin/ImageEffect/TextimageText.php
@@ -15,7 +15,7 @@ use Drupal\Core\Form\FormStateInterface;
 use Drupal\Core\Image\ImageInterface;
 use Drupal\Core\Render\BubbleableMetadata;
 use Drupal\image_effects\Component\ColorUtility;
-use Drupal\textimage\Component\Rectangle;
+use Drupal\image_effects\Component\PositionedRectangle;
 
 /**
  * Define the Textimage text.
@@ -642,14 +642,12 @@ $form_state->setValue(['ajax_config', 'preview_bar', 'debug_visuals'], $form_sta
 
       // Check wrapper image overflowing the original image.
       if ($this->canvasResizeNeeded($wrapper)) {
-        // Apply textimage_define_canvas, transparent background.
-        if (!$image->apply('textimage_define_canvas', [
-                'exact' => [
-                  'width' => $this->info['image_width'],
-                  'height' => $this->info['image_height'],
-                  'xpos' => $this->info['image_xpos'],
-                  'ypos' => $this->info['image_ypos'],
-                ],
+        // Apply set_canvas, transparent background.
+        if (!$image->apply('set_canvas', [
+                'width' => $this->info['image_width'],
+                'height' => $this->info['image_height'],
+                'x_pos' => $this->info['image_xpos'],
+                'y_pos' => $this->info['image_ypos'],
               ]
             )) {
           return FALSE;
@@ -657,7 +655,7 @@ $form_state->setValue(['ajax_config', 'preview_bar', 'debug_visuals'], $form_sta
         // Color fill the frame with carried on background color.
         if ($main_bg_color = $this->textimageFactory->getState('background_color')) {
           // Top rectangle.
-          $rectangle = new Rectangle();
+          $rectangle = new PositionedRectangle();
           if ($this->info['frame_top']) {
             $rectangle->setFromCorners([
               'c_a' => [0, $this->info['frame_top'] - 1],
@@ -665,7 +663,7 @@ $form_state->setValue(['ajax_config', 'preview_bar', 'debug_visuals'], $form_sta
               'c_c' => [$this->info['image_width'] - 1, 0],
               'c_d' => [0, 0],
             ]);
-            if (!$image->apply('textimage_draw_rectangle', ['rectangle' => $rectangle, 'fill_color' => $main_bg_color])) {
+            if (!$image->apply('draw_rectangle', ['rectangle' => $rectangle, 'fill_color' => $main_bg_color])) {
               return FALSE;
             };
           }
@@ -677,7 +675,7 @@ $form_state->setValue(['ajax_config', 'preview_bar', 'debug_visuals'], $form_sta
               'c_c' => [$this->info['image_width'] - 1, $image_height + $this->info['frame_top']],
               'c_d' => [0, $image_height + $this->info['frame_top']],
             ]);
-            if (!$image->apply('textimage_draw_rectangle', ['rectangle' => $rectangle, 'fill_color' => $main_bg_color])) {
+            if (!$image->apply('draw_rectangle', ['rectangle' => $rectangle, 'fill_color' => $main_bg_color])) {
               return FALSE;
             };
           }
@@ -689,7 +687,7 @@ $form_state->setValue(['ajax_config', 'preview_bar', 'debug_visuals'], $form_sta
               'c_c' => [$this->info['frame_left'] - 1, $this->info['frame_top']],
               'c_d' => [0, $this->info['frame_top']],
             ]);
-            if (!$image->apply('textimage_draw_rectangle', ['rectangle' => $rectangle, 'fill_color' => $main_bg_color])) {
+            if (!$image->apply('draw_rectangle', ['rectangle' => $rectangle, 'fill_color' => $main_bg_color])) {
               return FALSE;
             };
           }
@@ -701,7 +699,7 @@ $form_state->setValue(['ajax_config', 'preview_bar', 'debug_visuals'], $form_sta
               'c_c' => [$this->info['image_width'] - 1, $this->info['frame_top']],
               'c_d' => [$this->info['frame_left'] + $image_width, $this->info['frame_top']],
             ]);
-            if (!$image->apply('textimage_draw_rectangle', ['rectangle' => $rectangle, 'fill_color' => $main_bg_color])) {
+            if (!$image->apply('draw_rectangle', ['rectangle' => $rectangle, 'fill_color' => $main_bg_color])) {
               return FALSE;
             };
           }
@@ -717,7 +715,11 @@ $form_state->setValue(['ajax_config', 'preview_bar', 'debug_visuals'], $form_sta
     }
 
     // Finally, lay the wrapper over the source image.
-    if (!$image->apply('textimage_overlay', ['layer' => $wrapper, 'x' => $this->info['wrapper_xpos'], 'y' => $this->info['wrapper_ypos']])) {
+    if (!$image->apply('watermark', [
+        'watermark_image' => $wrapper,
+        'x_offset' => $this->info['wrapper_xpos'],
+        'y_offset' => $this->info['wrapper_ypos'],
+      ])) {
       return FALSE;
     }
 
diff --git a/src/Plugin/ImageToolkit/Operation/TextimageDefineCanvasTrait.php b/src/Plugin/ImageToolkit/Operation/TextimageDefineCanvasTrait.php
deleted file mode 100644
index 8fba476..0000000
--- a/src/Plugin/ImageToolkit/Operation/TextimageDefineCanvasTrait.php
+++ /dev/null
@@ -1,74 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\textimage\Plugin\ImageToolkit\Operation\TextimageDefineCanvasTrait.
- */
-
-namespace Drupal\textimage\Plugin\ImageToolkit\Operation;
-
-/**
- * Base trait for Textimage define canvas operations.
- */
-trait TextimageDefineCanvasTrait {
-
-  /**
-   * {@inheritdoc}
-   */
-  protected function arguments() {
-    return array(
-      'background_color' => array(
-        'description' => 'Color',
-        'required' => FALSE,
-        'default' => NULL,
-      ),
-      'exact' => array(
-        'description' => 'Exact dimensions canvas',
-        'required' => FALSE,
-        'default' => NULL,
-      ),
-      'relative' => array(
-        'description' => 'Relative dimensions canvas',
-        'required' => FALSE,
-        'default' => NULL,
-      ),
-    );
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  protected function validateArguments(array $arguments) {
-    $targetsize = array();
-    // May be given either exact or relative dimensions.
-    if ($arguments['exact'] && ($arguments['exact']['width'] || $arguments['exact']['height'])) {
-      // Allows only one dimension to be used if the other is unset.
-      if (!$arguments['exact']['width']) {
-        $arguments['exact']['width'] = $this->getToolkit()->getWidth();
-      }
-      if (!$arguments['exact']['height']) {
-        $arguments['exact']['height'] = $this->getToolkit()->getHeight();
-      }
-
-      $targetsize['width'] = $this->percentFilter($arguments['exact']['width'], $this->getToolkit()->getWidth());
-      $targetsize['height'] = $this->percentFilter($arguments['exact']['height'], $this->getToolkit()->getHeight());
-
-      $targetsize['left'] = image_filter_keyword($arguments['exact']['xpos'], $targetsize['width'], $this->getToolkit()->getWidth());
-      $targetsize['top'] = image_filter_keyword($arguments['exact']['ypos'], $targetsize['height'], $this->getToolkit()->getHeight());
-
-    }
-    else {
-      // Calculate relative size.
-      $targetsize['width'] = $this->getToolkit()->getWidth() + $arguments['relative']['leftdiff'] + $arguments['relative']['rightdiff'];
-      $targetsize['height'] = $this->getToolkit()->getHeight() + $arguments['relative']['topdiff'] + $arguments['relative']['bottomdiff'];
-      $targetsize['left'] = $arguments['relative']['leftdiff'];
-      $targetsize['top'] = $arguments['relative']['topdiff'];
-    }
-
-    // All the math is done.
-    $arguments['targetsize'] = $targetsize;
-
-    return $arguments;
-  }
-
-}
diff --git a/src/Plugin/ImageToolkit/Operation/TextimageDrawRectangleTrait.php b/src/Plugin/ImageToolkit/Operation/TextimageDrawRectangleTrait.php
deleted file mode 100644
index ee07ded..0000000
--- a/src/Plugin/ImageToolkit/Operation/TextimageDrawRectangleTrait.php
+++ /dev/null
@@ -1,67 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\textimage\Plugin\ImageToolkit\Operation\TextimageDrawRectangleTrait.
- */
-
-namespace Drupal\textimage\Plugin\ImageToolkit\Operation;
-
-use Drupal\image_effects\Component\ColorUtility;
-use Drupal\textimage\Component\Rectangle;
-
-/**
- * Base trait for Textimage draw rectangle operations.
- */
-trait TextimageDrawRectangleTrait {
-
-  /**
-   * {@inheritdoc}
-   */
-  protected function arguments() {
-    return array(
-      'rectangle' => array(
-        'description' => 'A Rectangle object.',
-      ),
-      'fill_color' => array(
-        'description' => 'The RGBA color of the polygon fill.',
-        'required' => FALSE,
-        'default' => NULL,
-      ),
-      'fill_color_luma' => array(
-        'description' => 'If TRUE, convert RGBA of the polygon fill to best match using luma.',
-        'required' => FALSE,
-        'default' => FALSE,
-      ),
-      'border_color' => array(
-        'description' => 'The RGBA color of the polygon line.',
-        'required' => FALSE,
-        'default' => NULL,
-      ),
-      'border_color_luma' => array(
-        'description' => 'If TRUE, convert RGBA of the polygon line to best match using luma.',
-        'required' => FALSE,
-        'default' => FALSE,
-      ),
-    );
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  protected function validateArguments(array $arguments) {
-    // Ensure 'rectangle' is an expected Rectangle object.
-    if (!$arguments['rectangle'] instanceof Rectangle) {
-      throw new \InvalidArgumentException("Rectangle passed to the 'textimage_draw_rectangle' operation is invalid");
-    }
-    // Match color luma.
-    if ($arguments['fill_color'] && $arguments['fill_color_luma']) {
-      $arguments['fill_color'] = ColorUtility::matchLuma($arguments['fill_color']);
-    }
-    if ($arguments['border_color'] && $arguments['border_color_luma']) {
-      $arguments['border_color'] = ColorUtility::matchLuma($arguments['border_color']);
-    }
-    return $arguments;
-  }
-
-}
diff --git a/src/Plugin/ImageToolkit/Operation/TextimageOperationTrait.php b/src/Plugin/ImageToolkit/Operation/TextimageOperationTrait.php
index 7e18b70..c47a806 100644
--- a/src/Plugin/ImageToolkit/Operation/TextimageOperationTrait.php
+++ b/src/Plugin/ImageToolkit/Operation/TextimageOperationTrait.php
@@ -82,140 +82,4 @@ trait TextimageOperationTrait {
     return $this->streamWrapperManager;
   }
 
-  /**
-   * Computes a length based on a length specification and an actual length.
-   *
-   * Examples:
-   *  (50, 400) returns 50; (50%, 400) returns 200;
-   *  (50, null) returns 50; (50%, null) returns null;
-   *  (null, null) returns null; (null, 100) returns null.
-   *
-   * @param string|null $length_specification
-   *   The length specification. An integer constant or a % specification.
-   * @param int|null $current_length
-   *   The current length. May be null.
-   *
-   * @return int|null
-   */
-  protected function percentFilter($length_specification, $current_length) {
-    if (strpos($length_specification, '%') !== FALSE) {
-      $length_specification =  $current_length !== NULL ? str_replace('%', '', $length_specification) * 0.01 * $current_length : NULL;
-    }
-    return $length_specification;
-  }
-
-  /**
-   * Accept a keyword (center, top, left, etc) and return it as an offset in pixels.
-   * Called on either the x or y values.
-   *
-   * May  be something like "20", "center", "left+20", "bottom+10". + values are
-   * in from the sides, so bottom+10 is 10 UP from the bottom.
-   *
-   * "center+50" is also OK.
-   *
-   * "30%" will place the CENTER of the object at 30% across. to get a 30% margin,
-   * use "left+30%"
-   *
-   * @param string|int $value
-   *   string or int value.
-   * @param int $base_size
-   *   Size in pixels of the range this item is to be placed in.
-   * @param int $layer_size
-   *   Size in pixels of the object to be placed.
-   *
-   * @return int
-   */
-  protected function keywordFilter($value, $base_size, $layer_size) {
-    // See above for the patterns this matches
-    if (! preg_match('/([a-z]*)([\+\-]?)(\d*)([^\d]*)/', $value, $results) ) {
-      // @todo (imagecache_actions) exception?
-      trigger_error("imagecache_actions had difficulty parsing the string '$value' when calculating position. Please check the syntax.", E_USER_WARNING);
-    }
-    list(, $keyword, $plusminus, $value, $unit) = $results;
-
-    return $this->calculateOffset($keyword, $plusminus . $value . $unit, $base_size, $layer_size);
-  }
-
-  /**
-   * Positive numbers are IN from the edge, negative offsets are OUT.
-   *
-   * $keyword, $value, $base_size, $layer_size
-   * eg
-   * left,20 200, 100 = 20
-   * right,20 200, 100 = 80 (object 100 wide placed 20 px from the right = x=80)
-   *
-   * top,50%, 200, 100 = 50 (Object is centered when using %)
-   * top,20%, 200, 100 = -10
-   * bottom,-20, 200, 100 = 220
-   * right, -25%, 200, 100 = 200 (this ends up just off screen)
-   *
-   * Also, the value can be a string, eg "bottom-100", or "center+25%"
-   */
-  protected function calculateOffset($keyword, $value, $base_size, $layer_size) {
-    $offset = 0; // used to account for dimensions of the placed object
-    $direction = 1;
-    $base = 0;
-    if ($keyword == 'right' || $keyword == 'bottom') {
-      $direction = -1;
-      $offset = -1 * $layer_size;
-      $base = $base_size;
-    }
-    if ($keyword == 'middle' || $keyword == 'center') {
-      $base = $base_size / 2;
-      $offset = -1 * ($layer_size / 2);
-    }
-
-    // Keywords may be used to stand in for numeric values
-    switch ($value) {
-      case 'left':
-      case 'top':
-        $value = 0;
-        break;
-      case 'middle':
-      case 'center':
-        $value = $base_size / 2;
-        break;
-      case 'bottom':
-      case 'right':
-        $value = $base_size;
-    }
-
-    // Handle keyword-number cases like top+50% or bottom-100px,
-    // @see imagecache_actions_keyword_filter().
-    if (preg_match('/^(.+)([\+\-])(\d+)([^\d]*)$/', $value, $results)) {
-      list(, $value_key, $value_mod, $mod_value, $mod_unit) = $results;
-      if ($mod_unit == '%') {
-        $mod_value = $mod_value / 100 * $base_size;
-      }
-      $mod_direction = ($value_mod == '-') ? -1 : + 1;
-      switch ($value_key) {
-        case 'left':
-        case 'top':
-        default:
-          $mod_base = 0;
-          break;
-        case 'middle':
-        case 'center':
-          $mod_base = $base_size / 2;
-          break;
-        case 'bottom':
-        case 'right':
-          $mod_base = $base_size;
-          break;
-      }
-      $modified_value = $mod_base + ($mod_direction * $mod_value);
-      return $modified_value;
-    }
-
-    // handle % values
-    if (substr($value, strlen($value) -1, 1) == '%') {
-      $value = intval($value / 100 * $base_size);
-      $offset = -1 * ($layer_size / 2);
-    }
-    $value = $base + ($direction * $value);
-
-    // Add any extra offset to position the item
-    return $value + $offset;
-  }
-
 }
diff --git a/src/Plugin/ImageToolkit/Operation/TextimageOverlayTrait.php b/src/Plugin/ImageToolkit/Operation/TextimageOverlayTrait.php
deleted file mode 100644
index 00e5aac..0000000
--- a/src/Plugin/ImageToolkit/Operation/TextimageOverlayTrait.php
+++ /dev/null
@@ -1,41 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\textimage\Plugin\ImageToolkit\Operation\TextimageOverlayTrait.
- */
-
-namespace Drupal\textimage\Plugin\ImageToolkit\Operation;
-
-/**
- * Base trait for Textimage overlay operations.
- */
-trait TextimageOverlayTrait {
-
-  /**
-   * {@inheritdoc}
-   */
-  protected function arguments() {
-    return array(
-      'layer' => array(
-        'description' => 'Image object to be placed over the current image.',
-      ),
-      'x' => array(
-        'description' => 'x-position of the overlay.',
-      ),
-      'y' => array(
-        'description' => 'y-position of the overlay.',
-      ),
-    );
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  protected function validateArguments(array $arguments) {
-    $arguments['x'] = $this->keywordFilter($arguments['x'], $this->getToolkit()->getWidth(), $arguments['layer']->getWidth());
-    $arguments['y'] = $this->keywordFilter($arguments['y'], $this->getToolkit()->getHeight(), $arguments['layer']->getHeight());
-    return $arguments;
-  }
-
-}
diff --git a/src/Plugin/ImageToolkit/Operation/gd/GDTextimageOperationBase.php b/src/Plugin/ImageToolkit/Operation/gd/GDTextimageOperationBase.php
deleted file mode 100644
index 01695d8..0000000
--- a/src/Plugin/ImageToolkit/Operation/gd/GDTextimageOperationBase.php
+++ /dev/null
@@ -1,88 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\textimage\Plugin\ImageToolkit\Operation\gd\GDTextimageOperationBase.
- */
-
-namespace Drupal\textimage\Plugin\ImageToolkit\Operation\gd;
-
-use Drupal\Component\Utility\Color;
-use Drupal\Component\Utility\Unicode;
-use Drupal\system\Plugin\ImageToolkit\Operation\gd\GDImageToolkitOperationBase;
-use Drupal\image_effects\Component\ColorUtility;
-use Drupal\textimage\Component\Rectangle;
-use Drupal\textimage\Plugin\ImageToolkit\Operation\TextimageOperationTrait;
-
-/**
- * Base class for Textimage GD image toolkit operations.
- */
-abstract class GDTextimageOperationBase extends GDImageToolkitOperationBase {
-
-  use TextimageOperationTrait;
-
-  /**
-   * Allocates a GD color from an RGBA hexadecimal.
-   *
-   * @param string $rgba_hex
-   *   A string specifing an RGBA color in the format '#RRGGBBAA'.
-   *
-   * @return int
-   *   A GD color index.
-   */
-  protected function allocateColorFromRgba($rgba_hex) {
-    list($r, $g, $b, $alpha) = array_values($this->hexToRgba($rgba_hex));
-    return imagecolorallocatealpha($this->getToolkit()->getResource(), $r, $g, $b, $alpha);
-  }
-
-  /**
-   * Convert a RGBA hex to its RGBA integer GD components.
-   *
-   * GD expects a value between 0 and 127 for alpha, where 0 indicates
-   * completely opaque while 127 indicates completely transparent.
-   * RGBA hexadecimal notation has #00 for transparent and #FF for
-   * fully opaque.
-   *
-   * @param string $rgba_hex
-   *   A string specifing an RGBA color in the format '#RRGGBBAA'.
-   *
-   * @return array
-   *   An array with four elements for red, green, blue, and alpha.
-   */
-  public function hexToRgba($rgba_hex) {
-    $rgbHex = Unicode::substr($rgba_hex, 0, 7);
-    try {
-      $rgb = Color::hexToRgb($rgbHex);
-      $opacity = ColorUtility::rgbaToOpacity($rgba_hex);
-      $alpha = 127 - floor(($opacity / 100) * 127);
-      $rgb['alpha'] = $alpha;
-      return $rgb;
-    }
-    catch (\InvalidArgumentException $e) {
-      return FALSE;
-    }
-  }
-
-  /**
-   * Convert a rectangle to a sequence of point coordinates.
-   *
-   * GD requires a simple array of point coordinates in its
-   * imagepolygon() function.
-   *
-   * @param \Drupal\textimage\Component\Rectangle $rect
-   *   A Rectangle object.
-   *
-   * @return array
-   *   A simple array of 8 point coordinates.
-   */
-  public function getRectangleCorners(Rectangle $rect) {
-    $points = [];
-    foreach (array('c_d', 'c_c', 'c_b', 'c_a') as $c) {
-      $point = $rect->getPoint($c);
-      $points[] = $point[0];
-      $points[] = $point[1];
-    }
-    return $points;
-  }
-
-}
diff --git a/src/Plugin/ImageToolkit/Operation/gd/TextimageDefineCanvas.php b/src/Plugin/ImageToolkit/Operation/gd/TextimageDefineCanvas.php
deleted file mode 100644
index 421379a..0000000
--- a/src/Plugin/ImageToolkit/Operation/gd/TextimageDefineCanvas.php
+++ /dev/null
@@ -1,73 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\textimage\Plugin\ImageToolkit\Operation\gd\TextimageDefineCanvas.
- */
-
-namespace Drupal\textimage\Plugin\ImageToolkit\Operation\gd;
-
-use Drupal\textimage\Plugin\ImageToolkit\Operation\TextimageDefineCanvasTrait;
-use Drupal\textimage\Component\Rectangle;
-
-/**
- * Defines Textimage GD2 define canvas operation.
- *
- * Much of the code in this class is taken from the imagecache_action module
- * for Drupal 7.
- *
- * @ImageToolkitOperation(
- *   id = "textimage_gd_textimage_define_canvas",
- *   toolkit = "gd",
- *   operation = "textimage_define_canvas",
- *   label = @Translation("Textimage define canvas image"),
- *   description = @Translation("Defines a canvas for the image.")
- * )
- */
-class TextimageDefineCanvas extends GDTextimageOperationBase {
-
-  use TextimageDefineCanvasTrait;
-
-  /**
-   * {@inheritdoc}
-   */
-  protected function execute(array $arguments) {
-    $targetsize = $arguments['targetsize'];
-
-    // Prepare the canvas.
-    $original_res = $this->getToolkit()->getResource();
-    $data = array(
-      'width' => $targetsize['width'],
-      'height' => $targetsize['height'],
-      'extension' => image_type_to_extension($this->getToolkit()->getType(), FALSE),
-      'transparent_color' => $this->getToolkit()->getTransparentColor(),
-      'is_temp' => TRUE,
-    );
-    $this->getToolkit()->apply('create_new', $data);
-    $data = array(
-      'rectangle' => new Rectangle($targetsize['width'], $targetsize['height']),
-      'fill_color' => $arguments['background_color'],
-    );
-    $this->getToolkit()->apply('textimage_draw_rectangle', $data);
-
-    // Overlay the current image on the canvas.
-    $x = $this->keywordFilter($targetsize['left'], $this->getToolkit()->getWidth(), imagesx($original_res));
-    $y = $this->keywordFilter($targetsize['top'], $this->getToolkit()->getHeight(), imagesy($original_res));
-    imagealphablending($original_res, TRUE);
-    imagesavealpha($original_res, TRUE);
-    imagealphablending($this->getToolkit()->getResource(), TRUE);
-    imagesavealpha($this->getToolkit()->getResource(), TRUE);
-    if (imagecopy($this->getToolkit()->getResource(), $original_res, $x, $y, 0, 0, imagesx($original_res), imagesy($original_res))) {
-      imagedestroy($original_res);
-      return TRUE;
-    }
-    else {
-      // In case of failure, destroy the temporary resource and restore
-      // the original one.
-      imagedestroy($this->getToolkit()->getResource());
-      $this->getToolkit()->setResource($original_res);
-    }
-    return FALSE;
-  }
-
-}
diff --git a/src/Plugin/ImageToolkit/Operation/gd/TextimageDrawEllipse.php b/src/Plugin/ImageToolkit/Operation/gd/TextimageDrawEllipse.php
index 23ce33f..b3a6111 100644
--- a/src/Plugin/ImageToolkit/Operation/gd/TextimageDrawEllipse.php
+++ b/src/Plugin/ImageToolkit/Operation/gd/TextimageDrawEllipse.php
@@ -7,6 +7,10 @@
 
 namespace Drupal\textimage\Plugin\ImageToolkit\Operation\gd;
 
+use Drupal\system\Plugin\ImageToolkit\Operation\gd\GDImageToolkitOperationBase;
+use Drupal\image_effects\Plugin\ImageToolkit\Operation\gd\GDOperationTrait;
+use Drupal\textimage\Plugin\ImageToolkit\Operation\TextimageOperationTrait;
+
 /**
  * Defines Textimage GD2 draw ellipse operation.
  *
@@ -18,7 +22,10 @@ namespace Drupal\textimage\Plugin\ImageToolkit\Operation\gd;
  *   description = @Translation("Draws on the image an ellipse of the specified color.")
  * )
  */
-class TextimageDrawEllipse extends GDTextimageOperationBase {
+class TextimageDrawEllipse extends GDImageToolkitOperationBase {
+
+  use TextimageOperationTrait;
+  use GDOperationTrait;
 
   /**
    * {@inheritdoc}
diff --git a/src/Plugin/ImageToolkit/Operation/gd/TextimageDrawLine.php b/src/Plugin/ImageToolkit/Operation/gd/TextimageDrawLine.php
index 6aa735e..2a1cb48 100644
--- a/src/Plugin/ImageToolkit/Operation/gd/TextimageDrawLine.php
+++ b/src/Plugin/ImageToolkit/Operation/gd/TextimageDrawLine.php
@@ -7,6 +7,10 @@
 
 namespace Drupal\textimage\Plugin\ImageToolkit\Operation\gd;
 
+use Drupal\system\Plugin\ImageToolkit\Operation\gd\GDImageToolkitOperationBase;
+use Drupal\image_effects\Plugin\ImageToolkit\Operation\gd\GDOperationTrait;
+use Drupal\textimage\Plugin\ImageToolkit\Operation\TextimageOperationTrait;
+
 /**
  * Defines Textimage GD2 draw line operation.
  *
@@ -18,7 +22,10 @@ namespace Drupal\textimage\Plugin\ImageToolkit\Operation\gd;
  *   description = @Translation("Draws on the image a line of the specified color.")
  * )
  */
-class TextimageDrawLine extends GDTextimageOperationBase {
+class TextimageDrawLine extends GDImageToolkitOperationBase {
+
+  use TextimageOperationTrait;
+  use GDOperationTrait;
 
   /**
    * {@inheritdoc}
diff --git a/src/Plugin/ImageToolkit/Operation/gd/TextimageDrawRectangle.php b/src/Plugin/ImageToolkit/Operation/gd/TextimageDrawRectangle.php
deleted file mode 100644
index 5287ca0..0000000
--- a/src/Plugin/ImageToolkit/Operation/gd/TextimageDrawRectangle.php
+++ /dev/null
@@ -1,43 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\textimage\Plugin\ImageToolkit\Operation\gd\TextimageDrawRectangle.
- */
-
-namespace Drupal\textimage\Plugin\ImageToolkit\Operation\gd;
-
-use Drupal\textimage\Plugin\ImageToolkit\Operation\TextimageDrawRectangleTrait;
-
-/**
- * Defines Textimage GD2 draw rectangle operation.
- *
- * @ImageToolkitOperation(
- *   id = "textimage_gd_textimage_draw_rectangle",
- *   toolkit = "gd",
- *   operation = "textimage_draw_rectangle",
- *   label = @Translation("Textimage Draw Rectangle"),
- *   description = @Translation("Draws on the image a rectangle, optionally filling it in with a specified color.")
- * )
- */
-class TextimageDrawRectangle extends GDTextimageOperationBase {
-
-  use TextimageDrawRectangleTrait;
-
-  /**
-   * {@inheritdoc}
-   */
-  protected function execute(array $arguments) {
-    $success = TRUE;
-    if ($arguments['fill_color']) {
-      $color = $this->allocateColorFromRgba($arguments['fill_color']);
-      $success = imagefilledpolygon($this->getToolkit()->getResource(), $this->getRectangleCorners($arguments['rectangle']), 4, $color);
-    }
-    if ($success && $arguments['border_color']) {
-      $color = $this->allocateColorFromRgba($arguments['border_color']);
-      $success = imagepolygon($this->getToolkit()->getResource(), $this->getRectangleCorners($arguments['rectangle']), 4, $color);
-    }
-    return $success;
-  }
-
-}
diff --git a/src/Plugin/ImageToolkit/Operation/gd/TextimageOverlay.php b/src/Plugin/ImageToolkit/Operation/gd/TextimageOverlay.php
deleted file mode 100644
index 3d11935..0000000
--- a/src/Plugin/ImageToolkit/Operation/gd/TextimageOverlay.php
+++ /dev/null
@@ -1,44 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\textimage\Plugin\ImageToolkit\Operation\gd\TextimageOverlay.
- */
-
-namespace Drupal\textimage\Plugin\ImageToolkit\Operation\gd;
-
-use Drupal\textimage\Plugin\ImageToolkit\Operation\TextimageOverlayTrait;
-
-/**
- * Defines Textimage GD2 overlay operation.
- *
- * Much of the code in this class is taken from the imagecache_action module
- * for Drupal 7.
- *
- * @ImageToolkitOperation(
- *   id = "textimage_gd_textimage_overlay",
- *   toolkit = "gd",
- *   operation = "textimage_overlay",
- *   label = @Translation("Textimage overlay image"),
- *   description = @Translation("Overlays an image over or under the current.")
- * )
- */
-class TextimageOverlay extends GDTextimageOperationBase {
-
-  use TextimageOverlayTrait;
-
-  /**
-   * {@inheritdoc}
-   */
-  protected function execute(array $arguments) {
-    $upper = $arguments['layer']->getToolkit()->getResource();
-    $lower = $this->getToolkit()->getResource();
-    imagealphablending($lower, TRUE);
-    imagesavealpha($lower, TRUE);
-    imagealphablending($upper, TRUE);
-    imagesavealpha($upper, TRUE);
-    imagecopy($lower, $upper, $arguments['x'], $arguments['y'], 0, 0, imagesx($upper), imagesy($upper));
-    return TRUE;
-  }
-
-}
diff --git a/src/Plugin/ImageToolkit/Operation/gd/TextimageReplaceImage.php b/src/Plugin/ImageToolkit/Operation/gd/TextimageReplaceImage.php
index 919ebb3..32e5f79 100644
--- a/src/Plugin/ImageToolkit/Operation/gd/TextimageReplaceImage.php
+++ b/src/Plugin/ImageToolkit/Operation/gd/TextimageReplaceImage.php
@@ -9,6 +9,10 @@ namespace Drupal\textimage\Plugin\ImageToolkit\Operation\gd;
 
 use Drupal\textimage\Plugin\ImageToolkit\Operation\TextimageReplaceImageTrait;
 
+use Drupal\system\Plugin\ImageToolkit\Operation\gd\GDImageToolkitOperationBase;
+use Drupal\image_effects\Plugin\ImageToolkit\Operation\gd\GDOperationTrait;
+use Drupal\textimage\Plugin\ImageToolkit\Operation\TextimageOperationTrait;
+
 /**
  * Defines Textimage GD2 image replace operation.
  *
@@ -20,8 +24,10 @@ use Drupal\textimage\Plugin\ImageToolkit\Operation\TextimageReplaceImageTrait;
  *   description = @Translation("Replace the current image with another one.")
  * )
  */
-class TextimageReplaceImage extends GDTextimageOperationBase {
+class TextimageReplaceImage extends GDImageToolkitOperationBase {
 
+  use TextimageOperationTrait;
+  use GDOperationTrait;
   use TextimageReplaceImageTrait;
 
   /**
@@ -42,11 +48,11 @@ class TextimageReplaceImage extends GDTextimageOperationBase {
 
     // Overlay replacement image.
     $data = [
-      'layer' => $arguments['replacement_image'],
-      'x' => 0,
-      'y' => 0,
+      'watermark_image' => $arguments['replacement_image'],
+      'x_offset' => 0,
+      'y_offset' => 0,
     ];
-    return $this->getToolkit()->apply('textimage_overlay', $data);
+    return $this->getToolkit()->apply('watermark', $data);
   }
 
 }
diff --git a/src/Plugin/ImageToolkit/Operation/gd/TextimageSetGifTransparentColor.php b/src/Plugin/ImageToolkit/Operation/gd/TextimageSetGifTransparentColor.php
index af0edf8..e9758d6 100644
--- a/src/Plugin/ImageToolkit/Operation/gd/TextimageSetGifTransparentColor.php
+++ b/src/Plugin/ImageToolkit/Operation/gd/TextimageSetGifTransparentColor.php
@@ -8,6 +8,9 @@
 namespace Drupal\textimage\Plugin\ImageToolkit\Operation\gd;
 
 use Drupal\Component\Utility\Color;
+use Drupal\system\Plugin\ImageToolkit\Operation\gd\GDImageToolkitOperationBase;
+use Drupal\image_effects\Plugin\ImageToolkit\Operation\gd\GDOperationTrait;
+use Drupal\textimage\Plugin\ImageToolkit\Operation\TextimageOperationTrait;
 use Drupal\textimage\Plugin\ImageToolkit\Operation\TextimageSetGifTransparentColorTrait;
 
 /**
@@ -21,8 +24,10 @@ use Drupal\textimage\Plugin\ImageToolkit\Operation\TextimageSetGifTransparentCol
  *   description = @Translation("Set the image transparent color for GIF images.")
  * )
  */
-class TextimageSetGifTransparentColor extends GDTextimageOperationBase  {
+class TextimageSetGifTransparentColor extends GDImageToolkitOperationBase {
 
+  use TextimageOperationTrait;
+  use GDOperationTrait;
   use TextimageSetGifTransparentColorTrait;
 
   /**
diff --git a/src/Plugin/ImageToolkit/Operation/gd/TextimageTextOverlay.php b/src/Plugin/ImageToolkit/Operation/gd/TextimageTextOverlay.php
index 5e9a5de..01b8421 100644
--- a/src/Plugin/ImageToolkit/Operation/gd/TextimageTextOverlay.php
+++ b/src/Plugin/ImageToolkit/Operation/gd/TextimageTextOverlay.php
@@ -8,6 +8,9 @@
 namespace Drupal\textimage\Plugin\ImageToolkit\Operation\gd;
 
 use Drupal\image_effects\Component\ColorUtility;
+use Drupal\system\Plugin\ImageToolkit\Operation\gd\GDImageToolkitOperationBase;
+use Drupal\image_effects\Plugin\ImageToolkit\Operation\gd\GDOperationTrait;
+use Drupal\textimage\Plugin\ImageToolkit\Operation\TextimageOperationTrait;
 
 /**
  * Defines Textimage GD2 text overlay operation.
@@ -20,7 +23,10 @@ use Drupal\image_effects\Component\ColorUtility;
  *   description = @Translation("Overlays a given text into the image.")
  * )
  */
-class TextimageTextOverlay extends GDTextimageOperationBase {
+class TextimageTextOverlay extends GDImageToolkitOperationBase {
+
+  use TextimageOperationTrait;
+  use GDOperationTrait;
 
   /**
    * {@inheritdoc}
diff --git a/src/Plugin/ImageToolkit/Operation/gd/TextimageTextToWrapper.php b/src/Plugin/ImageToolkit/Operation/gd/TextimageTextToWrapper.php
index 2bea64d..9845551 100644
--- a/src/Plugin/ImageToolkit/Operation/gd/TextimageTextToWrapper.php
+++ b/src/Plugin/ImageToolkit/Operation/gd/TextimageTextToWrapper.php
@@ -9,10 +9,13 @@ namespace Drupal\textimage\Plugin\ImageToolkit\Operation\gd;
 
 use Drupal\Component\Utility\Unicode;
 use Drupal\Core\Image\ImageInterface;
+use Drupal\system\Plugin\ImageToolkit\Operation\gd\GDImageToolkitOperationBase;
 use Drupal\image_effects\Component\ColorUtility;
-use Drupal\textimage\Component\Rectangle;
+use Drupal\image_effects\Component\PositionedRectangle;
+use Drupal\image_effects\Plugin\ImageToolkit\Operation\gd\GDOperationTrait;
 use Drupal\textimage\Component\TextUtility;
 use Drupal\textimage\Plugin\ImageToolkit\Operation\TextimageTextToWrapperTrait;
+use Drupal\textimage\Plugin\ImageToolkit\Operation\TextimageOperationTrait;
 
 /**
  * Defines Textimage GD2 text-to-wrapper operation.
@@ -25,8 +28,10 @@ use Drupal\textimage\Plugin\ImageToolkit\Operation\TextimageTextToWrapperTrait;
  *   description = @Translation("Overlays text over a GD resource.")
  * )
  */
-class TextimageTextToWrapper extends GDTextimageOperationBase {
+class TextimageTextToWrapper extends GDImageToolkitOperationBase {
 
+  use TextimageOperationTrait;
+  use GDOperationTrait;
   use TextimageTextToWrapperTrait;
 
   /**
@@ -103,12 +108,12 @@ class TextimageTextToWrapper extends GDTextimageOperationBase {
     $inner_box_height = ($height_info['height'] * $num_lines) + ($arguments['text']['line_spacing'] * ($num_lines - 1));
 
     // Get outer box.
-    $outer_rect = new Rectangle($inner_box_width + $arguments['layout']['padding_right'] + $arguments['layout']['padding_left'], $inner_box_height + $arguments['layout']['padding_top'] + $arguments['layout']['padding_bottom']);
+    $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 Rectangle($inner_box_width, $inner_box_height);
+    $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());
@@ -132,7 +137,7 @@ class TextimageTextToWrapper extends GDTextimageOperationBase {
         'rectangle' => $outer_rect,
         'fill_color' => $arguments['layout']['background_color'],
       );
-      $this->getToolkit()->apply('textimage_draw_rectangle', $data_rectangle);
+      $this->getToolkit()->apply('draw_rectangle', $data_rectangle);
     }
 
     // In debug mode, visually display the text boxes.
@@ -143,20 +148,20 @@ class TextimageTextToWrapper extends GDTextimageOperationBase {
         'border_color' => $arguments['layout']['background_color'] ?: '#FFFFFF',
         'border_color_luma' => TRUE,
       );
-      $this->getToolkit()->apply('textimage_draw_rectangle', $data);
+      $this->getToolkit()->apply('draw_rectangle', $data);
       // Outer box.
       $data = array(
         'rectangle' => $outer_rect,
         'border_color' => $arguments['layout']['background_color'] ?: '#FFFFFF',
         'border_color_luma' => TRUE,
       );
-      $this->getToolkit()->apply('textimage_draw_rectangle', $data);
+      $this->getToolkit()->apply('draw_rectangle', $data);
       // Wrapper.
       $data = array(
-        'rectangle' => new Rectangle($this->getToolkit()->getWidth(), $this->getToolkit()->getHeight()),
+        'rectangle' => new PositionedRectangle($this->getToolkit()->getWidth(), $this->getToolkit()->getHeight()),
         'border_color' => '#000000',
       );
-      $this->getToolkit()->apply('textimage_draw_rectangle', $data);
+      $this->getToolkit()->apply('draw_rectangle', $data);
     }
 
     // Process each of the text lines.
@@ -164,7 +169,7 @@ class TextimageTextToWrapper extends GDTextimageOperationBase {
     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 Rectangle($text_line_width, $line_height);
+      $text_line_rect = new PositionedRectangle($text_line_width, $line_height);
       $text_line_rect->setPoint('basepoint', $height_info['basepoint']);
 
       // Manage text alignment within the line.
@@ -278,8 +283,8 @@ class TextimageTextToWrapper extends GDTextimageOperationBase {
    *
    * Credit to Ruquay K Calloway
    *
-   * @param \Drupal\textimage\Component\Rectangle $rect
-   *   A Rectangle object, including basepoint.
+   * @param \Drupal\image_effects\Component\PositionedRectangle $rect
+   *   A PositionedRectangle object, including basepoint.
    * @param string $rgba
    *   RGBA color of the rectangle.
    * @param bool $luma
@@ -287,7 +292,7 @@ class TextimageTextToWrapper extends GDTextimageOperationBase {
    *
    * @see http://ruquay.com/sandbox/imagettf
    */
-  protected function drawDebugBox(Rectangle $rect, $rgba, $luma = FALSE) {
+  protected function drawDebugBox(PositionedRectangle $rect, $rgba, $luma = FALSE) {
 
     // Check color.
     if (!$rgba) {
@@ -305,7 +310,7 @@ class TextimageTextToWrapper extends GDTextimageOperationBase {
       'rectangle' => $rect,
       'border_color' => $rgba,
     );
-    $this->getToolkit()->apply('textimage_draw_rectangle', $data);
+    $this->getToolkit()->apply('draw_rectangle', $data);
 
     // Draw diagonal.
     $data = array(
diff --git a/src/Plugin/ImageToolkit/Operation/imagemagick/ImagemagickTextimageOperationBase.php b/src/Plugin/ImageToolkit/Operation/imagemagick/ImagemagickTextimageOperationBase.php
deleted file mode 100644
index 5e27115..0000000
--- a/src/Plugin/ImageToolkit/Operation/imagemagick/ImagemagickTextimageOperationBase.php
+++ /dev/null
@@ -1,40 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\textimage\Plugin\ImageToolkit\Operation\imagemagick\ImagemagickTextimageOperationBase.
- */
-
-namespace Drupal\textimage\Plugin\ImageToolkit\Operation\imagemagick;
-
-use Drupal\imagemagick\Plugin\ImageToolkit\Operation\imagemagick\ImagemagickImageToolkitOperationBase;
-use Drupal\textimage\Plugin\ImageToolkit\Operation\TextimageOperationTrait;
-
-/**
- * Base class for Textimage Imagemagick image toolkit operations.
- */
-abstract class ImagemagickTextimageOperationBase extends ImagemagickImageToolkitOperationBase {
-
-  use TextimageOperationTrait;
-
-  /**
-   * The format mapper service.
-   *
-   * @var \Drupal\imagemagick\ImagemagickFormatMapperInterface
-   */
-  protected $formatMapper;
-
-  /**
-   * Returns the format mapper service.
-   *
-   * @return \Drupal\imagemagick\ImagemagickFormatMapperInterface
-   *   The format mapper service.
-   */
-  protected function getFormatMapper() {
-    if (!$this->formatMapper) {
-      $this->formatMapper = \Drupal::service('imagemagick.format_mapper');
-    }
-    return $this->formatMapper;
-  }
-
-}
diff --git a/src/Plugin/ImageToolkit/Operation/imagemagick/TextimageDefineCanvas.php b/src/Plugin/ImageToolkit/Operation/imagemagick/TextimageDefineCanvas.php
deleted file mode 100644
index bbdb76c..0000000
--- a/src/Plugin/ImageToolkit/Operation/imagemagick/TextimageDefineCanvas.php
+++ /dev/null
@@ -1,69 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\textimage\Plugin\ImageToolkit\Operation\imagemagick\TextimageDefineCanvas.
- */
-
-namespace Drupal\textimage\Plugin\ImageToolkit\Operation\imagemagick;
-
-use Drupal\textimage\Plugin\ImageToolkit\Operation\TextimageDefineCanvasTrait;
-
-/**
- * Defines Textimage Imagemagick define canvas operation.
- *
- * Much of the code in this class is taken from the imagecache_action module
- * for Drupal 7.
- *
- * @ImageToolkitOperation(
- *   id = "textimage_imagemagick_textimage_define_canvas",
- *   toolkit = "imagemagick",
- *   operation = "textimage_define_canvas",
- *   label = @Translation("Textimage define canvas image"),
- *   description = @Translation("Defines a canvas for the image.")
- * )
- */
-class TextimageDefineCanvas extends ImagemagickTextimageOperationBase {
-
-  use TextimageDefineCanvasTrait;
-
-  /**
-   * {@inheritdoc}
-   */
-  protected function execute(array $arguments) {
-    $targetsize = $arguments['targetsize'];
-
-    // Calculate geometry.
-    $geometry = sprintf('%dx%d', $targetsize['width'], $targetsize['height']);
-    if ($targetsize['left'] || $targetsize['top']) {
-      $geometry .= sprintf('%+d%+d', -$targetsize['left'], -$targetsize['top']);
-    }
-
-    // Determine background.
-    if ($arguments['background_color']) {
-      $bg = '-background ' . $this->getToolkit()->escapeShellArg($arguments['background_color']);
-    }
-    else {
-      $format = $this->getToolkit()->getDestinationFormat() ?: $this->getToolkit()->getSourceFormat();
-      $mime_type = $this->getFormatMapper()->getMimeTypeFromFormat($format);
-      if ($mime_type === 'image/jpeg') {
-        // JPEG does not allow transparency. Set to white. @todo allow to be configurable.
-        $bg = '-background ' . $this->getToolkit()->escapeShellArg('#FFFFFF');
-      }
-      else {
-        $bg = '-background transparent';
-      }
-    }
-
-    // Add argument.
-    $this->getToolkit()->addArgument("-gravity none {$bg} -compose src-over -extent {$geometry}");
-
-    // Set dimensions.
-    $this->getToolkit()
-      ->setWidth($targetsize['width'])
-      ->setHeight($targetsize['height']);
-
-    return TRUE;
-  }
-
-}
diff --git a/src/Plugin/ImageToolkit/Operation/imagemagick/TextimageDrawRectangle.php b/src/Plugin/ImageToolkit/Operation/imagemagick/TextimageDrawRectangle.php
deleted file mode 100644
index 213372b..0000000
--- a/src/Plugin/ImageToolkit/Operation/imagemagick/TextimageDrawRectangle.php
+++ /dev/null
@@ -1,47 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\textimage\Plugin\ImageToolkit\Operation\imagemagick\TextimageDrawRectangle.
- */
-
-namespace Drupal\textimage\Plugin\ImageToolkit\Operation\imagemagick;
-
-use Drupal\textimage\Plugin\ImageToolkit\Operation\TextimageDrawRectangleTrait;
-
-/**
- * Defines Textimage Imagemagick draw rectangle operation.
- *
- * @ImageToolkitOperation(
- *   id = "textimage_imagemagick_textimage_draw_rectangle",
- *   toolkit = "imagemagick",
- *   operation = "textimage_draw_rectangle",
- *   label = @Translation("Textimage Draw Rectangle"),
- *   description = @Translation("Draws on the image a rectangle, optionally filling it in with a specified color.")
- * )
- */
-class TextimageDrawRectangle extends ImagemagickTextimageOperationBase {
-
-  use TextimageDrawRectangleTrait;
-
-  /**
-   * {@inheritdoc}
-   */
-  protected function execute(array $arguments) {
-    $arg = '';
-    if ($arguments['fill_color']) {
-      $arg .= '-fill ' . $this->getToolkit()->escapeShellArg($arguments['fill_color']);
-    }
-    else {
-      $arg .= '-fill none';
-    }
-    if ($arguments['border_color']) {
-      $arg .= ' -stroke ' . $this->getToolkit()->escapeShellArg($arguments['border_color']) . ' -strokewidth 1';
-    }
-    $d = $arguments['rectangle']->getPoint('c_d');
-    $b = $arguments['rectangle']->getPoint('c_b');
-    $this->getToolkit()->addArgument($arg . ' -draw ' . $this->getToolkit()->escapeShellArg("rectangle {$d[0]},{$d[1]} {$b[0]},{$b[1]}"));
-    return TRUE;
-  }
-
-}
diff --git a/src/Plugin/ImageToolkit/Operation/imagemagick/TextimageOverlay.php b/src/Plugin/ImageToolkit/Operation/imagemagick/TextimageOverlay.php
deleted file mode 100644
index 25c9e74..0000000
--- a/src/Plugin/ImageToolkit/Operation/imagemagick/TextimageOverlay.php
+++ /dev/null
@@ -1,54 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\textimage\Plugin\ImageToolkit\Operation\imagemagick\TextimageOverlay.
- */
-
-namespace Drupal\textimage\Plugin\ImageToolkit\Operation\imagemagick;
-
-use Drupal\textimage\Plugin\ImageToolkit\Operation\TextimageOverlayTrait;
-
-/**
- * Defines Textimage Imagemagick overlay operation.
- *
- * Much of the code in this class is taken from the imagecache_action module
- * for Drupal 7.
- *
- * @ImageToolkitOperation(
- *   id = "textimage_imagemagick_textimage_overlay",
- *   toolkit = "imagemagick",
- *   operation = "textimage_overlay",
- *   label = @Translation("Textimage overlay image"),
- *   description = @Translation("Overlays an image over or under the current.")
- * )
- */
-class TextimageOverlay extends ImagemagickTextimageOperationBase {
-
-  use TextimageOverlayTrait;
-
-  /**
-   * {@inheritdoc}
-   */
-  protected function execute(array $arguments) {
-    // In Imagemagick terms:
-    // - $this is the destination (the image being constructed).
-    // - $arguments['layer'] is the source (the source of the current
-    //   operation).
-    // Add the layer image to the Imagemagick command line.
-    if (!$source_path = $arguments['layer']->getToolkit()->getSourceLocalPath()) {
-      return FALSE;
-    }
-    $source = $this->getToolkit()->escapeShellArg($source_path);
-
-    // Set offset. Offset arguments require a sign in front.
-    $x = ($arguments['x'] >= 0) ? "+" . $arguments['x'] : $arguments['x'];
-    $y = ($arguments['y'] >= 0) ? "+" . $arguments['y'] : $arguments['y'];
-
-    // Add argument, compose the layer with the destination.
-    $this->getToolkit()->addArgument("-gravity none {$source} -geometry {$x}{$y} -compose src-over -composite");
-
-    return TRUE;
-  }
-
-}
diff --git a/src/Plugin/ImageToolkit/Operation/imagemagick/TextimageReplaceImage.php b/src/Plugin/ImageToolkit/Operation/imagemagick/TextimageReplaceImage.php
index 0183da4..60afe84 100644
--- a/src/Plugin/ImageToolkit/Operation/imagemagick/TextimageReplaceImage.php
+++ b/src/Plugin/ImageToolkit/Operation/imagemagick/TextimageReplaceImage.php
@@ -7,6 +7,9 @@
 
 namespace Drupal\textimage\Plugin\ImageToolkit\Operation\imagemagick;
 
+use Drupal\image_effects\Plugin\ImageToolkit\Operation\imagemagick\ImagemagickOperationTrait;
+use Drupal\imagemagick\Plugin\ImageToolkit\Operation\imagemagick\ImagemagickImageToolkitOperationBase;
+use Drupal\textimage\Plugin\ImageToolkit\Operation\TextimageOperationTrait;
 use Drupal\textimage\Plugin\ImageToolkit\Operation\TextimageReplaceImageTrait;
 
 /**
@@ -20,8 +23,10 @@ use Drupal\textimage\Plugin\ImageToolkit\Operation\TextimageReplaceImageTrait;
  *   description = @Translation("Replace the current image with another one.")
  * )
  */
-class TextimageReplaceImage extends ImagemagickTextimageOperationBase {
+class TextimageReplaceImage extends ImagemagickImageToolkitOperationBase {
 
+  use ImagemagickOperationTrait;
+  use TextimageOperationTrait;
   use TextimageReplaceImageTrait;
 
   /**
diff --git a/src/Plugin/ImageToolkit/Operation/imagemagick/TextimageSetGifTransparentColor.php b/src/Plugin/ImageToolkit/Operation/imagemagick/TextimageSetGifTransparentColor.php
index ea759bb..391c3ef 100644
--- a/src/Plugin/ImageToolkit/Operation/imagemagick/TextimageSetGifTransparentColor.php
+++ b/src/Plugin/ImageToolkit/Operation/imagemagick/TextimageSetGifTransparentColor.php
@@ -7,6 +7,9 @@
 
 namespace Drupal\textimage\Plugin\ImageToolkit\Operation\imagemagick;
 
+use Drupal\image_effects\Plugin\ImageToolkit\Operation\imagemagick\ImagemagickOperationTrait;
+use Drupal\imagemagick\Plugin\ImageToolkit\Operation\imagemagick\ImagemagickImageToolkitOperationBase;
+use Drupal\textimage\Plugin\ImageToolkit\Operation\TextimageOperationTrait;
 use Drupal\textimage\Plugin\ImageToolkit\Operation\TextimageSetGifTransparentColorTrait;
 
 /**
@@ -20,8 +23,10 @@ use Drupal\textimage\Plugin\ImageToolkit\Operation\TextimageSetGifTransparentCol
  *   description = @Translation("Set the image transparent color for GIF images.")
  * )
  */
-class TextimageSetGifTransparentColor extends ImagemagickTextimageOperationBase {
+class TextimageSetGifTransparentColor extends ImagemagickImageToolkitOperationBase {
 
+  use ImagemagickOperationTrait;
+  use TextimageOperationTrait;
   use TextimageSetGifTransparentColorTrait;
 
   /**
diff --git a/src/Plugin/ImageToolkit/Operation/imagemagick/TextimageTextToWrapper.php b/src/Plugin/ImageToolkit/Operation/imagemagick/TextimageTextToWrapper.php
index 6e4db86..49dde2b 100644
--- a/src/Plugin/ImageToolkit/Operation/imagemagick/TextimageTextToWrapper.php
+++ b/src/Plugin/ImageToolkit/Operation/imagemagick/TextimageTextToWrapper.php
@@ -7,6 +7,9 @@
 
 namespace Drupal\textimage\Plugin\ImageToolkit\Operation\imagemagick;
 
+use Drupal\image_effects\Plugin\ImageToolkit\Operation\imagemagick\ImagemagickOperationTrait;
+use Drupal\imagemagick\Plugin\ImageToolkit\Operation\imagemagick\ImagemagickImageToolkitOperationBase;
+use Drupal\textimage\Plugin\ImageToolkit\Operation\TextimageOperationTrait;
 use Drupal\textimage\Plugin\ImageToolkit\Operation\TextimageTextToWrapperTrait;
 
 /**
@@ -20,8 +23,10 @@ use Drupal\textimage\Plugin\ImageToolkit\Operation\TextimageTextToWrapperTrait;
  *   description = @Translation("Overlays text over an image.")
  * )
  */
-class TextimageTextToWrapper extends ImagemagickTextimageOperationBase {
+class TextimageTextToWrapper extends ImagemagickImageToolkitOperationBase {
 
+  use ImagemagickOperationTrait;
+  use TextimageOperationTrait;
   use TextimageTextToWrapperTrait;
 
   /**
