plugin.php 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427
  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. $opts = $this->getCurrentOpts($volume);
  98. if (!$this->iaEnabled($opts, $elfinder)) {
  99. return false;
  100. }
  101. $imageType = null;
  102. $srcImgInfo = null;
  103. if (extension_loaded('fileinfo') && function_exists('mime_content_type')) {
  104. $mime = mime_content_type($src);
  105. if (substr($mime, 0, 5) !== 'image') {
  106. return false;
  107. }
  108. }
  109. if (extension_loaded('exif') && function_exists('exif_imagetype')) {
  110. $imageType = exif_imagetype($src);
  111. if ($imageType === false) {
  112. return false;
  113. }
  114. } else {
  115. $srcImgInfo = getimagesize($src);
  116. if ($srcImgInfo === false) {
  117. return false;
  118. }
  119. $imageType = $srcImgInfo[2];
  120. }
  121. // check target image type
  122. $imgTypes = array(
  123. IMAGETYPE_GIF => IMG_GIF,
  124. IMAGETYPE_JPEG => IMG_JPEG,
  125. IMAGETYPE_PNG => IMG_PNG,
  126. IMAGETYPE_BMP => IMG_WBMP,
  127. IMAGETYPE_WBMP => IMG_WBMP
  128. );
  129. if (!isset($imgTypes[$imageType]) || !($opts['targetType'] & $imgTypes[$imageType])) {
  130. return false;
  131. }
  132. // check Animation Gif
  133. if ($imageType === IMAGETYPE_GIF && elFinder::isAnimationGif($src)) {
  134. return false;
  135. }
  136. // check Animation Png
  137. if ($imageType === IMAGETYPE_PNG && elFinder::isAnimationPng($src)) {
  138. return false;
  139. }
  140. // check water mark image
  141. if (!file_exists($opts['source'])) {
  142. $opts['source'] = dirname(__FILE__) . "/" . $opts['source'];
  143. }
  144. if (is_readable($opts['source'])) {
  145. $watermarkImgInfo = getimagesize($opts['source']);
  146. if (!$watermarkImgInfo) {
  147. return false;
  148. }
  149. } else {
  150. return false;
  151. }
  152. if (!$srcImgInfo) {
  153. $srcImgInfo = getimagesize($src);
  154. }
  155. $watermark = $opts['source'];
  156. $quality = $opts['quality'];
  157. $transparency = $opts['transparency'];
  158. // check target image size
  159. if ($opts['targetMinPixel'] > 0 && $opts['targetMinPixel'] > min($srcImgInfo[0], $srcImgInfo[1])) {
  160. return false;
  161. }
  162. $watermark_width = $watermarkImgInfo[0];
  163. $watermark_height = $watermarkImgInfo[1];
  164. // Specified as a ratio to the image size
  165. if ($opts['ratio'] && $opts['ratio'] > 0 && $opts['ratio'] <= 1) {
  166. $maxW = $srcImgInfo[0] * $opts['ratio'] - ($opts['marginX'] * 2);
  167. $maxH = $srcImgInfo[1] * $opts['ratio'] - ($opts['marginY'] * 2);
  168. $dx = $dy = 0;
  169. if (($maxW >= $watermarkImgInfo[0] && $maxH >= $watermarkImgInfo[0]) || ($maxW <= $watermarkImgInfo[0] && $maxH <= $watermarkImgInfo[0])) {
  170. $dx = abs($srcImgInfo[0] - $watermarkImgInfo[0]);
  171. $dy = abs($srcImgInfo[1] - $watermarkImgInfo[1]);
  172. } else if ($maxW < $watermarkImgInfo[0]) {
  173. $dx = -1;
  174. } else {
  175. $dy = -1;
  176. }
  177. if ($dx < $dy) {
  178. $ww = $maxW;
  179. $wh = $watermarkImgInfo[1] * ($ww / $watermarkImgInfo[0]);
  180. } else {
  181. $wh = $maxH;
  182. $ww = $watermarkImgInfo[0] * ($wh / $watermarkImgInfo[1]);
  183. }
  184. $watermarkImgInfo[0] = $ww;
  185. $watermarkImgInfo[1] = $wh;
  186. } else {
  187. $opts['ratio'] = null;
  188. }
  189. $opts['position'] = strtoupper($opts['position']);
  190. // Set vertical position
  191. if (strpos($opts['position'], 'T') !== false) {
  192. // Top
  193. $dest_x = $opts['marginX'];
  194. } else if (strpos($opts['position'], 'M') !== false) {
  195. // Middle
  196. $dest_x = ($srcImgInfo[0] - $watermarkImgInfo[0]) / 2;
  197. } else {
  198. // Bottom
  199. $dest_x = $srcImgInfo[0] - $watermarkImgInfo[0] - max($opts['marginBottom'], $opts['marginX']);
  200. }
  201. // Set horizontal position
  202. if (strpos($opts['position'], 'L') !== false) {
  203. // Left
  204. $dest_y = $opts['marginY'];
  205. } else if (strpos($opts['position'], 'C') !== false) {
  206. // Middle
  207. $dest_y = ($srcImgInfo[1] - $watermarkImgInfo[1]) / 2;
  208. } else {
  209. // Right
  210. $dest_y = $srcImgInfo[1] - $watermarkImgInfo[1] - max($opts['marginRight'], $opts['marginY']);
  211. }
  212. // check interlace
  213. $opts['interlace'] = ($opts['interlace'] & $imgTypes[$imageType]);
  214. if (class_exists('Imagick', false)) {
  215. return $this->watermarkPrint_imagick($src, $watermark, $dest_x, $dest_y, $quality, $transparency, $watermarkImgInfo, $opts);
  216. } else {
  217. elFinder::expandMemoryForGD(array($watermarkImgInfo, $srcImgInfo));
  218. return $this->watermarkPrint_gd($src, $watermark, $dest_x, $dest_y, $quality, $transparency, $watermarkImgInfo, $srcImgInfo, $opts);
  219. }
  220. }
  221. private function watermarkPrint_imagick($src, $watermark, $dest_x, $dest_y, $quality, $transparency, $watermarkImgInfo, $opts)
  222. {
  223. try {
  224. // Open the original image
  225. $img = new Imagick($src);
  226. // Open the watermark
  227. $watermark = new Imagick($watermark);
  228. // zoom
  229. if ($opts['ratio']) {
  230. $watermark->scaleImage($watermarkImgInfo[0], $watermarkImgInfo[1]);
  231. }
  232. // Set transparency
  233. if (strtoupper($watermark->getImageFormat()) !== 'PNG') {
  234. $watermark->setImageOpacity($transparency / 100);
  235. }
  236. // Overlay the watermark on the original image
  237. $img->compositeImage($watermark, imagick::COMPOSITE_OVER, $dest_x, $dest_y);
  238. // Set quality
  239. if (strtoupper($img->getImageFormat()) === 'JPEG') {
  240. $img->setImageCompression(imagick::COMPRESSION_JPEG);
  241. $img->setCompressionQuality($quality);
  242. }
  243. // set interlace
  244. $opts['interlace'] && $img->setInterlaceScheme(Imagick::INTERLACE_PLANE);
  245. $result = $img->writeImage($src);
  246. $img->clear();
  247. $img->destroy();
  248. $watermark->clear();
  249. $watermark->destroy();
  250. return $result ? true : false;
  251. } catch (Exception $e) {
  252. $ermsg = $e->getMessage();
  253. $ermsg && trigger_error($ermsg);
  254. return false;
  255. }
  256. }
  257. private function watermarkPrint_gd($src, $watermark, $dest_x, $dest_y, $quality, $transparency, $watermarkImgInfo, $srcImgInfo, $opts)
  258. {
  259. $watermark_width = $watermarkImgInfo[0];
  260. $watermark_height = $watermarkImgInfo[1];
  261. $ermsg = '';
  262. switch ($watermarkImgInfo['mime']) {
  263. case 'image/gif':
  264. if (imagetypes() & IMG_GIF) {
  265. $oWatermarkImg = imagecreatefromgif($watermark);
  266. } else {
  267. $ermsg = 'GIF images are not supported as watermark image';
  268. }
  269. break;
  270. case 'image/jpeg':
  271. if (imagetypes() & IMG_JPG) {
  272. $oWatermarkImg = imagecreatefromjpeg($watermark);
  273. } else {
  274. $ermsg = 'JPEG images are not supported as watermark image';
  275. }
  276. break;
  277. case 'image/png':
  278. if (imagetypes() & IMG_PNG) {
  279. $oWatermarkImg = imagecreatefrompng($watermark);
  280. } else {
  281. $ermsg = 'PNG images are not supported as watermark image';
  282. }
  283. break;
  284. case 'image/wbmp':
  285. if (imagetypes() & IMG_WBMP) {
  286. $oWatermarkImg = imagecreatefromwbmp($watermark);
  287. } else {
  288. $ermsg = 'WBMP images are not supported as watermark image';
  289. }
  290. break;
  291. default:
  292. $oWatermarkImg = false;
  293. $ermsg = $watermarkImgInfo['mime'] . ' images are not supported as watermark image';
  294. break;
  295. }
  296. if (!$ermsg) {
  297. // zoom
  298. if ($opts['ratio']) {
  299. $tmpImg = imagecreatetruecolor($watermarkImgInfo[0], $watermarkImgInfo[1]);
  300. imagealphablending($tmpImg, false);
  301. imagesavealpha($tmpImg, true);
  302. imagecopyresampled($tmpImg, $oWatermarkImg, 0, 0, 0, 0, $watermarkImgInfo[0], $watermarkImgInfo[1], imagesx($oWatermarkImg), imagesy($oWatermarkImg));
  303. imageDestroy($oWatermarkImg);
  304. $oWatermarkImg = $tmpImg;
  305. $tmpImg = null;
  306. }
  307. switch ($srcImgInfo['mime']) {
  308. case 'image/gif':
  309. if (imagetypes() & IMG_GIF) {
  310. $oSrcImg = imagecreatefromgif($src);
  311. } else {
  312. $ermsg = 'GIF images are not supported as source image';
  313. }
  314. break;
  315. case 'image/jpeg':
  316. if (imagetypes() & IMG_JPG) {
  317. $oSrcImg = imagecreatefromjpeg($src);
  318. } else {
  319. $ermsg = 'JPEG images are not supported as source image';
  320. }
  321. break;
  322. case 'image/png':
  323. if (imagetypes() & IMG_PNG) {
  324. $oSrcImg = imagecreatefrompng($src);
  325. } else {
  326. $ermsg = 'PNG images are not supported as source image';
  327. }
  328. break;
  329. case 'image/wbmp':
  330. if (imagetypes() & IMG_WBMP) {
  331. $oSrcImg = imagecreatefromwbmp($src);
  332. } else {
  333. $ermsg = 'WBMP images are not supported as source image';
  334. }
  335. break;
  336. default:
  337. $oSrcImg = false;
  338. $ermsg = $srcImgInfo['mime'] . ' images are not supported as source image';
  339. break;
  340. }
  341. }
  342. if ($ermsg || false === $oSrcImg || false === $oWatermarkImg) {
  343. $ermsg && trigger_error($ermsg);
  344. return false;
  345. }
  346. if ($srcImgInfo['mime'] === 'image/png') {
  347. if (function_exists('imagecolorallocatealpha')) {
  348. $bg = imagecolorallocatealpha($oSrcImg, 255, 255, 255, 127);
  349. imagefill($oSrcImg, 0, 0, $bg);
  350. }
  351. }
  352. if ($watermarkImgInfo['mime'] === 'image/png') {
  353. imagecopy($oSrcImg, $oWatermarkImg, $dest_x, $dest_y, 0, 0, $watermark_width, $watermark_height);
  354. } else {
  355. imagecopymerge($oSrcImg, $oWatermarkImg, $dest_x, $dest_y, 0, 0, $watermark_width, $watermark_height, $transparency);
  356. }
  357. // set interlace
  358. $opts['interlace'] && imageinterlace($oSrcImg, true);
  359. switch ($srcImgInfo['mime']) {
  360. case 'image/gif':
  361. imagegif($oSrcImg, $src);
  362. break;
  363. case 'image/jpeg':
  364. imagejpeg($oSrcImg, $src, $quality);
  365. break;
  366. case 'image/png':
  367. if (function_exists('imagesavealpha') && function_exists('imagealphablending')) {
  368. imagealphablending($oSrcImg, false);
  369. imagesavealpha($oSrcImg, true);
  370. }
  371. imagepng($oSrcImg, $src);
  372. break;
  373. case 'image/wbmp':
  374. imagewbmp($oSrcImg, $src);
  375. break;
  376. }
  377. imageDestroy($oSrcImg);
  378. imageDestroy($oWatermarkImg);
  379. return true;
  380. }
  381. }