1<?php 2 3/** 4 * elFinder Plugin Watermark 5 * Print watermark on file upload. 6 * ex. binding, configure on connector options 7 * $opts = array( 8 * 'bind' => array( 9 * 'upload.presave' => array( 10 * 'Plugin.Watermark.onUpLoadPreSave' 11 * ) 12 * ), 13 * // global configure (optional) 14 * 'plugin' => array( 15 * 'Watermark' => array( 16 * 'enable' => true, // For control by volume driver 17 * 'source' => 'logo.png', // Path to Water mark image 18 * 'ratio' => 0.2, // Ratio to original image (ratio > 0 and ratio <= 1) 19 * 'position' => 'RB', // Position L(eft)/C(enter)/R(ight) and T(op)/M(edium)/B(ottom) 20 * 'marginX' => 5, // Margin horizontal pixel 21 * 'marginY' => 5, // Margin vertical pixel 22 * 'quality' => 95, // JPEG image save quality 23 * 'transparency' => 70, // Water mark image transparency ( other than PNG ) 24 * 'targetType' => IMG_GIF|IMG_JPG|IMG_PNG|IMG_WBMP, // Target image formats ( bit-field ) 25 * 'targetMinPixel' => 200, // Target image minimum pixel size 26 * 'interlace' => IMG_GIF|IMG_JPG, // Set interlacebit image formats ( bit-field ) 27 * 'offDropWith' => null, // Enabled by default. To disable it if it is dropped with pressing the meta key 28 * // Alt: 8, Ctrl: 4, Meta: 2, Shift: 1 - sum of each value 29 * // In case of using any key, specify it as an array 30 * 'onDropWith' => null // Disabled by default. To enable it if it is dropped with pressing the meta key 31 * // Alt: 8, Ctrl: 4, Meta: 2, Shift: 1 - sum of each value 32 * // In case of using any key, specify it as an array 33 * ) 34 * ), 35 * // each volume configure (optional) 36 * 'roots' => array( 37 * array( 38 * 'driver' => 'LocalFileSystem', 39 * 'path' => '/path/to/files/', 40 * 'URL' => 'http://localhost/to/files/' 41 * 'plugin' => array( 42 * 'Watermark' => array( 43 * 'enable' => true, // For control by volume driver 44 * 'source' => 'logo.png', // Path to Water mark image 45 * 'ratio' => 0.2, // Ratio to original image (ratio > 0 and ratio <= 1) 46 * 'position' => 'RB', // Position L(eft)/C(enter)/R(ight) and T(op)/M(edium)/B(ottom) 47 * 'marginX' => 5, // Margin horizontal pixel 48 * 'marginY' => 5, // Margin vertical pixel 49 * 'quality' => 95, // JPEG image save quality 50 * 'transparency' => 70, // Water mark image transparency ( other than PNG ) 51 * 'targetType' => IMG_GIF|IMG_JPG|IMG_PNG|IMG_WBMP, // Target image formats ( bit-field ) 52 * 'targetMinPixel' => 200, // Target image minimum pixel size 53 * 'interlace' => IMG_GIF|IMG_JPG, // Set interlacebit image formats ( bit-field ) 54 * 'offDropWith' => null, // Enabled by default. To disable it if it is dropped with pressing the meta key 55 * // Alt: 8, Ctrl: 4, Meta: 2, Shift: 1 - sum of each value 56 * // In case of using any key, specify it as an array 57 * 'onDropWith' => null // Disabled by default. To enable it if it is dropped with pressing the meta key 58 * // Alt: 8, Ctrl: 4, Meta: 2, Shift: 1 - sum of each value 59 * // In case of using any key, specify it as an array 60 * ) 61 * ) 62 * ) 63 * ) 64 * ); 65 * 66 * @package elfinder 67 * @author Naoki Sawada 68 * @license New BSD 69 */ 70class elFinderPluginWatermark extends elFinderPlugin 71{ 72 73 private $watermarkImgInfo = null; 74 75 public function __construct($opts) 76 { 77 $defaults = array( 78 'enable' => true, // For control by volume driver 79 'source' => 'logo.png', // Path to Water mark image 80 'ratio' => 0.2, // Ratio to original image (ratio > 0 and ratio <= 1) 81 'position' => 'RB', // Position L(eft)/C(enter)/R(ight) and T(op)/M(edium)/B(ottom) 82 'marginX' => 5, // Margin horizontal pixel 83 'marginY' => 5, // Margin vertical pixel 84 'quality' => 95, // JPEG image save quality 85 'transparency' => 70, // Water mark image transparency ( other than PNG ) 86 'targetType' => IMG_GIF | IMG_JPG | IMG_PNG | IMG_WBMP, // Target image formats ( bit-field ) 87 'targetMinPixel' => 200, // Target image minimum pixel size 88 'interlace' => IMG_GIF | IMG_JPG, // Set interlacebit image formats ( bit-field ) 89 'offDropWith' => null, // To disable it if it is dropped with pressing the meta key 90 // Alt: 8, Ctrl: 4, Meta: 2, Shift: 1 - sum of each value 91 // In case of using any key, specify it as an array 92 'marginRight' => 0, // Deprecated - marginX should be used 93 'marginBottom' => 0, // Deprecated - marginY should be used 94 'disableWithContentSaveId' => true // Disable on URL upload with post data "contentSaveId" 95 ); 96 97 $this->opts = array_merge($defaults, $opts); 98 99 } 100 101 public function onUpLoadPreSave(&$thash, &$name, $src, $elfinder, $volume) 102 { 103 if (!$src) { 104 return false; 105 } 106 107 $opts = $this->getCurrentOpts($volume); 108 109 if (!$this->iaEnabled($opts, $elfinder)) { 110 return false; 111 } 112 113 $imageType = null; 114 $srcImgInfo = null; 115 if (extension_loaded('fileinfo') && function_exists('mime_content_type')) { 116 $mime = mime_content_type($src); 117 if (substr($mime, 0, 5) !== 'image') { 118 return false; 119 } 120 } 121 if (extension_loaded('exif') && function_exists('exif_imagetype')) { 122 $imageType = exif_imagetype($src); 123 if ($imageType === false) { 124 return false; 125 } 126 } else { 127 $srcImgInfo = getimagesize($src); 128 if ($srcImgInfo === false) { 129 return false; 130 } 131 $imageType = $srcImgInfo[2]; 132 } 133 134 // check target image type 135 $imgTypes = array( 136 IMAGETYPE_GIF => IMG_GIF, 137 IMAGETYPE_JPEG => IMG_JPEG, 138 IMAGETYPE_PNG => IMG_PNG, 139 IMAGETYPE_BMP => IMG_WBMP, 140 IMAGETYPE_WBMP => IMG_WBMP 141 ); 142 if (!isset($imgTypes[$imageType]) || !($opts['targetType'] & $imgTypes[$imageType])) { 143 return false; 144 } 145 146 // check Animation Gif 147 if ($imageType === IMAGETYPE_GIF && elFinder::isAnimationGif($src)) { 148 return false; 149 } 150 // check Animation Png 151 if ($imageType === IMAGETYPE_PNG && elFinder::isAnimationPng($src)) { 152 return false; 153 } 154 // check water mark image 155 if (!file_exists($opts['source'])) { 156 $opts['source'] = dirname(__FILE__) . "/" . $opts['source']; 157 } 158 if (is_readable($opts['source'])) { 159 $watermarkImgInfo = getimagesize($opts['source']); 160 if (!$watermarkImgInfo) { 161 return false; 162 } 163 } else { 164 return false; 165 } 166 167 if (!$srcImgInfo) { 168 $srcImgInfo = getimagesize($src); 169 } 170 171 $watermark = $opts['source']; 172 $quality = $opts['quality']; 173 $transparency = $opts['transparency']; 174 175 // check target image size 176 if ($opts['targetMinPixel'] > 0 && $opts['targetMinPixel'] > min($srcImgInfo[0], $srcImgInfo[1])) { 177 return false; 178 } 179 180 $watermark_width = $watermarkImgInfo[0]; 181 $watermark_height = $watermarkImgInfo[1]; 182 183 // Specified as a ratio to the image size 184 if ($opts['ratio'] && $opts['ratio'] > 0 && $opts['ratio'] <= 1) { 185 $maxW = $srcImgInfo[0] * $opts['ratio'] - ($opts['marginX'] * 2); 186 $maxH = $srcImgInfo[1] * $opts['ratio'] - ($opts['marginY'] * 2); 187 $dx = $dy = 0; 188 if (($maxW >= $watermarkImgInfo[0] && $maxH >= $watermarkImgInfo[0]) || ($maxW <= $watermarkImgInfo[0] && $maxH <= $watermarkImgInfo[0])) { 189 $dx = abs($srcImgInfo[0] - $watermarkImgInfo[0]); 190 $dy = abs($srcImgInfo[1] - $watermarkImgInfo[1]); 191 } else if ($maxW < $watermarkImgInfo[0]) { 192 $dx = -1; 193 } else { 194 $dy = -1; 195 } 196 if ($dx < $dy) { 197 $ww = $maxW; 198 $wh = $watermarkImgInfo[1] * ($ww / $watermarkImgInfo[0]); 199 } else { 200 $wh = $maxH; 201 $ww = $watermarkImgInfo[0] * ($wh / $watermarkImgInfo[1]); 202 } 203 $watermarkImgInfo[0] = $ww; 204 $watermarkImgInfo[1] = $wh; 205 } else { 206 $opts['ratio'] = null; 207 } 208 209 $opts['position'] = strtoupper($opts['position']); 210 211 // Set vertical position 212 if (strpos($opts['position'], 'T') !== false) { 213 // Top 214 $dest_x = $opts['marginX']; 215 } else if (strpos($opts['position'], 'M') !== false) { 216 // Middle 217 $dest_x = ($srcImgInfo[0] - $watermarkImgInfo[0]) / 2; 218 } else { 219 // Bottom 220 $dest_x = $srcImgInfo[0] - $watermarkImgInfo[0] - max($opts['marginBottom'], $opts['marginX']); 221 } 222 223 // Set horizontal position 224 if (strpos($opts['position'], 'L') !== false) { 225 // Left 226 $dest_y = $opts['marginY']; 227 } else if (strpos($opts['position'], 'C') !== false) { 228 // Middle 229 $dest_y = ($srcImgInfo[1] - $watermarkImgInfo[1]) / 2; 230 } else { 231 // Right 232 $dest_y = $srcImgInfo[1] - $watermarkImgInfo[1] - max($opts['marginRight'], $opts['marginY']); 233 } 234 235 236 // check interlace 237 $opts['interlace'] = ($opts['interlace'] & $imgTypes[$imageType]); 238 239 // Repeated use of Imagick::compositeImage() may cause PHP to hang, so disable it 240 //if (class_exists('Imagick', false)) { 241 // return $this->watermarkPrint_imagick($src, $watermark, $dest_x, $dest_y, $quality, $transparency, $watermarkImgInfo, $opts); 242 //} else { 243 elFinder::expandMemoryForGD(array($watermarkImgInfo, $srcImgInfo)); 244 return $this->watermarkPrint_gd($src, $watermark, $dest_x, $dest_y, $quality, $transparency, $watermarkImgInfo, $srcImgInfo, $opts); 245 //} 246 } 247 248 private function watermarkPrint_imagick($src, $watermarkSrc, $dest_x, $dest_y, $quality, $transparency, $watermarkImgInfo, $opts) 249 { 250 251 try { 252 253 // Open the original image 254 $img = new Imagick($src); 255 256 // Open the watermark 257 $watermark = new Imagick($watermarkSrc); 258 259 // zoom 260 if ($opts['ratio']) { 261 $watermark->scaleImage($watermarkImgInfo[0], $watermarkImgInfo[1]); 262 } 263 264 // Set transparency 265 if (strtoupper($watermark->getImageFormat()) !== 'PNG') { 266 $watermark->setImageOpacity($transparency / 100); 267 } 268 269 // Overlay the watermark on the original image 270 $img->compositeImage($watermark, imagick::COMPOSITE_OVER, $dest_x, $dest_y); 271 272 // Set quality 273 if (strtoupper($img->getImageFormat()) === 'JPEG') { 274 $img->setImageCompression(imagick::COMPRESSION_JPEG); 275 $img->setCompressionQuality($quality); 276 } 277 278 // set interlace 279 $opts['interlace'] && $img->setInterlaceScheme(Imagick::INTERLACE_PLANE); 280 281 $result = $img->writeImage($src); 282 283 $img->clear(); 284 $img->destroy(); 285 $watermark->clear(); 286 $watermark->destroy(); 287 288 return $result ? true : false; 289 } catch (Exception $e) { 290 $ermsg = $e->getMessage(); 291 $ermsg && trigger_error($ermsg); 292 return false; 293 } 294 } 295 296 private function watermarkPrint_gd($src, $watermark, $dest_x, $dest_y, $quality, $transparency, $watermarkImgInfo, $srcImgInfo, $opts) 297 { 298 299 $watermark_width = $watermarkImgInfo[0]; 300 $watermark_height = $watermarkImgInfo[1]; 301 302 $ermsg = ''; 303 switch ($watermarkImgInfo['mime']) { 304 case 'image/gif': 305 if (imagetypes() & IMG_GIF) { 306 $oWatermarkImg = imagecreatefromgif($watermark); 307 } else { 308 $ermsg = 'GIF images are not supported as watermark image'; 309 } 310 break; 311 case 'image/jpeg': 312 if (imagetypes() & IMG_JPG) { 313 $oWatermarkImg = imagecreatefromjpeg($watermark); 314 } else { 315 $ermsg = 'JPEG images are not supported as watermark image'; 316 } 317 break; 318 case 'image/png': 319 if (imagetypes() & IMG_PNG) { 320 $oWatermarkImg = imagecreatefrompng($watermark); 321 } else { 322 $ermsg = 'PNG images are not supported as watermark image'; 323 } 324 break; 325 case 'image/wbmp': 326 if (imagetypes() & IMG_WBMP) { 327 $oWatermarkImg = imagecreatefromwbmp($watermark); 328 } else { 329 $ermsg = 'WBMP images are not supported as watermark image'; 330 } 331 break; 332 default: 333 $oWatermarkImg = false; 334 $ermsg = $watermarkImgInfo['mime'] . ' images are not supported as watermark image'; 335 break; 336 } 337 338 339 if (!$ermsg) { 340 // zoom 341 if ($opts['ratio']) { 342 $tmpImg = imagecreatetruecolor($watermarkImgInfo[0], $watermarkImgInfo[1]); 343 imagealphablending($tmpImg, false); 344 imagesavealpha($tmpImg, true); 345 imagecopyresampled($tmpImg, $oWatermarkImg, 0, 0, 0, 0, $watermarkImgInfo[0], $watermarkImgInfo[1], imagesx($oWatermarkImg), imagesy($oWatermarkImg)); 346 imageDestroy($oWatermarkImg); 347 $oWatermarkImg = $tmpImg; 348 $tmpImg = null; 349 } 350 351 switch ($srcImgInfo['mime']) { 352 case 'image/gif': 353 if (imagetypes() & IMG_GIF) { 354 $oSrcImg = imagecreatefromgif($src); 355 } else { 356 $ermsg = 'GIF images are not supported as source image'; 357 } 358 break; 359 case 'image/jpeg': 360 if (imagetypes() & IMG_JPG) { 361 $oSrcImg = imagecreatefromjpeg($src); 362 } else { 363 $ermsg = 'JPEG images are not supported as source image'; 364 } 365 break; 366 case 'image/png': 367 if (imagetypes() & IMG_PNG) { 368 $oSrcImg = imagecreatefrompng($src); 369 } else { 370 $ermsg = 'PNG images are not supported as source image'; 371 } 372 break; 373 case 'image/wbmp': 374 if (imagetypes() & IMG_WBMP) { 375 $oSrcImg = imagecreatefromwbmp($src); 376 } else { 377 $ermsg = 'WBMP images are not supported as source image'; 378 } 379 break; 380 default: 381 $oSrcImg = false; 382 $ermsg = $srcImgInfo['mime'] . ' images are not supported as source image'; 383 break; 384 } 385 } 386 387 if ($ermsg || false === $oSrcImg || false === $oWatermarkImg) { 388 $ermsg && trigger_error($ermsg); 389 return false; 390 } 391 392 if ($srcImgInfo['mime'] === 'image/png') { 393 if (function_exists('imagecolorallocatealpha')) { 394 $bg = imagecolorallocatealpha($oSrcImg, 255, 255, 255, 127); 395 imagefill($oSrcImg, 0, 0, $bg); 396 } 397 } 398 399 if ($watermarkImgInfo['mime'] === 'image/png') { 400 imagecopy($oSrcImg, $oWatermarkImg, $dest_x, $dest_y, 0, 0, $watermark_width, $watermark_height); 401 } else { 402 imagecopymerge($oSrcImg, $oWatermarkImg, $dest_x, $dest_y, 0, 0, $watermark_width, $watermark_height, $transparency); 403 } 404 405 // set interlace 406 $opts['interlace'] && imageinterlace($oSrcImg, true); 407 408 switch ($srcImgInfo['mime']) { 409 case 'image/gif': 410 imagegif($oSrcImg, $src); 411 break; 412 case 'image/jpeg': 413 imagejpeg($oSrcImg, $src, $quality); 414 break; 415 case 'image/png': 416 if (function_exists('imagesavealpha') && function_exists('imagealphablending')) { 417 imagealphablending($oSrcImg, false); 418 imagesavealpha($oSrcImg, true); 419 } 420 imagepng($oSrcImg, $src); 421 break; 422 case 'image/wbmp': 423 imagewbmp($oSrcImg, $src); 424 break; 425 } 426 427 imageDestroy($oSrcImg); 428 imageDestroy($oWatermarkImg); 429 430 return true; 431 } 432} 433