plugin.php 14 KB

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