Skip to content
Snippets Groups Projects
Unverified Commit ffa74702 authored by Dima Storozhuk's avatar Dima Storozhuk
Browse files

#3516140, #3516141, #3483736 Enhance watermark generation and update hook implementations


Replaced `hook_entity_type_build()` with `hook_entity_type_alter()` and added `hook_implements_alter()` to modify hook priorities. Introduced new methods for watermark generation, font handling, and text configuration in the custom `ImageStyle` class. Updated README with module conflicts and ensured compatibility with Drupal 11.

Signed-off-by: default avatarDmytro Storozhuk <dima@itech4web.com>
parent 6a916d65
No related branches found
No related tags found
No related merge requests found
......@@ -22,3 +22,9 @@ No additional configuration required. After enabling, this module replaces the `
### Danger
**Do not use on production!** During the installation process, the module removes the styles folder in your site's public folder.
### Conflicts
This module conflicts with [imageapi_optimize](https://www.drupal.org/project/imageapi_optimize) module, because both modules overrides the entity type `image_style` class.
When enabled - `ImageStyle` class from `responsive_image_debugger` will be used.
So this module should be used on dev/testing environment for debugging purposes only.
It will also conflict with any other modules which overrides the `image_style` entity type class.
......@@ -2,7 +2,6 @@ name: Responsive Image Debugger
type: module
description: Print the name of the image style directly on the image displayed.
package: Image
core: 8.x
core_version_requirement: ^8 || ^9 || ^10
core_version_requirement: ^8 || ^9 || ^10 || ^11
dependencies:
- image_effects:image_effects
......@@ -9,6 +9,7 @@ use Drupal\Core\File\FileSystemInterface;
*/
function responsive_image_debugger_install() {
_responsive_image_debugger_remove_styles_folder();
module_set_weight('responsive_image_debugger', 100);
}
/**
......
......@@ -12,10 +12,26 @@
use Drupal\responsive_image_debugger\Entity\ImageStyle;
/**
* Implements hook__entity_type_build().
* Implements hook__entity_type_alter().
*/
function responsive_image_debugger_entity_type_build(&$entity_types) {
function responsive_image_debugger_entity_type_alter(array &$entity_types) {
if (isset($entity_types['image_style'])) {
$entity_types['image_style']->setClass(ImageStyle::class);
}
}
/**
* Implements hook_implements_alter().
*/
function responsive_image_debugger_implements_alter(&$implementations, $hook) {
// For example, push this module’s implementation to the end for hook_form_alter
if (in_array($hook, ['entity_type_alter', 'entity_type'])) {
$module = 'responsive_image_debugger';
if (isset($implementations[$module])) {
$weight = $implementations[$module];
unset($implementations[$module]);
// Re-add it to the end (highest weight)
$implementations[$module] = $weight;
}
}
}
......@@ -15,6 +15,117 @@ use Drupal\image\ImageEffectManager;
*/
class ImageStyle extends ImageStyleBase{
const BG_COLOR = '#FF00FF';
/**
* Generate a semi-transparent image with custom color and text.
*
* @param string $color - Hex color (e.g., '#FF00FF')
* @param string $text - Text to display on watermark
* @param int $width - Width of the watermark image
* @param int $height - Height of the watermark image
*
* @return string - Path to generated watermark image
*/
public function generateWatermark(string $color, string $text, int $width, int $height, Image $drupal_image): string {
$font_path = $this->getFont();
$font_size = $this->getFontSize($drupal_image);
$bbox = imagettfbbox($font_size, 0, $font_path, $text);
$text_width = $bbox[2] - $bbox[0];
$text_height = $bbox[1] - $bbox[7];
$image = imagecreatetruecolor($text_width + 50, $text_height + 50);
$text_color = imagecolorallocate($image, 0, 0, 0);
imagesavealpha($image, true);
$transparent_color = imagecolorallocatealpha($image, 0, 0, 0, 127);
imagefill($image, 0, 0, $transparent_color);
// Convert hex to RGBA
[$r, $g, $b] = sscanf($color, "#%02x%02x%02x");
$bg_color = imagecolorallocatealpha($image, $r, $g, $b, 80);
imagefilledrectangle($image, 0, 0, $width, $height, $bg_color);
// Add text
$x = 20;
$y = 50;
imagettftext($image, $font_size, 0, (int) $x, (int) $y, $text_color, $font_path, $text);
// Save watermark
$filename = 'public://watermark_' . time() . '.png';
imagepng($image, \Drupal::service('file_system')->realpath($filename));
imagedestroy($image);
return $filename;
}
/**
* Apply watermark to the center of the original image.
*
* @param string $watermarkPath - Path to the watermark image
* @param string $originalImagePath - Path to the original image
*
* @throws \Exception
*/
public function applyWatermark(string $watermarkPath, string $originalImagePath): void {
// Get image format from original file extension
$ext = strtolower(pathinfo($originalImagePath, PATHINFO_EXTENSION));
// Load original image
switch ($ext) {
case 'jpeg':
case 'jpg':
$original = imagecreatefromjpeg($originalImagePath);
break;
case 'png':
$original = imagecreatefrompng($originalImagePath);
break;
case 'gif':
$original = imagecreatefromgif($originalImagePath);
break;
default:
throw new \Exception("Unsupported image format: $ext");
}
// Load watermark
$watermark = imagecreatefrompng($watermarkPath);
// Calculate position to center the watermark
$orig_w = imagesx($original);
$orig_h = imagesy($original);
$wm_w = imagesx($watermark);
$wm_h = imagesy($watermark);
$x = ($orig_w - $wm_w) / 2;
$y = ($orig_h - $wm_h) / 2;
// Preserve alpha blending
imagealphablending($original, true);
imagesavealpha($original, true);
// Merge watermark
imagecopy($original, $watermark, (int) $x, (int) $y, 0, 0, $wm_w, $wm_h);
// Save image in original format
switch ($ext) {
case 'jpeg':
case 'jpg':
imagejpeg($original, $originalImagePath, 90);
break;
case 'png':
imagepng($original, $originalImagePath);
break;
case 'gif':
imagegif($original, $originalImagePath);
break;
}
// Clean up
imagedestroy($original);
imagedestroy($watermark);
}
/**
* {@inheritdoc}
*/
......@@ -38,8 +149,9 @@ class ImageStyle extends ImageStyleBase{
$effect->applyEffect($image);
}
/** @var \Drupal\image_effects\Plugin\ImageEffect\TextOverlayImageEffect $image_effect */
$image_effect = $this->buildDebugEffect($image);
$image_effect->applyEffect($image);
$effect_applied = $image_effect->applyEffect($image);
if (!$image->save($derivative_uri)) {
if (file_exists($derivative_uri)) {
......@@ -49,98 +161,129 @@ class ImageStyle extends ImageStyleBase{
return FALSE;
}
if (!$effect_applied) {
$watermark_path = $this->generateWatermark(self::BG_COLOR, $this->getText($image), 500, 200, $image);
$this->applyWatermark($watermark_path, $derivative_uri);
}
return TRUE;
}
/**
* Add imageapi_optimize module capability, in case it is enabled.
*
* @return string
*/
public function getPipeline() {
return '__default__';
}
/**
* Builds a debug image effect.
* Builds and returns a debug effect configuration for an image.
*
* @param Image $image - The image object for which the debug effect is to be applied.
*
* @return ImageEffectInterface
* The debug image effect.
* @return ImageEffectInterface - The configured image effect instance.
*/
private function buildDebugEffect(Image $image): ImageEffectInterface {
$module_path = \Drupal::service('extension.list.module')
->getPath('responsive_image_debugger');
$font = $module_path . '/fonts/MontserratMedium.otf';
$text_array = [
'width: ' . $image->getWidth(),
'height: ' . $image->getHeight(),
"style name: \n" . $this->getName(),
];
$text_string = implode("\n", $text_array);
$image->getWidth();
$font_size = 24;
if ($image->getWidth() >= 400) {
$font_size = $image->getWidth() * .02;
}
elseif ($image->getWidth() <= 400 && $image->getWidth() >= 200) {
$font_size = $image->getWidth() * .06;
}
elseif ($image->getWidth() <= 200) {
$font_size = $image->getWidth() * .04;
}
$configuration = [
'id' => 'image_effects_text_overlay',
'weight' => -1000,
'data' =>
[
'text_string' => $text_string,
'font' =>
[
'name' => 'Montserrat Medium',
'uri' => $font,
'size' => $font_size,
'data' => [
'text_string' => $this->getText($image),
'font' => [
'angle' => 0,
'color' => '#000000FF',
'stroke_mode' => 'outline',
'stroke_color' => '#000000FF',
'outline_top' => 0,
'outline_right' => 0,
'name' => 'Montserrat Medium',
'outline_bottom' => 0,
'outline_left' => 0,
'outline_right' => 0,
'outline_top' => 0,
'shadow_height' => 0,
'shadow_width' => 0,
'shadow_x_offset' => 1,
'shadow_y_offset' => 1,
'shadow_width' => 0,
'shadow_height' => 0,
'size' => $this->getFontSize($image),
'stroke_color' => '#000000FF',
'stroke_mode' => 'outline',
'uri' => $this->getFont(),
],
'layout' =>
[
'padding_top' => 10,
'padding_right' => 10,
'text' => [
'maximum_chars' => NULL,
'align' => 'left',
'case_format' => '',
'decode_entities' => TRUE,
'excess_chars_text' => '…',
'fixed_width' => FALSE,
'line_spacing' => 0,
'maximum_width' => $image->getWidth() - 5,
'strip_tags' => TRUE,
],
'layout' => [
'background_color' => self::BG_COLOR,
'extended_color' => '',
'overflow_action' => 'extend',
'padding_bottom' => 10,
'padding_left' => 10,
'x_pos' => 'center',
'y_pos' => 'center',
'padding_right' => 10,
'padding_top' => 10,
'x_offset' => 0,
'x_pos' => 'center',
'y_offset' => 0,
'background_color' => '#FF40FF80',
'overflow_action' => 'crop',
'extended_color' => '',
],
'text' =>
[
'strip_tags' => true,
'decode_entities' => true,
'maximum_chars' => NULL,
'excess_chars_text' => '…',
'maximum_width' => $image->getWidth() - 5,
'fixed_width' => false,
'align' => 'center',
'line_spacing' => 0,
'case_format' => '',
'y_pos' => 'center',
],
],
];
/** @var ImageEffectManager $image_effect_manager */
$image_effect_manager = \Drupal::service('plugin.manager.image.effect');
$image_effect = $image_effect_manager->createInstance('image_effects_text_overlay', $configuration);
return $image_effect_manager->createInstance('image_effects_text_overlay', $configuration);
}
/**
* Retrieve the file path to the font used for rendering text.
*
* @return string - The full file path to the font file.
*/
private function getFont(): string {
$module_path = \Drupal::service('extension.list.module')
->getPath('responsive_image_debugger');
return $module_path . '/fonts/MontserratMedium.otf';
}
/**
* Determines the font size based on the width of the given image.
*
* @param Image $image - The image object used to calculate the font size.
*
* @return int - The calculated font size as an integer.
*/
private function getFontSize(Image $image): int {
$font_size = 24;
if ($image->getWidth() >= 400) {
$font_size = $image->getWidth() * .02;
}
elseif ($image->getWidth() <= 400 && $image->getWidth() >= 200) {
$font_size = $image->getWidth() * .06;
}
elseif ($image->getWidth() <= 200) {
$font_size = $image->getWidth() * .04;
}
return (int) $font_size;
}
/**
* Generate a text description of the image properties.
*/
private function getText(Image $image): string {
$text_array = [
'width: ' . $image->getWidth(),
'height: ' . $image->getHeight(),
"style name: \n" . $this->getName(),
];
return $image_effect;
return implode("\n", $text_array);
}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please to comment