832 lines
		
	
	
		
			20 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			832 lines
		
	
	
		
			20 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
| <?php
 | |
| 
 | |
| /**
 | |
|  * @author          Tassos Marinos <info@tassos.gr>
 | |
|  * @link            https://www.tassos.gr
 | |
|  * @copyright       Copyright © 2024 Tassos All Rights Reserved
 | |
|  * @license         GNU GPLv3 <http://www.gnu.org/licenses/gpl.html> or later
 | |
| */
 | |
| 
 | |
| namespace NRFramework;
 | |
| 
 | |
| // No direct access
 | |
| defined('_JEXEC') or die;
 | |
| 
 | |
| use NRFramework\Mimes;
 | |
| use NRFramework\File;
 | |
| use Joomla\CMS\Image\Image as JoomlaImage;
 | |
| use Joomla\Filesystem\Path;
 | |
| use Joomla\CMS\Factory;
 | |
| use Joomla\CMS\Uri\Uri;
 | |
| 
 | |
| class Image
 | |
| {
 | |
| 	/**
 | |
| 	 * Resize an image.
 | |
| 	 * 
 | |
| 	 * @param   string   $source
 | |
| 	 * @param   string   $width
 | |
| 	 * @param   string   $height
 | |
| 	 * @param   integer  $quality
 | |
| 	 * @param   string   $mode
 | |
| 	 * @param   boolean  $unique_filename
 | |
| 	 * @param   boolean  $fix_orientation
 | |
| 	 * @param   string 	 $gif_mode			If the uploaded image is a GIF image, how will it be copied? Options: "copy" source, "resize" source
 | |
| 	 * 
 | |
| 	 * @return  mixed
 | |
| 	 */
 | |
| 	public static function resize($source, $width, $height, $quality = 70, $mode = 'crop', $destination = '', $unique_filename = false, $fix_orientation = true, $gif_mode = 'copy')
 | |
| 	{
 | |
| 		$width = (int) $width;
 | |
| 		$height = (int) $height;
 | |
| 
 | |
| 		// Destination file name
 | |
| 		$destination = empty($destination) ? $source : $destination;
 | |
| 
 | |
| 		// size must be WIDTHxHEIGHT
 | |
| 		$size = $width . 'x' . $height;
 | |
| 
 | |
| 		switch ($mode)
 | |
| 		{
 | |
| 			// Crop and Resize
 | |
| 			case 'crop':
 | |
| 				$mode = 5;
 | |
| 				break;
 | |
| 			// Scale Fill
 | |
| 			case 'stretch':
 | |
| 				$mode = 1;
 | |
| 				break;
 | |
| 			// Fit, will fill empty space with black
 | |
| 			case 'fit':
 | |
| 				$mode = 6;
 | |
| 				break;
 | |
| 			default:
 | |
| 				$mode = 5;
 | |
| 				break;
 | |
| 		}
 | |
| 
 | |
| 		try {
 | |
| 			$image = new JoomlaImage($source);
 | |
| 
 | |
| 			$origWidth = $image->getWidth();
 | |
| 			$origHeight = $image->getHeight();
 | |
| 
 | |
| 			/**
 | |
| 			 * If the image width is less than the given width,
 | |
| 			 * set the image width we are resizing to the image's width.
 | |
| 			 */
 | |
| 			if ($origWidth < $width)
 | |
| 			{
 | |
| 				$size = $origWidth . 'x';
 | |
| 
 | |
| 				if ($origHeight < $height)
 | |
| 				{
 | |
| 					$size .= $origHeight;
 | |
| 				}
 | |
| 				else
 | |
| 				{
 | |
| 					$size .= $height;
 | |
| 				}
 | |
| 			}
 | |
| 			else if ($origHeight < $height)
 | |
| 			{
 | |
| 				$prefix = $width;
 | |
| 
 | |
| 				if ($origWidth < $width)
 | |
| 				{
 | |
| 					$prefix = $origWidth;
 | |
| 				}
 | |
| 				
 | |
| 				$size = $prefix . 'x' . $origHeight;
 | |
| 			}
 | |
| 
 | |
| 			// Fix orientation
 | |
| 			if ($fix_orientation)
 | |
| 			{
 | |
| 				self::fixOrientation($image);
 | |
| 			}
 | |
| 
 | |
| 			// Determine the MIME of the original file to get the proper type
 | |
| 			$mime = Mimes::detectFileType($source);
 | |
| 			
 | |
| 			// PNG images should not have a quality value
 | |
| 			$options = $mime == 'image/png' ? ['quality' => 9] : ['quality' => $quality];
 | |
| 			
 | |
| 			// Get the image type
 | |
| 			$image_type = self::getImageType($mime);
 | |
| 
 | |
| 			if ($unique_filename)
 | |
| 			{
 | |
| 				// Make destination file unique
 | |
| 				File::uniquefy($destination);
 | |
| 			}
 | |
| 
 | |
| 			$destination = Path::clean($destination);
 | |
| 
 | |
| 			// Resize image
 | |
| 			if ($mime === 'image/gif')
 | |
| 			{
 | |
| 				if ($gif_mode === 'copy')
 | |
| 				{
 | |
| 					File::copy($source, $destination, true);
 | |
| 				}
 | |
| 				else
 | |
| 				{
 | |
| 					foreach ($image->generateThumbs($size, $mode) as $thumb)
 | |
| 					{
 | |
| 						$thumb->toFile($destination, $image_type, $options);
 | |
| 					}
 | |
| 				}
 | |
| 			}
 | |
| 			else
 | |
| 			{
 | |
| 				foreach ($image->generateThumbs($size, $mode) as $thumb)
 | |
| 				{
 | |
| 					$thumb->toFile($destination, $image_type, $options);
 | |
| 				}
 | |
| 			}
 | |
| 
 | |
| 			return $destination;
 | |
| 		} catch(\Exception $e) {}
 | |
| 
 | |
| 		return false;
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Resizes an image by height.
 | |
| 	 * 
 | |
| 	 * @param   string  $src
 | |
| 	 * @param   string  $height
 | |
| 	 * @param   string  $destination
 | |
| 	 * @param   int	 	$quality
 | |
| 	 * @param   bool 	$unique_filename
 | |
| 	 * @param   bool 	$fix_orientation
 | |
| 	 * @param   string 	$gif_mode			If the uploaded image is a GIF image, how will it be copied? Options: "copy" source, "resize" source
 | |
| 	 * 
 | |
| 	 * @return  bool
 | |
| 	 */
 | |
| 	public static function resizeByHeight($src, $height, $destination = null, $quality = 70, $unique_filename = false, $fix_orientation = true, $gif_mode = 'copy')
 | |
| 	{
 | |
| 		$height = (int) $height;
 | |
| 		
 | |
| 		// Create a new JImage object from the source image path
 | |
| 		$image = new JoomlaImage($src);
 | |
| 
 | |
| 		// Fix orientation
 | |
| 		if ($fix_orientation)
 | |
| 		{
 | |
| 			self::fixOrientation($image);
 | |
| 		}
 | |
| 	  
 | |
| 		// Determine the MIME of the original file to get the proper type
 | |
| 		$mime = Mimes::detectFileType($src);
 | |
| 
 | |
| 		// Get the image type
 | |
| 		$image_type = self::getImageType($mime);
 | |
| 
 | |
| 		// Output file name
 | |
| 		$destination = empty($destination) ? $src : $destination;
 | |
| 		
 | |
| 		if ($unique_filename)
 | |
| 		{
 | |
| 			// Make destination file unique
 | |
| 			File::uniquefy($destination);
 | |
| 		}
 | |
| 
 | |
| 		$destination = Path::clean($destination);
 | |
| 		
 | |
| 		// PNG images should not have a quality value
 | |
| 		$options = $mime == 'image/png' ? ['quality' => 9] : ['quality' => $quality];
 | |
| 
 | |
| 		// Get the original width and height of the image
 | |
| 		$origWidth = $image->getWidth();
 | |
| 		$origHeight = $image->getHeight();
 | |
| 	  
 | |
| 		// Calculate the new width based on the desired height
 | |
| 		$newWidth = ($origWidth / $origHeight) * $height;
 | |
| 
 | |
| 		// Resize image
 | |
| 		if ($mime === 'image/gif')
 | |
| 		{
 | |
| 			if ($gif_mode === 'copy')
 | |
| 			{
 | |
| 				File::copy($source, $destination, true);
 | |
| 			}
 | |
| 			else
 | |
| 			{
 | |
| 				$resizedImage = $image->resize($newWidth, $height);
 | |
| 				$resizedImage->toFile($destination, $image_type, $options);
 | |
| 			}
 | |
| 		}
 | |
| 		else
 | |
| 		{
 | |
| 			$resizedImage = $image->resize($newWidth, $height);
 | |
| 			$resizedImage->toFile($destination, $image_type, $options);
 | |
| 		}
 | |
| 	  
 | |
| 		// Return true if the image was successfully resized and saved, false otherwise
 | |
| 		return $destination;
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Resizes an image by keeping the aspect ratio
 | |
| 	 * 
 | |
| 	 * @param   string   $source
 | |
| 	 * @param   array    $width
 | |
| 	 * @param   integer  $quality
 | |
| 	 * @param   array    $destination
 | |
| 	 * @param   boolean  $unique_filename
 | |
| 	 * @param   boolean  $fix_orientation
 | |
| 	 * @param   string 	 $gif_mode			If the uploaded image is a GIF image, how will it be copied? Options: "copy" source, "resize" source
 | |
| 	 * 
 | |
| 	 * @return  boolean
 | |
| 	 */
 | |
| 	public static function resizeAndKeepAspectRatio($source, $width, $quality = 70, $destination = '', $unique_filename = false, $fix_orientation = true, $gif_mode = 'copy')
 | |
| 	{
 | |
| 		// Ensure we have received valid image dimensions
 | |
| 		if (!count($image_dimensions = getimagesize($source)))
 | |
| 		{
 | |
| 			return false;
 | |
| 		}
 | |
| 
 | |
| 		// Get the image width
 | |
| 		if (!$uploaded_image_width = (int) $image_dimensions[0])
 | |
| 		{
 | |
| 			return false;
 | |
| 		}
 | |
| 
 | |
| 		// Get the image height
 | |
| 		if (!$uploaded_image_height = (int) $image_dimensions[1])
 | |
| 		{
 | |
| 			return false;
 | |
| 		}
 | |
| 
 | |
| 		$width = (int) $width;
 | |
| 
 | |
| 		/**
 | |
| 		 * If the image width is less than the given width,
 | |
| 		 * set the image width we are resizing to the image's width.
 | |
| 		 */
 | |
| 		if ($uploaded_image_width < (int) $width)
 | |
| 		{
 | |
| 			$width = $uploaded_image_width;
 | |
| 		}
 | |
| 
 | |
| 		// Determine the MIME of the original file to get the proper type
 | |
| 		$mime = Mimes::detectFileType($source);
 | |
| 
 | |
| 		// PNG images should not have a quality value
 | |
| 		$options = $mime == 'image/png' ? ['quality' => 9] : ['quality' => $quality];
 | |
| 
 | |
| 		// Get the image type
 | |
| 		$image_type = self::getImageType($mime);
 | |
| 
 | |
| 		try {
 | |
| 			// Get image object
 | |
| 			$image = new JoomlaImage($source);
 | |
| 
 | |
| 			// Fix orientation
 | |
| 			if ($fix_orientation)
 | |
| 			{
 | |
| 				self::fixOrientation($image);
 | |
| 			}
 | |
| 
 | |
| 			// Calculate aspect ratio
 | |
| 			$ratio = $uploaded_image_width / $uploaded_image_height;
 | |
| 	
 | |
| 			// Get new height based on aspect ratio
 | |
| 			$targetHeight = $width / $ratio;
 | |
| 	
 | |
| 			// Output file name
 | |
| 			$destination = empty($destination) ? $source : $destination;
 | |
| 
 | |
| 			if ($unique_filename)
 | |
| 			{
 | |
| 				// Make destination file unique
 | |
| 				File::uniquefy($destination);
 | |
| 			}
 | |
| 	
 | |
| 			$destination = Path::clean($destination);
 | |
| 			
 | |
| 			// Resize image
 | |
| 			if ($mime === 'image/gif')
 | |
| 			{
 | |
| 				if ($gif_mode === 'copy')
 | |
| 				{
 | |
| 					File::copy($source, $destination, true);
 | |
| 				}
 | |
| 				else
 | |
| 				{
 | |
| 					$resizedImage = $image->resize($width, $targetHeight, true);
 | |
| 					$resizedImage->toFile($destination, $image_type, $options);
 | |
| 				}
 | |
| 			}
 | |
| 			else
 | |
| 			{
 | |
| 				$resizedImage = $image->resize($width, $targetHeight, true);
 | |
| 				$resizedImage->toFile($destination, $image_type, $options);
 | |
| 			}
 | |
| 
 | |
| 			return $destination;
 | |
| 		} catch(\Exception $e) {}
 | |
| 
 | |
| 		return false;
 | |
| 	}
 | |
| 
 | |
| 	public static function resizeByWidthOrHeight($source, $width, $height, $quality = 80, $destination = '', $resize_method = 'crop', $unique_filename = false, $fix_orientation = true)
 | |
| 	{
 | |
| 		$resized_image = null;
 | |
| 		
 | |
| 		// If width is null, and we have height set, we are resizing by height
 | |
| 		if (is_null($width) && $height && !is_null($height))
 | |
| 		{
 | |
| 			$resized_image = Image::resizeByHeight($source, $height, $destination, 80, $unique_filename, $fix_orientation);
 | |
| 		}
 | |
| 		else
 | |
| 		{
 | |
| 			/**
 | |
| 			 * If height is zero, then we suppose we want to keep aspect ratio.
 | |
| 			 * 
 | |
| 			 * Resize with width & height: If height is not set
 | |
| 			 * Resize and keep aspect ratio: If height is set
 | |
| 			 */
 | |
| 			$resized_image = $height && !is_null($height)
 | |
| 				?
 | |
| 				Image::resize($source, $width, $height, 80, $resize_method, $destination, $unique_filename, $fix_orientation)
 | |
| 				:
 | |
| 				Image::resizeAndKeepAspectRatio($source, $width, 80, $destination, $unique_filename, $fix_orientation);
 | |
| 
 | |
| 		}
 | |
| 
 | |
| 		return $resized_image;
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Returns the orientation of the image.
 | |
| 	 * 
 | |
| 	 * @param   string  $path
 | |
| 	 * 
 | |
| 	 * @return  int
 | |
| 	 */
 | |
| 	public static function getOrientation($path)
 | |
| 	{
 | |
| 		if (!$exif = @exif_read_data($path))
 | |
| 		{
 | |
| 			return;
 | |
| 		}
 | |
| 
 | |
| 		return intval(@$exif['Orientation']);
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Fixes the orientation of the generated image and ensures it appears with the same orientation as the source.
 | |
| 	 * 
 | |
| 	 * @param    string  $path
 | |
| 	 * @param    int     $orientation
 | |
| 	 * 
 | |
| 	 * @return   void
 | |
| 	 */
 | |
| 	public static function fixOrientation(&$image, $orientation = null)
 | |
| 	{
 | |
| 		$orientation = self::getOrientation($image->getPath());
 | |
| 		
 | |
| 		if(!in_array($orientation, [3, 6, 8]))
 | |
| 		{
 | |
| 			return;
 | |
| 		}
 | |
| 
 | |
| 		switch ($orientation)
 | |
| 		{
 | |
|             case 3:
 | |
|                 $image->rotate(180, -1, false);
 | |
|                 break;
 | |
| 
 | |
|             case 6:
 | |
|                 $image->rotate(270, -1, false);
 | |
|                 break;
 | |
| 
 | |
|             case 8:
 | |
|                 $image->rotate(90, -1, false);
 | |
|                 break;
 | |
| 		}
 | |
| 
 | |
| 		return true;
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Returns the image type based on its mime type
 | |
| 	 * 
 | |
| 	 * @param   string  $mime
 | |
| 	 * 
 | |
| 	 * @return  int
 | |
| 	 */
 | |
| 	public static function getImageType($mime)
 | |
| 	{
 | |
| 		switch ($mime)
 | |
| 		{
 | |
| 			case 'image/png':
 | |
| 				return IMAGETYPE_PNG;
 | |
| 				break;
 | |
| 			case 'image/gif':
 | |
| 				return IMAGETYPE_GIF;
 | |
| 				break;
 | |
| 			case 'image/webp':
 | |
| 				return IMAGETYPE_WEBP;
 | |
| 				break;
 | |
| 			case 'image/jpeg':
 | |
| 			default:
 | |
| 				return IMAGETYPE_JPEG;
 | |
| 				break;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Creates a watermark from text.
 | |
| 	 * 
 | |
| 	 * @param   string   $text
 | |
| 	 * @param   integer  $font_size
 | |
| 	 * @param   integer  $opacity
 | |
| 	 * @param   string   $color
 | |
| 	 * @param   integer  $originalWidth
 | |
| 	 * @param   integer  $originalHeight
 | |
| 	 * 
 | |
| 	 * @return  object
 | |
| 	 */
 | |
| 	public static function createWatermarkText($text = '', $_font_size = 30, $opacity = 60, $color = '#ffffff', $originalWidth = null, $originalHeight = null)
 | |
| 	{
 | |
| 		$font = implode(DIRECTORY_SEPARATOR, [JPATH_SITE, 'media', 'plg_system_nrframework', 'font', 'arial.ttf']);
 | |
| 
 | |
| 		// Scale down font size based on the original image width or height
 | |
| 		$dimension = $originalWidth > $originalHeight ? $originalWidth : $originalHeight;
 | |
| 		$font_size = $dimension ? $_font_size * ($dimension / 1000) * 1.2 : $_font_size;
 | |
| 
 | |
| 		if ($font_size > $_font_size)
 | |
| 		{
 | |
| 			$font_size = $_font_size;
 | |
| 		}
 | |
| 
 | |
| 		if ($font_size < 14)
 | |
| 		{
 | |
| 			$font_size = 14;
 | |
| 		}
 | |
| 
 | |
| 		$TextSize = @ImageTTFBBox($font_size, 0, $font, $text) or die;
 | |
| 		$TextWidth = abs($TextSize[2]) + abs($TextSize[0]);
 | |
| 		$TextHeight = abs($TextSize[7]) + abs($TextSize[1]);
 | |
| 
 | |
| 		$watermarkImage = imagecreatetruecolor($TextWidth, $TextHeight);
 | |
| 
 | |
| 		imagealphablending($watermarkImage, false);
 | |
| 		imagesavealpha($watermarkImage, true);
 | |
| 		$bgText = imagecolorallocatealpha($watermarkImage, 255, 255, 255, 127);
 | |
| 		imagefill($watermarkImage, 0, 0, $bgText);
 | |
| 		$wmTransp = 127 - ($opacity * 1.27);
 | |
| 		$rgb = self::hex2rgb($color, false);
 | |
| 		$colorResource = imagecolorallocatealpha($watermarkImage, $rgb[0], $rgb[1], $rgb[2], $wmTransp);
 | |
| 		
 | |
| 		// Create watermark
 | |
| 		imagettftext($watermarkImage, $font_size, 0, 0, abs($TextSize[5]), $colorResource, $font, $text);
 | |
| 
 | |
| 		return $watermarkImage;
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Apply the watermark.
 | |
| 	 * 
 | |
| 	 * @param   array  $opts
 | |
| 	 * 
 | |
| 	 * @return  void
 | |
| 	 */
 | |
| 	public static function applyWatermark($opts = [])
 | |
| 	{
 | |
| 		$defaults = [
 | |
| 			'source' => null,
 | |
| 			'destination' => null,
 | |
| 			'preset' => 'custom',
 | |
| 			'type' => 'text',
 | |
| 			'text' => null,
 | |
| 			'position' => 'bottom-right',
 | |
| 			'angle' => 0,
 | |
| 			'opacity' => 50,
 | |
| 			'size' => 30,
 | |
| 			'color' => '#fff'
 | |
| 		];
 | |
| 
 | |
| 		$opts = array_merge($defaults, $opts);
 | |
| 
 | |
| 		if (!$opts['source'])
 | |
| 		{
 | |
| 			return false;
 | |
| 		}
 | |
| 
 | |
| 		if (!is_file($opts['source']))
 | |
| 		{
 | |
| 			return false;
 | |
| 		}
 | |
| 
 | |
| 		$destination = $opts['destination'] ? $opts['destination'] : $opts['source'];
 | |
| 
 | |
| 		$originalImage = new JoomlaImage($opts['source']);
 | |
| 
 | |
| 		// Get the dimensions of the original image
 | |
| 		$originalWidth = $originalImage->getWidth();
 | |
| 		$originalHeight = $originalImage->getHeight();
 | |
| 
 | |
| 		$watermarkSource = null;
 | |
| 
 | |
| 		switch ($opts['type'])
 | |
| 		{
 | |
| 			case 'image':
 | |
| 				$watermarkSource = $opts['image'];
 | |
| 				break;
 | |
| 			
 | |
| 			case 'text':
 | |
| 			default:
 | |
| 				if (!$watermarkText = self::getWatermarkText($opts['text_preset'], $opts['text'], $destination))
 | |
| 				{
 | |
| 					return;
 | |
| 				}
 | |
| 
 | |
| 				$watermarkSource = self::createWatermarkText($watermarkText, (int) $opts['size'], 100, $opts['color'], $originalWidth, $originalHeight);
 | |
| 				break;
 | |
| 		}
 | |
| 
 | |
| 		if (!$watermarkSource)
 | |
| 		{
 | |
| 			return;
 | |
| 		}
 | |
| 
 | |
| 		$original_image_mime = $originalImage->getImageFileProperties($opts['source'])->mime;
 | |
| 
 | |
| 		// Create the final image
 | |
| 		$finalImage = imagecreatetruecolor($originalWidth, $originalHeight);
 | |
| 
 | |
| 		$watermarkOpacity = (int) $opts['opacity'];
 | |
| 
 | |
| 		// Add a black background color if the image is PNG and watermark opacity is not 100, as imagecopymerge() doesn't work with transparent PNG images
 | |
| 		if ($original_image_mime === 'image/png')
 | |
| 		{
 | |
| 			if ($watermarkOpacity === 100)
 | |
| 			{
 | |
| 				imagesavealpha($finalImage, true);
 | |
| 				$trans_background = imagecolorallocatealpha($finalImage, 0, 0, 0, 127);
 | |
| 				imagefill($finalImage, 0, 0, $trans_background);
 | |
| 			}
 | |
| 			else
 | |
| 			{
 | |
| 				$black = imagecolorallocate($finalImage, 0, 0, 0);
 | |
| 				imagefill($finalImage, 0, 0, $black);
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		// Copy original image to the final image
 | |
| 		imagecopy($finalImage, $originalImage->getHandle(), 0, 0, 0, 0, $originalWidth, $originalHeight);
 | |
| 
 | |
| 		// Get watermark image
 | |
| 		$watermarkImage = new JoomlaImage($watermarkSource);
 | |
| 
 | |
| 		// Rotate it
 | |
| 		if ($opts['angle'])
 | |
| 		{
 | |
| 			$angle = $opts['angle'] ? 360 - (int) $opts['angle'] : 0;
 | |
| 			$watermarkImage->rotate($angle, -1, false);
 | |
| 		}
 | |
| 
 | |
| 		// Get the dimensions of the watermark image
 | |
| 		$watermarkWidth = $watermarkImage->getWidth();
 | |
| 		$watermarkHeight = $watermarkImage->getHeight();
 | |
| 
 | |
| 		if (!$watermarkWidth || !$watermarkHeight)
 | |
| 		{
 | |
| 			return false;
 | |
| 		}
 | |
| 
 | |
| 		// Final watermark width/height
 | |
| 		$width = $watermarkWidth;
 | |
| 		$height = $watermarkHeight;
 | |
| 
 | |
| 		// Scale watermark image
 | |
| 		if ($opts['type'] === 'image')
 | |
| 		{
 | |
| 			$scaleFactor = min($originalWidth / $watermarkWidth, $originalHeight / $watermarkHeight);
 | |
| 
 | |
| 			// Calculate the new dimensions of the watermark image
 | |
| 			$width = $watermarkWidth * $scaleFactor;
 | |
| 			$height = $watermarkHeight * $scaleFactor;
 | |
| 
 | |
| 			if ($width > $watermarkWidth)
 | |
| 			{
 | |
| 				$width = $watermarkWidth;
 | |
| 				$height = $watermarkHeight;
 | |
| 			}
 | |
| 		}
 | |
| 		
 | |
| 		$width = (int) $width;
 | |
| 		$height = (int) $height;
 | |
| 
 | |
| 		list($dest_x, $dest_y) = self::getWatermarkPosition($opts['position'], $originalWidth, $originalHeight, $width, $height);
 | |
| 
 | |
| 		// Resize watermark image before applying it into the final image
 | |
| 		$watermarkImage = $watermarkImage->resize($width, $height);
 | |
| 
 | |
| 		// Copy the watermark image into the final image
 | |
| 		if ($watermarkOpacity === 100)
 | |
| 		{
 | |
| 			imagecopy($finalImage, $watermarkImage->getHandle(), round($dest_x), round($dest_y), 0, 0, $width, $height);
 | |
| 		}
 | |
| 		else
 | |
| 		{
 | |
| 			self::imagecopymerge_alpha($finalImage, $watermarkImage->getHandle(), round($dest_x), round($dest_y), 0, 0, $width, $height, $watermarkOpacity);
 | |
| 		}
 | |
| 
 | |
| 		// Save final image
 | |
| 		switch ($original_image_mime)
 | |
| 		{
 | |
| 			case 'image/gif':
 | |
| 				imagegif($finalImage, $destination);
 | |
| 				break;
 | |
| 			
 | |
| 			case 'image/webp':
 | |
| 				imagewebp($finalImage, $destination, 70);
 | |
| 				break;
 | |
| 
 | |
| 			case 'image/png':
 | |
| 				imagepng($finalImage, $destination, 6);
 | |
| 				break;
 | |
| 
 | |
| 			default:
 | |
| 				imagejpeg($finalImage, $destination, 70);
 | |
| 				break;
 | |
| 		}
 | |
| 
 | |
| 		imagedestroy($finalImage);
 | |
| 		$originalImage->destroy();
 | |
| 		$watermarkImage->destroy();
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * imagecopy but with alpha channel support.
 | |
| 	 * 
 | |
| 	 * @param   object   $dst_im
 | |
| 	 * @param   object   $src_im
 | |
| 	 * @param   integer  $dst_x
 | |
| 	 * @param   integer  $dst_y
 | |
| 	 * @param   integer  $src_x
 | |
| 	 * @param   integer  $src_y
 | |
| 	 * @param   integer  $src_w
 | |
| 	 * 
 | |
| 	 * @return  void
 | |
| 	 */
 | |
| 	public static function imagecopymerge_alpha($dst_im, $src_im, $dst_x, $dst_y, $src_x, $src_y, $src_w, $src_h, $pct)
 | |
| 	{
 | |
| 		$cut = imagecreatetruecolor($src_w, $src_h);
 | |
| 		
 | |
|         // copying relevant section from background to the cut resource
 | |
|         imagecopy($cut, $dst_im, 0, 0, $dst_x, $dst_y, $src_w, $src_h);
 | |
| 		
 | |
|         // copying relevant section from watermark to the cut resource
 | |
|         imagecopy($cut, $src_im, 0, 0, $src_x, $src_y, $src_w, $src_h);
 | |
| 
 | |
|         // insert cut resource to destination image
 | |
|         imagecopymerge($dst_im, $cut, $dst_x, $dst_y, 0, 0, $src_w, $src_h, $pct);
 | |
|     }
 | |
| 
 | |
| 	/**
 | |
| 	 * Returns the watermark position.
 | |
| 	 * 
 | |
| 	 * @param   string   $position
 | |
| 	 * @param   integer  $originalWidth
 | |
| 	 * @param   integer  $originalHeight
 | |
| 	 * @param   integer  $width
 | |
| 	 * @param   integer  $height
 | |
| 	 * 
 | |
| 	 * @return  array
 | |
| 	 */
 | |
| 	public static function getWatermarkPosition($position, $originalWidth, $originalHeight, $width, $height)
 | |
| 	{
 | |
| 		/**
 | |
| 		 * Position watermark based on given position.
 | |
| 		 * 
 | |
| 		 * top-left
 | |
| 		 * top-center
 | |
| 		 * top-right
 | |
| 		 * center-left
 | |
| 		 * center-center
 | |
| 		 * center-right
 | |
| 		 * bottom-left
 | |
| 		 * bottom-center
 | |
| 		 * bottom-right
 | |
| 		 */
 | |
| 		$position = explode('-', $position);
 | |
| 
 | |
| 		// Padding from corner
 | |
| 		$yPOS = $xPOS = 10;
 | |
| 		$dest_x = $dest_y = 0;
 | |
| 		
 | |
| 		if (isset($position[0]))
 | |
| 		{
 | |
| 			switch ($position[0])
 | |
| 			{
 | |
| 				case 'top':
 | |
| 					$dest_y = 0 + $yPOS;
 | |
| 					break;
 | |
| 				case 'center':
 | |
| 					$dest_y = round($originalHeight / 2) - round($height / 2);
 | |
| 					break;
 | |
| 				case 'bottom':
 | |
| 					$dest_y = $originalHeight - $height - $yPOS;
 | |
| 					break;
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		if (isset($position[1]))
 | |
| 		{
 | |
| 			switch ($position[1])
 | |
| 			{
 | |
| 				case 'left':
 | |
| 					$dest_x = 0 + $xPOS;
 | |
| 					break;
 | |
| 				case 'center':
 | |
| 					$dest_x = round($originalWidth / 2) - round($width / 2);
 | |
| 					break;
 | |
| 				case 'right':
 | |
| 					$dest_x = $originalWidth - $width - $xPOS;
 | |
| 					break;
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		return [$dest_x, $dest_y];
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Returns the watermark text.
 | |
| 	 * 
 | |
| 	 * @param   string  $preset
 | |
| 	 * @param   string  $text
 | |
| 	 * @param   string  $filename
 | |
| 	 * 
 | |
| 	 * @return  string
 | |
| 	 */
 | |
| 	public static function getWatermarkText($preset = '', $text = '', $filename = '')
 | |
| 	{
 | |
| 		switch ($preset)
 | |
| 		{
 | |
| 			case 'site_name':
 | |
| 				$text = Factory::getApplication()->get('sitename');
 | |
| 				break;
 | |
| 			
 | |
| 			case 'site_url':
 | |
| 				$text = Uri::root();
 | |
| 				break;
 | |
| 
 | |
| 			case 'custom':
 | |
| 				$st = new \NRFramework\SmartTags();
 | |
| 
 | |
| 				// Add file Smart Tags
 | |
| 				$file_data = File::pathinfo($filename);
 | |
| 				$source_basename = $file_data['basename'];
 | |
| 				$file_data['filename'] = $file_data['filename'];
 | |
| 				$file_data['basename'] = $source_basename;
 | |
| 
 | |
| 				$st->add($file_data, 'file.');
 | |
| 
 | |
| 				
 | |
| 				$text = $st->replace($text);
 | |
| 				break;
 | |
| 		}
 | |
| 
 | |
| 		return $text;
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Converts hexidecimal color value to rgb values and returns as array/string
 | |
| 	 *
 | |
| 	 * @param   string 		  $hex
 | |
| 	 * @param   bool		  $asString
 | |
| 	 * 
 | |
| 	 * @return  array|string
 | |
| 	 */
 | |
| 	public static function hex2rgb($hex, $asString = false)
 | |
| 	{
 | |
|         // strip off any leading #
 | |
|         if (0 === strpos($hex, '#'))
 | |
| 		{
 | |
|            $hex = substr($hex, 1);
 | |
|         }
 | |
| 		else if (0 === strpos($hex, '&H'))
 | |
| 		{
 | |
|            $hex = substr($hex, 2);
 | |
|         }
 | |
| 
 | |
|         // break into hex 3-tuple
 | |
|         $cutpoint = ceil(strlen($hex) / 2)-1;
 | |
|         $rgb = explode(':', wordwrap($hex, $cutpoint, ':', $cutpoint), 3);
 | |
| 
 | |
|         // convert each tuple to decimal
 | |
|         $rgb[0] = (isset($rgb[0]) ? hexdec($rgb[0]) : 0);
 | |
|         $rgb[1] = (isset($rgb[1]) ? hexdec($rgb[1]) : 0);
 | |
|         $rgb[2] = (isset($rgb[2]) ? hexdec($rgb[2]) : 0);
 | |
| 
 | |
|         return ($asString ? "{$rgb[0]} {$rgb[1]} {$rgb[2]}" : $rgb);
 | |
|     }
 | |
| } |