plugin.php 18 KB

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