PageRenderTime 54ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 0ms

/classes/MediaAndFileUtil/ImageUtil.php

https://gitlab.com/indybay/indybay-active
PHP | 473 lines | 431 code | 21 blank | 21 comment | 70 complexity | ec1ff0f0e706048a03c0d818419bdaa9 MD5 | raw file
Possible License(s): AGPL-3.0
  1. <?php
  2. namespace Indybay\MediaAndFileUtil;
  3. use Indybay\Common;
  4. use Indybay\DB\MediaAttachmentDB;
  5. /**
  6. * Main class for image management.
  7. *
  8. * This is mainly just a series of wrappers around php functions that may be
  9. * useful if we ever want to change which functions are used.
  10. *
  11. * Written December 2005
  12. * * Modification Log:
  13. * 12/2005-1/2005 zogren/Zachary Ogren/zogren@yahoo.com
  14. * initial development.
  15. */
  16. class ImageUtil extends Common {
  17. /**
  18. * Scales image by width.
  19. */
  20. public function scaleImageByWidthIfTooLarge($file_name, $copy_from, $copy_to, $max_width, $max_height = 0, $crop_height_and_fix_width = 0, $rotate = 0) {
  21. $new_file_info = [];;
  22. $new_file_info['file_name'] = $file_name;
  23. if (!file_exists($copy_from)) {
  24. echo $copy_from . ' does not exist!<br />';
  25. exit;
  26. }
  27. if (file_exists($copy_to)) {
  28. echo $copy_to . ' already exists!<br />';
  29. exit;
  30. }
  31. list($width_orig, $height_orig, $type) = getimagesize($copy_from);
  32. $new_file_info['image_width'] = $width_orig;
  33. $new_file_info['image_height'] = $height_orig;
  34. $new_file_info['file_size'] = filesize($copy_from);
  35. if ($max_width == 0) {
  36. $max_width = $width_orig;
  37. }
  38. if ($max_height == 0) {
  39. $max_height = $height_orig;
  40. }
  41. if ($width_orig > $max_width || $height_orig > $max_height ||
  42. ($crop_height_and_fix_width == 1 && ($width_orig != $max_width || $height_orig != $max_height)) || $rotate > 0) {
  43. $image_orig = '';
  44. if (strpos(strtolower($copy_to), '.jpeg') > 0 || strpos(strtolower($copy_to), '.jpg') > 0) {
  45. $image_orig = imagecreatefromjpeg($copy_from);
  46. }
  47. elseif (strpos($copy_to, '.png') > 0 || strpos($copy_to, '.png') > 0) {
  48. $image_orig = imagecreatefrompng($copy_from);
  49. if ($image_orig != '') {
  50. $copy_to = str_replace('.png', '.jpg', $copy_to);
  51. $file_name = str_replace('.png', '.jpg', $file_name);
  52. }
  53. }
  54. elseif (strpos($copy_to, '.gif') > 0 || strpos($copy_to, '.gif') > 0) {
  55. if ($type == 3) {
  56. $image_orig = imagecreatefrompng($copy_from);
  57. if ($image_orig != '') {
  58. $copy_to = str_replace('.png', '.jpg', $copy_to);
  59. $file_name = str_replace('.png', '.jpg', $file_name);
  60. }
  61. }
  62. else {
  63. $image_orig = imagecreatefromgif($copy_from);
  64. if ($image_orig != '') {
  65. $copy_to = str_replace('.gif', '.jpg', $copy_to);
  66. $file_name = str_replace('.gif', '.jpg', $file_name);
  67. }
  68. }
  69. }
  70. if ($image_orig != '') {
  71. if ($rotate > 0) {
  72. $image_orig = $this->imageRotateRightAngle($image_orig, $rotate);
  73. if ($rotate == 90 || $rotate == 270) {
  74. $width_orig2 = $height_orig;
  75. $height_orig = $width_orig;
  76. $width_orig = $width_orig2;
  77. $new_file_info['image_width'] = $width_orig;
  78. $new_file_info['image_height'] = $height_orig;
  79. }
  80. }
  81. if ($crop_height_and_fix_width == 1) {
  82. $width_new = $max_width;
  83. $height_new = $height_orig * ($max_width / $width_orig);
  84. if ($height_new < $max_height) {
  85. $height_new = $max_height;
  86. $width_new = $width_orig * ($max_height / $height_orig);
  87. }
  88. }
  89. else {
  90. $height_new = $height_orig * ($max_width / $width_orig);
  91. if ($height_new > $max_height) {
  92. $height_new = $max_height;
  93. $width_new = $width_orig * ($max_height / $height_orig);
  94. }
  95. else {
  96. $width_new = $max_width;
  97. $height_new = $height_orig * ($max_width / $width_orig);
  98. }
  99. }
  100. $new_file_info = [];
  101. $new_file_info['image_width'] = $width_new;
  102. $new_file_info['image_height'] = $height_new;
  103. // Fill background white for converted gifs.
  104. $image_new = imagecreatetruecolor($width_new, $height_new);
  105. $kek = imagecolorallocate($image_new,
  106. 255, 255, 255);
  107. imagefill($image_new, 0, 0, $kek);
  108. imagecopyresampled($image_new, $image_orig, 0, 0, 0, 0, $width_new, $height_new, $width_orig, $height_orig);
  109. imageinterlace($image_new, 1);
  110. imagejpeg($image_new, $copy_to, 100);
  111. if ($crop_height_and_fix_width == 1 && ($max_height != $height_new||$max_width != $width_new)) {
  112. $image_new2 = imagecreatetruecolor($max_width, $max_height);
  113. $kek = imagecolorallocate($image_new2,
  114. 255, 255, 255);
  115. imagefill($image_new, 0, 0, $kek);
  116. imagecopy($image_new2, $image_new, 0, 0, ($width_new - $max_width) / 2, ($height_new - $max_height) / 2, $max_width, $max_height);
  117. imageinterlace($image_new2, 1);
  118. imagejpeg($image_new2, $copy_to, 100);
  119. $new_file_info['image_width'] = $max_width;
  120. $new_file_info['image_height'] = $max_height;
  121. }
  122. $new_file_info['file_size'] = filesize($copy_to);
  123. $new_file_info['file_name'] = $file_name;
  124. }
  125. else {
  126. copy($copy_from, $copy_to);
  127. }
  128. }
  129. else {
  130. copy($copy_from, $copy_to);
  131. }
  132. return $new_file_info;
  133. }
  134. /**
  135. * Gets image info from full file path.
  136. */
  137. public function getImageInfoFromFullFilePath($full_file_path) {
  138. if (!file_exists($full_file_path)) {
  139. $image_info = '';
  140. }
  141. else {
  142. $image_size_info = getimagesize($full_file_path);
  143. $image_width = $image_size_info[0];
  144. $image_height = $image_size_info[1];
  145. $file_size = filesize($full_file_path);
  146. $image_info = [];
  147. $image_info['image_width'] = $image_width;
  148. $image_info['image_height'] = $image_height;
  149. $image_info['file_size'] = $file_size;
  150. }
  151. return $image_info;
  152. }
  153. /**
  154. * Makes image from PDF.
  155. */
  156. public function makeImageForPdf($media_attachment_id, $file_info) {
  157. $media_attachment_db_class = new MediaAttachmentDB();
  158. $upload_path = UPLOAD_PATH . '/';
  159. $date_rel_path = $file_info['relative_path'];
  160. $upload_dir = $upload_path . $date_rel_path;
  161. $existing_full_file_path = $upload_dir . $file_info['file_name'];
  162. $small_worked = FALSE;
  163. $large_worked = FALSE;
  164. $out = '';
  165. $ret = '';
  166. if (file_exists($existing_full_file_path)) {
  167. if (!is_readable($existing_full_file_path)) {
  168. sleep(5);
  169. }
  170. if (!is_readable($existing_full_file_path)) {
  171. echo '<!--Couldnt read file to convert to pdf-->';
  172. return '';
  173. }
  174. }
  175. else {
  176. echo '<!--Couldnt find file to convert to pdf-->';
  177. return '';
  178. }
  179. $exec_command = IMAGE_MAGICK_CONVERT . ' ' . escapeshellarg($existing_full_file_path . '[0]') . ' -colorspace rgb -bordercolor white -border 0 -alpha remove -background white -resize "140x140" -trim ' . escapeshellarg($existing_full_file_path . '_140_.jpg');
  180. exec($exec_command, $out, $ret);
  181. $fs = 0;
  182. $fs = @filesize($existing_full_file_path . '_140_.jpg');
  183. if ($fs + 0 > 10) {
  184. list($testw, $testh, $type) = getimagesize($existing_full_file_path . '_140_.jpg');
  185. if ($type == 2 && $testw + 0 > 0) {
  186. $image_orig = imagecreatefromjpeg($existing_full_file_path . '_140_.jpg');
  187. $image_new = imagecreatetruecolor($testw, $testh);
  188. $kek = imagecolorallocate($image_new, 255, 255, 255);
  189. imagefill($image_new, 0, 0, $kek);
  190. imagecopyresampled($image_new, $image_orig, 0, 0, 0, 0, $testw, $testh, $testw, $testh);
  191. imageinterlace($image_new, 1);
  192. imagejpeg($image_new, $existing_full_file_path . '_140_.jpg', 100);
  193. if (filesize($existing_full_file_path . '_140_.jpg') + 0 > 10) {
  194. list($testw, $testh, $type) = getimagesize($existing_full_file_path . '_140_.jpg');
  195. if ($type == 2 && $testw + 0 > 0) {
  196. $small_worked = TRUE;
  197. }
  198. }
  199. }
  200. }
  201. $exec_command = IMAGE_MAGICK_CONVERT . ' ' . escapeshellarg($existing_full_file_path . '[0]') . ' -colorspace rgb -bordercolor white -border 0 -alpha remove -background white -resize "600x900" -trim ' . escapeshellarg($existing_full_file_path . '_600_.jpg');
  202. exec($exec_command);
  203. $fs = 0;
  204. $fs = @filesize($existing_full_file_path . '_600_.jpg');
  205. if ($fs + 0 > 10) {
  206. list($testw, $testh, $type) = getimagesize($existing_full_file_path . '_600_.jpg');
  207. if ($type == 2 && $testw + 0 > 0) {
  208. $image_orig = imagecreatefromjpeg($existing_full_file_path . '_600_.jpg');
  209. $image_new = imagecreatetruecolor($testw, $testh);
  210. $kek = imagecolorallocate($image_new, 255, 255, 255);
  211. imagefill($image_new, 0, 0, $kek);
  212. imagecopyresampled($image_new, $image_orig, 0, 0, 0, 0, $testw, $testh, $testw, $testh);
  213. imageinterlace($image_new, 1);
  214. imagejpeg($image_new, $existing_full_file_path . '_600_.jpg', 100);
  215. if (filesize($existing_full_file_path . '_600_.jpg') + 0 > 10) {
  216. list($testw, $testh, $type) = getimagesize($existing_full_file_path . '_600_.jpg');
  217. if ($type == 2 && $testw + 0 > 0) {
  218. $large_worked = TRUE;
  219. }
  220. }
  221. }
  222. }
  223. else {
  224. echo "\n<!--Couldn't generate thumbnail for pdf-->\n";
  225. }
  226. $alt_tag = $file_info['file_name'];
  227. if ($large_worked) {
  228. $media_attachment_db_class->addMediaAttachment($file_info['file_name'] . '_600_.jpg', $file_info['file_name'] . '_600_.jpg', $date_rel_path, $alt_tag, $file_info['original_file_name'],
  229. 15, 0, UPLOAD_TYPE_THUMBNAIL_MEDIUM, UPLOAD_STATUS_VALIDATED, $media_attachment_id, 0, 0, 0);
  230. }
  231. if ($small_worked) {
  232. $media_attachment_db_class->addMediaAttachment($file_info['file_name'] . '_140_.jpg', $file_info['file_name'] . '_140_.jpg', $date_rel_path, $alt_tag, $file_info['original_file_name'],
  233. 15, 0, UPLOAD_TYPE_THUMBNAIL_SMALL, UPLOAD_STATUS_VALIDATED, $media_attachment_id, 0, 0, 0);
  234. }
  235. }
  236. /**
  237. * Makes image from video.
  238. */
  239. public function makeImageForVideo($media_attachment_id, $file_info) {
  240. $media_attachment_db_class = new MediaAttachmentDB();
  241. $upload_path = UPLOAD_PATH . '/';
  242. $date_rel_path = $file_info['relative_path'];
  243. $upload_dir = $upload_path . $date_rel_path;
  244. $existing_full_file_path = $upload_dir . $file_info['file_name'];
  245. $small_worked = FALSE;
  246. $large_worked = FALSE;
  247. $exec_command = FFMPEGTHUMBNAILER . ' -f -s 160 -i "' . $existing_full_file_path . '" -o ' . $existing_full_file_path . '_160_.png';
  248. exec($exec_command);
  249. $fs = 0;
  250. $fs = @filesize($existing_full_file_path . '_160_.png');
  251. if ($fs + 0 > 10) {
  252. list($testw, $testh, $type) = getimagesize($existing_full_file_path . '_160_.png');
  253. if ($type == 3 && $testw + 0 > 0) {
  254. $image_orig = imagecreatefrompng($existing_full_file_path . '_160_.png');
  255. $image_new = imagecreatetruecolor($testw, $testh);
  256. $kek = imagecolorallocate($image_new, 255, 255, 255);
  257. imagefill($image_new, 0, 0, $kek);
  258. imagecopyresampled($image_new, $image_orig, 0, 0, 0, 0, $testw, $testh, $testw, $testh);
  259. imageinterlace($image_new, 1);
  260. imagepng($image_new, $existing_full_file_path . '_160_.png');
  261. if (filesize($existing_full_file_path . '_160_.png') + 0 > 10) {
  262. list($small_testw, $small_testh, $type) = getimagesize($existing_full_file_path . '_160_.png');
  263. if ($type == 3 && $small_testw + 0 > 0) {
  264. $small_worked = TRUE;
  265. }
  266. }
  267. }
  268. }
  269. $exec_command = FFMPEGTHUMBNAILER . ' -f -s 600 -i "' . $existing_full_file_path . '" -o ' . $existing_full_file_path . '_600_.png';
  270. exec($exec_command);
  271. $fs = 0;
  272. $fs = @filesize($existing_full_file_path . '_600_.png');
  273. if ($fs + 0 > 10) {
  274. list($testw, $testh, $type) = getimagesize($existing_full_file_path . '_600_.png');
  275. if ($type == 3 && $testw + 0 > 0) {
  276. $image_orig = imagecreatefrompng($existing_full_file_path . '_600_.png');
  277. $image_new = imagecreatetruecolor($testw, $testh);
  278. $kek = imagecolorallocate($image_new, 255, 255, 255);
  279. imagefill($image_new, 0, 0, $kek);
  280. imagecopyresampled($image_new, $image_orig, 0, 0, 0, 0, $testw, $testh, $testw, $testh);
  281. imageinterlace($image_new, 1);
  282. imagepng($image_new, $existing_full_file_path . '_600_.png');
  283. if (filesize($existing_full_file_path . '_600_.png') + 0 > 10) {
  284. list($testw, $testh, $type) = getimagesize($existing_full_file_path . '_600_.png');
  285. if ($type == 3 && $testw + 0 > 0) {
  286. $large_worked = TRUE;
  287. }
  288. }
  289. }
  290. }
  291. // @fixme: 'alt_tag' not defined but should be, so define it here:
  292. $file_info['alt_tag'] = isset($file_info['alt_tag']) ? $file_info['alt_tag'] : '';
  293. if ($large_worked) {
  294. $media_attachment_db_class->addMediaAttachment($file_info['file_name'] . '_600_.png',
  295. $file_info['file_name'] . '_600_.png', $date_rel_path, $file_info['alt_tag'], $file_info['original_file_name'],
  296. 16, 0, UPLOAD_TYPE_THUMBNAIL_MEDIUM, UPLOAD_STATUS_VALIDATED, $media_attachment_id, 0, $testw, $testh);
  297. }
  298. if ($small_worked) {
  299. $media_attachment_db_class->addMediaAttachment($file_info['file_name'] . '_160_.png',
  300. $file_info['file_name'] . '_160_.png', $date_rel_path, $file_info['alt_tag'], $file_info['original_file_name'],
  301. 16, 0, UPLOAD_TYPE_THUMBNAIL_SMALL, UPLOAD_STATUS_VALIDATED, $media_attachment_id, 0, $small_testw, $small_testh);
  302. }
  303. }
  304. /**
  305. * Makes preview version of non-browser-playable video.
  306. */
  307. public function makeH264ForVideo(array $file_info) {
  308. $media_attachment_db_class = new MediaAttachmentDB();
  309. $existing_full_file_path = UPLOAD_PATH . '/' . $file_info['relative_path'] . $file_info['file_name'];
  310. $suffix = '_preview_.mp4';
  311. if (!file_exists($existing_full_file_path . $suffix)) {
  312. exec(FFMPEG . ' -i ' . escapeshellarg($existing_full_file_path) . ' ' . escapeshellarg($existing_full_file_path . $suffix));
  313. if (file_exists($existing_full_file_path . $suffix) && filesize($existing_full_file_path . $suffix)) {
  314. return $media_attachment_db_class->addMediaAttachment($file_info['file_name'] . $suffix,
  315. $file_info['file_name'] . $suffix, $file_info['relative_path'], $file_info['alt_tag'], $file_info['original_file_name'],
  316. 30, 0, UPLOAD_TYPE_H264, UPLOAD_STATUS_VALIDATED, $file_info['media_attachment_id'], filesize($existing_full_file_path . $suffix), $file_info['image_width'], $file_info['image_height']);
  317. }
  318. }
  319. }
  320. /**
  321. * Returns GD image handle of rotated image.
  322. *
  323. * $imgSrc - GD image handle of source image
  324. * $angle - angle of rotation. Needs to be positive integer
  325. * angle shall be 0,90,180,270, but if you give other it
  326. * will be rouned to nearest right angle (i.e. 52->90 degs,
  327. * 96->90 degs)
  328. */
  329. public function imageRotateRightAngle($imgSrc, $angle) {
  330. // Ensuring we got really RightAngle (if not we choose the closest one)
  331. $angle = min(((int) (($angle + 45) / 90) * 90), 270);
  332. // No need to fight.
  333. if ($angle == 0) {
  334. return($imgSrc);
  335. }
  336. // Dimenstion of source image.
  337. $srcX = imagesx($imgSrc);
  338. $srcY = imagesy($imgSrc);
  339. switch ($angle) {
  340. case 90:
  341. $imgDest = imagecreatetruecolor($srcY, $srcX);
  342. for ($x = 0; $x < $srcX; $x++) {
  343. for ($y = 0; $y < $srcY; $y++) {
  344. imagecopy($imgDest, $imgSrc, $srcY - $y - 1, $x, $x, $y, 1, 1);
  345. }
  346. }
  347. break;
  348. case 180:
  349. $imgDest = $this->imageFlip($imgSrc, IMAGE_FLIP_BOTH);
  350. break;
  351. case 270:
  352. $imgDest = imagecreatetruecolor($srcY, $srcX);
  353. for ($x = 0; $x < $srcX; $x++) {
  354. for ($y = 0; $y < $srcY; $y++) {
  355. imagecopy($imgDest, $imgSrc, $y, $srcX - $x - 1, $x, $y, 1, 1);
  356. }
  357. }
  358. break;
  359. }
  360. return($imgDest);
  361. }
  362. /**
  363. * Flips image.
  364. */
  365. public function imageflip($image, $mode) {
  366. $w = imagesx($image);
  367. $h = imagesy($image);
  368. $flipped = imagecreate($w, $h);
  369. if ($mode) {
  370. for ($y = 0; $y < $h; $y++) {
  371. imagecopy($flipped, $image, 0, $y, 0, $h - $y - 1, $w, 1);
  372. }
  373. }
  374. else {
  375. for ($x = 0; $x < $w; $x++) {
  376. imagecopy($flipped, $image, $x, 0, $w - $x - 1, 0, 1, $h);
  377. }
  378. }
  379. return $flipped;
  380. }
  381. /**
  382. * Gets width and height of a video using ffprobe.
  383. */
  384. public function getVideoDimensions(array &$file_info): bool {
  385. $path = UPLOAD_PATH . '/' . $file_info['relative_path'] . $file_info['file_name'];
  386. if (!is_readable($path)) {
  387. trigger_error("Could not read $path");
  388. return FALSE;
  389. }
  390. $command = FFPROBE . ' -v error -select_streams v:0 -show_entries stream -of json ' . escapeshellarg($path);
  391. exec($command, $output, $return_var);
  392. if ($return_var) {
  393. trigger_error("Error code $return_var running $command");
  394. return FALSE;
  395. }
  396. $probe = json_decode(implode($output));
  397. $width = (int) ($probe->streams[0]->width ?? 0);
  398. $height = (int) ($probe->streams[0]->height ?? 0);
  399. if ($width < 1 || $height < 1) {
  400. trigger_error("Could not get dimensions for $path");
  401. return FALSE;
  402. }
  403. // Swap width and height if video is rotated.
  404. if (!empty($probe->streams[0]->tags->rotate) && in_array($probe->streams[0]->tags->rotate, [90, 270])) {
  405. $tmp = $width;
  406. $width = $height;
  407. $height = $tmp;
  408. }
  409. $media_attachment_db_class = new MediaAttachmentDB();
  410. $media_attachment_db_class->updateVideoDimensions((int) $file_info['media_attachment_id'], $width, $height);
  411. $file_info['image_width'] = $width;
  412. $file_info['image_height'] = $height;
  413. return TRUE;
  414. }
  415. /**
  416. * Gets width and height of a video using ffprobe.
  417. */
  418. public function getVideoBrowserCompat(array &$file_info) {
  419. $path = UPLOAD_PATH . '/' . $file_info['relative_path'] . $file_info['file_name'];
  420. if (!is_readable($path)) {
  421. trigger_error("Could not read $path");
  422. return FALSE;
  423. }
  424. $command = FFPROBE . ' -v error -select_streams v:0 -show_entries stream=codec_name -of json ' . escapeshellarg($path);
  425. exec($command, $output, $return_var);
  426. if ($return_var) {
  427. trigger_error("Error code $return_var running $command");
  428. return FALSE;
  429. }
  430. $probe = json_decode(implode($output));
  431. $codec = $probe->streams[0]->codec_name ?? '';
  432. $file_info['browser_compat'] = $codec === 'h264' ? 1 : 0;
  433. $media_attachment_db_class = new MediaAttachmentDB();
  434. $media_attachment_db_class->updateVideoBrowserCompat((int) $file_info['media_attachment_id'], $file_info['browser_compat']);
  435. }
  436. }