<?php
/**
 * elFinder Plugin Watermark
 *
 * Print watermark on file upload.
 *
 * ex. binding, configure on connector options
 *	$opts = array(
 *		'bind' => array(
 *			'upload.presave' => array(
 *				'Plugin.Watermark.onUpLoadPreSave'
 *			)
 *		),
 *		// global configure (optional)
 *		'plugin' => array(
 *			'Watermark' => array(
 *				'enable'         => true,       // For control by volume driver
 *				'source'         => 'logo.png', // Path to Water mark image
 *				'ratio'          => 0.2,        // Ratio to original image (ratio > 0 and ratio <= 1)
 *				'position'       => 'RB',       // Position L(eft)/C(enter)/R(ight) and T(op)/M(edium)/B(ottom)
 *				'marginX'        => 5,          // Margin horizontal pixel
 *				'marginY'        => 5,          // Margin vertical pixel
 *				'quality'        => 95,         // JPEG image save quality
 *				'transparency'   => 70,         // Water mark image transparency ( other than PNG )
 *				'targetType'     => IMG_GIF|IMG_JPG|IMG_PNG|IMG_WBMP, // Target image formats ( bit-field )
 *				'targetMinPixel' => 200,        // Target image minimum pixel size
 *				'interlace'      => IMG_GIF|IMG_JPG, // Set interlacebit image formats ( bit-field )
 *				'offDropWith'    => null,       // Enabled by default. To disable it if it is dropped with pressing the meta key
 *				                                // Alt: 8, Ctrl: 4, Meta: 2, Shift: 1 - sum of each value
 *				                                // In case of using any key, specify it as an array
 *				'onDropWith'     => null        // Disabled by default. To enable it if it is dropped with pressing the meta key
 *				                                // Alt: 8, Ctrl: 4, Meta: 2, Shift: 1 - sum of each value
 *				                                // In case of using any key, specify it as an array
 *			)
 *		),
 *		// each volume configure (optional)
 *		'roots' => array(
 *			array(
 *				'driver' => 'LocalFileSystem',
 *				'path'   => '/path/to/files/',
 *				'URL'    => 'http://localhost/to/files/'
 *				'plugin' => array(
 *					'Watermark' => array(
 *			 			'enable'         => true,       // For control by volume driver
 *						'source'         => 'logo.png', // Path to Water mark image
 *						'ratio'          => 0.2,        // Ratio to original image (ratio > 0 and ratio <= 1)
*						'position'       => 'RB',       // Position L(eft)/C(enter)/R(ight) and T(op)/M(edium)/B(ottom)
 *						'marginX'        => 5,          // Margin horizontal pixel
 *						'marginY'        => 5,          // Margin vertical pixel
 *						'quality'        => 95,         // JPEG image save quality
 *						'transparency'   => 70,         // Water mark image transparency ( other than PNG )
 *						'targetType'     => IMG_GIF|IMG_JPG|IMG_PNG|IMG_WBMP, // Target image formats ( bit-field )
 *						'targetMinPixel' => 200,        // Target image minimum pixel size
 *						'interlace'      => IMG_GIF|IMG_JPG, // Set interlacebit image formats ( bit-field )
 *        				'offDropWith'    => null,       // Enabled by default. To disable it if it is dropped with pressing the meta key
 *        				                                // Alt: 8, Ctrl: 4, Meta: 2, Shift: 1 - sum of each value
 *        				                                // In case of using any key, specify it as an array
 *        				'onDropWith'     => null        // Disabled by default. To enable it if it is dropped with pressing the meta key
 *						                                // Alt: 8, Ctrl: 4, Meta: 2, Shift: 1 - sum of each value
 *						                                // In case of using any key, specify it as an array
 *					)
 *				)
 *			)
 *		)
 *	);
 *
 * @package elfinder
 * @author Naoki Sawada
 * @license New BSD
 */
class elFinderPluginWatermark extends elFinderPlugin {

	private $watermarkImgInfo = null;

