ProcessImage.php 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387
  1. <?php
  2. namespace Typemill\Models;
  3. use Typemill\Models\Helpers;
  4. class ProcessImage extends ProcessAssets
  5. {
  6. public function createImage(string $image, string $name, array $desiredSizes)
  7. {
  8. # fix error from jpeg-library
  9. ini_set ('gd.jpeg_ignore_warning', 1);
  10. error_reporting(E_ALL & ~E_NOTICE);
  11. # clear temporary folder
  12. $this->clearTempFolder();
  13. # set the name of the image
  14. $this->setFileName($name, 'image');
  15. # decode the image from base64-string
  16. $imageDecoded = $this->decodeImage($image);
  17. $imageData = $imageDecoded["image"];
  18. $imageType = $imageDecoded["type"];
  19. # transform image-stream into image
  20. $image = imagecreatefromstring($imageData);
  21. # get the size of the original image
  22. $imageSize = $this->getImageSize($image);
  23. # check the desired sizes and calculate the height, if not set
  24. $desiredSizes = $this->setHeight($imageSize, $desiredSizes);
  25. # resize the images
  26. $resizedImages = $this->imageResize($image, $imageSize, $desiredSizes, $imageType);
  27. # store the original name as txt-file
  28. $tmpname = fopen($this->tmpFolder . $this->getName() . '.' . $imageType . ".txt", "w");
  29. $this->saveOriginal($this->tmpFolder, $imageData, $name = 'original', $imageType);
  30. # temporary store resized images
  31. foreach($resizedImages as $key => $resizedImage)
  32. {
  33. $this->saveImage($this->tmpFolder, $resizedImage, $key, $imageType);
  34. }
  35. # if the image is an animated gif, then overwrite the resized version for live use with the original version
  36. if($imageType == "gif" && $this->detectAnimatedGif($imageData))
  37. {
  38. $this->saveOriginal($this->tmpFolder, $imageData, $name = 'live', $imageType);
  39. }
  40. return true;
  41. }
  42. public function detectAnimatedGif($image_file_contents)
  43. {
  44. $is_animated = preg_match('#(\x00\x21\xF9\x04.{4}\x00\x2C.*){2,}#s', $image_file_contents);
  45. if ($is_animated == 1)
  46. {
  47. return true;
  48. }
  49. return false;
  50. }
  51. public function publishImage()
  52. {
  53. # name is stored in temporary folder as name of the .txt-file
  54. foreach(glob($this->tmpFolder . '*.txt') as $imagename)
  55. {
  56. $tmpname = str_replace('.txt', '', basename($imagename));
  57. # set extension and sanitize name
  58. $this->setFileName($tmpname, 'image');
  59. unlink($imagename);
  60. }
  61. $name = uniqid();
  62. if($this->filename && $this->extension)
  63. {
  64. $name = $this->filename;
  65. }
  66. $files = scandir($this->tmpFolder);
  67. $success = true;
  68. foreach($files as $file)
  69. {
  70. if (!in_array($file, array(".","..")))
  71. {
  72. $tmpfilename = explode(".", $file);
  73. if($tmpfilename[0] == 'original')
  74. {
  75. $success = rename($this->tmpFolder . $file, $this->originalFolder . $name . '.' . $tmpfilename[1]);
  76. }
  77. if($tmpfilename[0] == 'live')
  78. {
  79. $success = rename($this->tmpFolder . $file, $this->liveFolder . $name . '.' . $tmpfilename[1]);
  80. }
  81. if($tmpfilename[0] == 'thumbs')
  82. {
  83. $success = rename($this->tmpFolder . $file, $this->thumbFolder . $name . '.' . $tmpfilename[1]);
  84. }
  85. }
  86. }
  87. if($success)
  88. {
  89. return true;
  90. return 'media/live/' . $name . '.' . $tmpfilename[1];
  91. }
  92. return false;
  93. }
  94. public function decodeImage(string $image)
  95. {
  96. $imageParts = explode(";base64,", $image);
  97. $imageType = explode("/", $imageParts[0]);
  98. $imageData = base64_decode($imageParts[1]);
  99. if ($imageData !== false)
  100. {
  101. return array("image" => $imageData, "type" => $imageType[1]);
  102. }
  103. return false;
  104. }
  105. public function getImageSize($image)
  106. {
  107. $width = imagesx($image);
  108. $height = imagesy($image);
  109. return array('width' => $width, 'height' => $height);
  110. }
  111. public function setHeight(array $imageSize, array $desiredSizes)
  112. {
  113. foreach($desiredSizes as $key => $desiredSize)
  114. {
  115. # if desired size is bigger than the actual image, then drop the desired sizes and use the actual image size instead
  116. if($desiredSize['width'] > $imageSize['width'])
  117. {
  118. $desiredSizes[$key] = $imageSize;
  119. continue;
  120. }
  121. if(!isset($desiredSize['height']))
  122. {
  123. $resizeFactor = $imageSize['width'] / $desiredSize['width'];
  124. $desiredSizes[$key]['height'] = round( ($imageSize['height'] / $resizeFactor), 0);
  125. }
  126. }
  127. return $desiredSizes;
  128. }
  129. public function imageResize($imageData, array $source, array $desiredSizes, $imageType)
  130. {
  131. $copiedImages = array();
  132. foreach($desiredSizes as $key => $desired)
  133. {
  134. // resize
  135. $ratio = max($desired['width']/$source['width'], $desired['height']/$source['height']);
  136. $h = $desired['height'] / $ratio;
  137. $x = ($source['width'] - $desired['width'] / $ratio) / 2;
  138. $y = ($source['height'] - $desired['height'] / $ratio) / 2;
  139. $w = $desired['width'] / $ratio;
  140. $new = imagecreatetruecolor($desired['width'], $desired['height']);
  141. // preserve transparency
  142. if($imageType == "gif" or $imageType == "png")
  143. {
  144. imagecolortransparent($new, imagecolorallocatealpha($new, 0, 0, 0, 127));
  145. imagealphablending($new, false);
  146. imagesavealpha($new, true);
  147. }
  148. imagecopyresampled($new, $imageData, 0, 0, $x, $y, $desired['width'], $desired['height'], $w, $h);
  149. $copiedImages[$key] = $new;
  150. }
  151. return $copiedImages;
  152. }
  153. # save original in temporary folder
  154. public function saveOriginal($folder, $image, $name, $type)
  155. {
  156. $path = $folder . $name . '.' . $type;
  157. file_put_contents($path, $image);
  158. }
  159. # save resized images in temporary folder
  160. public function saveImage($folder, $image, $name, $type)
  161. {
  162. if($type == "png")
  163. {
  164. $result = imagepng( $image, $folder . $name . '.png' );
  165. }
  166. elseif($type == "gif")
  167. {
  168. $result = imagegif( $image, $folder . $name . '.gif' );
  169. }
  170. else
  171. {
  172. $result = imagejpeg( $image, $folder . $name . '.jpeg' );
  173. $type = 'jpeg';
  174. }
  175. imagedestroy($image);
  176. if($result)
  177. {
  178. return $name . '.' . $type;
  179. }
  180. return false;
  181. }
  182. public function deleteImage($name)
  183. {
  184. # validate name
  185. $name = basename($name);
  186. $result = true;
  187. if(!file_exists($this->originalFolder . $name) OR !unlink($this->originalFolder . $name))
  188. {
  189. $result = false;
  190. }
  191. if(!file_exists($this->liveFolder . $name) OR !unlink($this->liveFolder . $name))
  192. {
  193. $result = false;
  194. }
  195. if(!file_exists($this->thumbFolder . $name) OR !unlink($this->thumbFolder . $name))
  196. {
  197. $result = false;
  198. }
  199. # you should not use glob but exact name with ending
  200. /*
  201. foreach(glob($this->originalFolder . $name) as $image)
  202. {
  203. if(!unlink($image))
  204. {
  205. $success = false;
  206. }
  207. }
  208. */
  209. # array_map('unlink', glob("some/dir/*.txt"));
  210. return $result;
  211. }
  212. /*
  213. * scans content of a folder (without recursion)
  214. * vars: folder path as string
  215. * returns: one-dimensional array with names of folders and files
  216. */
  217. public function scanMediaFlat()
  218. {
  219. $thumbs = array_diff(scandir($this->thumbFolder), array('..', '.'));
  220. $imagelist = array();
  221. foreach ($thumbs as $key => $name)
  222. {
  223. if (file_exists($this->liveFolder . $name))
  224. {
  225. $imagelist[] = [
  226. 'name' => $name,
  227. 'timestamp' => filemtime($this->liveFolder . $name),
  228. 'src_thumb' => 'media/thumbs/' . $name,
  229. 'src_live' => 'media/live/' . $name,
  230. ];
  231. }
  232. }
  233. $imagelist = Helpers::array_sort($imagelist, 'timestamp', SORT_DESC);
  234. return $imagelist;
  235. }
  236. public function getImageDetails($name, $structure)
  237. {
  238. $name = basename($name);
  239. if (!in_array($name, array(".","..")) && file_exists($this->liveFolder . $name))
  240. {
  241. $imageinfo = getimagesize($this->liveFolder . $name);
  242. $imagedetails = [
  243. 'name' => $name,
  244. 'timestamp' => filemtime($this->liveFolder . $name),
  245. 'bytes' => filesize($this->liveFolder . $name),
  246. 'width' => $imageinfo[0],
  247. 'height' => $imageinfo[1],
  248. 'type' => $imageinfo['mime'],
  249. 'src_thumb' => 'media/thumbs/' . $name,
  250. 'src_live' => 'media/live/' . $name,
  251. 'pages' => $this->findPagesWithUrl($structure, $name, $result = [])
  252. ];
  253. return $imagedetails;
  254. }
  255. return false;
  256. }
  257. public function generateThumbs()
  258. {
  259. # generate images from live folder to 'tmthumbs'
  260. $liveImages = scandir($this->liveFolder);
  261. foreach ($liveImages as $key => $name)
  262. {
  263. if (!in_array($name, array(".","..")))
  264. {
  265. $result = $this->generateThumbFromImageFile($name);
  266. }
  267. }
  268. return $result;
  269. }
  270. public function generateThumbFromImageFile($filename)
  271. {
  272. $this->setFileName($filename, 'image', $overwrite = true);
  273. if($this->extension == 'jpeg') $this->extension = 'jpg';
  274. switch($this->extension)
  275. {
  276. case 'gif': $image = imagecreatefromgif($this->liveFolder . $filename); break;
  277. case 'jpg': $image = imagecreatefromjpeg($this->liveFolder . $filename); break;
  278. case 'png': $image = imagecreatefrompng($this->liveFolder . $filename); break;
  279. default: return 'image type not supported';
  280. }
  281. $originalSize = $this->getImageSize($image);
  282. $thumbSize = $this->desiredSizes['thumbs'];
  283. $thumb = $this->imageResize($image, $originalSize, ['thumbs' => $thumbSize ], $this->extension);
  284. $saveImage = $this->saveImage($this->thumbFolder, $thumb['thumbs'], $this->filename, $this->extension);
  285. if($saveImage)
  286. {
  287. return true;
  288. }
  289. return false;
  290. }
  291. public function generateSizesFromImageFile($filename, $image)
  292. {
  293. $this->setFileName($filename, 'image');
  294. if($this->extension == 'jpeg') $this->extension = 'jpg';
  295. switch($this->extension)
  296. {
  297. case 'gif': $image = imagecreatefromgif($image); break;
  298. case 'jpg': $image = imagecreatefromjpeg($image); break;
  299. case 'png': $image = imagecreatefrompng($image); break;
  300. default: return 'image type not supported';
  301. }
  302. $originalSize = $this->getImageSize($image);
  303. $resizedImages = $this->imageResize($image, $originalSize, $this->desiredSizes, $this->extension);
  304. return $resizedImages;
  305. }
  306. }