	public function __construct($opts) {
		$defaults = array(
			'enable'         => true,       // For control by volume driver
			'source'         => 'logo.png', // Path to Water mark image
			'ratio'          => 0.2,        // Ratio to original image (ratio > 0 and ratio <= 1)
			'position'       => 'RB',       // Position L(eft)/C(enter)/R(ight) and T(op)/M(edium)/B(ottom)
			'marginX'        => 5,          // Margin horizontal pixel
			'marginY'        => 5,          // Margin vertical pixel
			'quality'        => 95,         // JPEG image save quality
			'transparency'   => 70,         // Water mark image transparency ( other than PNG )
			'targetType'     => IMG_GIF|IMG_JPG|IMG_PNG|IMG_WBMP, // Target image formats ( bit-field )
			'targetMinPixel' => 200,        // Target image minimum pixel size
			'interlace'      => IMG_GIF|IMG_JPG, // Set interlacebit image formats ( bit-field )
			'offDropWith'    => null,       // To disable it if it is dropped with pressing the meta key
			                                // Alt: 8, Ctrl: 4, Meta: 2, Shift: 1 - sum of each value
			                                // In case of using any key, specify it as an array
			'marginRight'    => 0,          // Deprecated - marginX should be used
			'marginBottom'   => 0           // Deprecated - marginY should be used
		);

		$this->opts = array_merge($defaults, $opts);

	}

	public function onUpLoadPreSave(&$thash, &$name, $src, $elfinder, $volume) {
		$opts = $this->getCurrentOpts($volume);
		
		if (! $this->iaEnabled($opts)) {
			return false;
		}
		
		$imageType = null;
		$srcImgInfo = null;
		if (extension_loaded('fileinfo') && function_exists('mime_content_type')) {
			$mime = mime_content_type($src);
			if (substr($mime, 0, 5) !== 'image') {
				return false;
			}
		}
		if (extension_loaded('exif') && function_exists('exif_imagetype')) {
			$imageType = exif_imagetype($src);
		} else {
			$srcImgInfo = getimagesize($src);
			if ($srcImgInfo === false) {
				return false;
			}
			$imageType = $srcImgInfo[2];
		}
		
		// check target image type
		$imgTypes = array(
				IMAGETYPE_GIF  => IMG_GIF,
				IMAGETYPE_JPEG => IMG_JPEG,
				IMAGETYPE_PNG  => IMG_PNG,
				IMAGETYPE_BMP  => IMG_WBMP,
				IMAGETYPE_WBMP => IMG_WBMP
		);
		if (! isset($imgTypes[$imageType]) || ! ($opts['targetType'] & $imgTypes[$imageType])) {
			return false;
		}
		
		// check Animation Gif
		if ($imageType === IMAGETYPE_GIF && elFinder::isAnimationGif($src)) {
			return false;
		}
		// check Animation Png
		if ($imageType === IMAGETYPE_PNG && elFinder::isAnimationPng($src)) {
			return false;
		}
		// check water mark image
		if (! file_exists($opts['source'])) {
			$opts['source'] = dirname(__FILE__) . "/" . $opts['source'];
		}
		if (is_readable($opts['source'])) {
			$watermarkImgInfo = getimagesize($opts['source']);
			if (! $watermarkImgInfo) {
				return false;
			}
		} else {
			return false;
		}
		
		if (! $srcImgInfo) {
			$srcImgInfo = getimagesize($src);
		}
		
		$watermark = $opts['source'];
		$quality = $opts['quality'];
		$transparency = $opts['transparency'];

		// check target image size
		if ($opts['targetMinPixel'] > 0 && $opts['targetMinPixel'] > min($srcImgInfo[0], $srcImgInfo[1])) {
			return false;
		}
		
		$watermark_width = $watermarkImgInfo[0];
		$watermark_height = $watermarkImgInfo[1];

		// Specified as a ratio to the image size
		if ($opts['ratio'] && $opts['ratio'] > 0 && $opts['ratio'] <= 1) {
			$maxW = $srcImgInfo[0] * $opts['ratio'] - ($opts['marginX'] * 2);
			$maxH = $srcImgInfo[1] * $opts['ratio'] - ($opts['marginY'] * 2);
			$dx = $dy = 0;
			if (($maxW >= $watermarkImgInfo[0] && $maxH >= $watermarkImgInfo[0]) || ($maxW <= $watermarkImgInfo[0] && $maxH <= $watermarkImgInfo[0])) {
				$dx = abs($srcImgInfo[0] - $watermarkImgInfo[0]);
				$dy = abs($srcImgInfo[1] - $watermarkImgInfo[1]);
			} else if ($maxW < $watermarkImgInfo[0]) {
				$dx = -1;
			} else {
				$dy = -1;
			}
			if ($dx < $dy) {
				$ww = $maxW;
				$wh = $watermarkImgInfo[1] * ($ww / $watermarkImgInfo[0]);
			} else {
				$wh = $maxH;
				$ww = $watermarkImgInfo[0] * ($wh / $watermarkImgInfo[1]);
			}
			$watermarkImgInfo[0] = $ww;
			$watermarkImgInfo[1] = $wh;
		} else {
			$opts['ratio'] = null;
		}

		$opts['position'] = strtoupper($opts['position']);
		
		// Set vertical position
		if (strpos($opts['position'], 'T') !== false) {
			// Top
			$dest_x = $opts['marginX'];
		} else if (strpos($opts['position'], 'M') !== false) {
			// Middle
			$dest_x = ($srcImgInfo[0] - $watermarkImgInfo[0]) / 2;
		} else {
			// Bottom
			$dest_x = $srcImgInfo[0] - $watermarkImgInfo[0] - max($opts['marginBottom'], $opts['marginX']);
		}

		// Set horizontal position
		if (strpos($opts['position'], 'L') !== false) {
			// Left
			$dest_y = $opts['marginY'];
		} else if (strpos($opts['position'], 'C') !== false) {
			// Middle
			$dest_y = ($srcImgInfo[1] - $watermarkImgInfo[1]) / 2;
		} else {
			// Right
			$dest_y = $srcImgInfo[1] - $watermarkImgInfo[1] - max($opts['marginRight'], $opts['marginY']);
		}

		
		// check interlace
		$opts['interlace'] = ($opts['interlace'] & $imgTypes[$imageType]);
		
		if (class_exists('Imagick', false)) {
			return $this->watermarkPrint_imagick($src, $watermark, $dest_x, $dest_y, $quality, $transparency, $watermarkImgInfo, $opts);
		} else {
			elFinder::expandMemoryForGD(array($watermarkImgInfo, $srcImgInfo));
			return $this->watermarkPrint_gd($src, $watermark, $dest_x, $dest_y, $quality, $transparency, $watermarkImgInfo, $srcImgInfo, $opts);
		}
	}
	
	private function watermarkPrint_imagick($src, $watermark, $dest_x, $dest_y, $quality, $transparency, $watermarkImgInfo, $opts) {
		
		try {
		
			// Open the original image
			$img = new Imagick($src);
			
			// Open the watermark
			$watermark = new Imagick($watermark);
			
			// zoom
			if ($opts['ratio']) {
				$watermark->scaleImage($watermarkImgInfo[0], $watermarkImgInfo[1]);
			}

			// Set transparency
			if (strtoupper($watermark->getImageFormat()) !== 'PNG') {
				$watermark->setImageOpacity($transparency/100);
			}
			
			// Overlay the watermark on the original image
			$img->compositeImage($watermark, imagick::COMPOSITE_OVER, $dest_x, $dest_y);
			
			// Set quality
			if (strtoupper($img->getImageFormat()) === 'JPEG') {
				$img->setImageCompression(imagick::COMPRESSION_JPEG);
				$img->setCompressionQuality($quality);
			}
			
			// set interlace
			$opts['interlace'] && $img->setInterlaceScheme(Imagick::INTERLACE_PLANE);
			
			$result = $img->writeImage($src);
			
			$img->clear();
			$img->destroy();
			$watermark->clear();
			$watermark->destroy();
			
			return $result ? true : false;
		} catch (Exception $e) {
			$ermsg = $e->getMessage();
			$ermsg && trigger_error($ermsg);
			return false;
		}
	}
	
	private function watermarkPrint_gd($src, $watermark, $dest_x, $dest_y, $quality, $transparency, $watermarkImgInfo, $srcImgInfo, $opts) {
		
		$watermark_width = $watermarkImgInfo[0];
		$watermark_height = $watermarkImgInfo[1];

		$ermsg = '';
		switch ($watermarkImgInfo['mime']) {
			case 'image/gif':
				if (imagetypes() & IMG_GIF) {
					$oWatermarkImg = imagecreatefromgif($watermark);
				} else {
					$ermsg = 'GIF images are not supported as watermark image';
				}
				break;
			case 'image/jpeg':
				if (imagetypes() & IMG_JPG) {
					$oWatermarkImg = imagecreatefromjpeg($watermark) ;
				} else {
					$ermsg = 'JPEG images are not supported as watermark image';
				}
				break;
			case 'image/png':
				if (imagetypes() & IMG_PNG) {
					$oWatermarkImg = imagecreatefrompng($watermark) ;
				} else {
					$ermsg = 'PNG images are not supported as watermark image';
				}
				break;
			case 'image/wbmp':
				if (imagetypes() & IMG_WBMP) {
					$oWatermarkImg = imagecreatefromwbmp($watermark);
				} else {
					$ermsg = 'WBMP images are not supported as watermark image';
				}
				break;
			default:
				$oWatermarkImg = false;
				$ermsg = $watermarkImgInfo['mime'].' images are not supported as watermark image';
				break;
		}
		

		if (! $ermsg) {
			// zoom
			if ($opts['ratio']) {
				$tmpImg = imagecreatetruecolor($watermarkImgInfo[0], $watermarkImgInfo[1]);
				imagealphablending($tmpImg, false);
				imagesavealpha($tmpImg, true);
				imagecopyresampled($tmpImg, $oWatermarkImg, 0, 0, 0, 0, $watermarkImgInfo[0], $watermarkImgInfo[1], imagesx($oWatermarkImg), imagesy($oWatermarkImg));
				imageDestroy($oWatermarkImg);
				$oWatermarkImg = $tmpImg;
				$tmpImg = null;
			}

			switch ($srcImgInfo['mime']) {
				case 'image/gif':
					if (imagetypes() & IMG_GIF) {
						$oSrcImg = imagecreatefromgif($src);
					} else {
						$ermsg = 'GIF images are not supported as source image';
					}
					break;
				case 'image/jpeg':
					if (imagetypes() & IMG_JPG) {
						$oSrcImg = imagecreatefromjpeg($src) ;
					} else {
						$ermsg = 'JPEG images are not supported as source image';
					}
					break;
				case 'image/png':
					if (imagetypes() & IMG_PNG) {
						$oSrcImg = imagecreatefrompng($src) ;
					} else {
						$ermsg = 'PNG images are not supported as source image';
					}
					break;
				case 'image/wbmp':
					if (imagetypes() & IMG_WBMP) {
						$oSrcImg = imagecreatefromwbmp($src);
					} else {
						$ermsg = 'WBMP images are not supported as source image';
					}
					break;
				default:
					$oSrcImg = false;
					$ermsg = $srcImgInfo['mime'].' images are not supported as source image';
					break;
			}
		}
		
		if ($ermsg || false === $oSrcImg || false === $oWatermarkImg) {
			$ermsg && trigger_error($ermsg);
			return false;
		}
		
		if ($srcImgInfo['mime'] === 'image/png') {
			if (function_exists('imagecolorallocatealpha')) {
				$bg = imagecolorallocatealpha($oSrcImg, 255, 255, 255, 127);
				imagefill($oSrcImg, 0, 0 , $bg);
			}
		}
		
		if ($watermarkImgInfo['mime'] === 'image/png') {
			imagecopy($oSrcImg, $oWatermarkImg, $dest_x, $dest_y, 0, 0, $watermark_width, $watermark_height);
		} else {
			imagecopymerge($oSrcImg, $oWatermarkImg, $dest_x, $dest_y, 0, 0, $watermark_width, $watermark_height, $transparency);
		}
		
		// set interlace
		$opts['interlace'] && imageinterlace($oSrcImg, true);
		
		switch ($srcImgInfo['mime']) {
			case 'image/gif':
				imagegif($oSrcImg, $src);
				break;
			case 'image/jpeg':
				imagejpeg($oSrcImg, $src, $quality);
				break;
			case 'image/png':
				if (function_exists('imagesavealpha') && function_exists('imagealphablending')) {
					imagealphablending($oSrcImg, false);
					imagesavealpha($oSrcImg, true);
				}
				imagepng($oSrcImg, $src);
				break;
			case 'image/wbmp':
				imagewbmp($oSrcImg, $src);
				break;
		}

		imageDestroy($oSrcImg);
		imageDestroy($oWatermarkImg);
		
		return true;
	}
}
