[cms] Images scale crop optimize overwrite (ImageMagic) [5.3.0-B1]
if you have a lot of news with pictures and operators is not prepared thumbnails and using images of different formats and high resolution ....
- optimize images - decrease image resolution, decrease quality, repac and another that allows the ImageMagick (CKEditor is now able to change the resolution. In kernel is not an option)
- after optimization overwrite original for save space on the server
- scale and crop images with different proportions
- update images for templates and cms block
- for cms block automatically add link over image to view full size image
Solution
(for external ImageMagic only)
<img src="<inp2:Field name='MyImage' format='resize:800x600;overwrite:1;gravity:center;wm:/img/wm_logo.png|10|10;optimize:-interlace Plane|-depth 8 -type TrueColor;' />" alt="" />
Options description:
resize:800x600; - resize image if original 1024x1024 after scale will be 600x600; if original 1024x768 after scale 800x600; if original 768x1024 after scale 450x600
You can specify only one parameter resize:800x; all images after scale will be width 800 height reduced proportionally
overwrite:1; - overwrite original after scale.
gravity:center; crop with gravity; possible options: NorthWest, North, NorthEast, West, Center, East, SouthWest, South, SouthEast (Standart options from ImageMagic)
if gravity active overwrite will be disabled automatically. See attached image for crop logic.
wm:/img/wm_logo.png|10|10; watermarking, option 1 - filename; 2- horizontal margin; 2-vertical margin; margin position illustrated in image
I'm add options to SystemSettings table ImageMagickWMFilename, ImageMagickWMhorzMargin, ImageMagickWMvertMargin. If Filename exists in ImageMagickWMFilename watermarking is active. if you want to disable for wm for specific template set option in tag: "wm:||;". For overwrite default value from SystemOptions set new option in "wm:" tag.
if overwrite is active then after scale image with new params will be saved without watermark.
if gravity is active - watermarking disabled.
optimize:options for jpg|options for png/gif; standard options from ImageMagic documentation for optimize Image;
Im add options to SystemSettings ImageMagickOptimizeJpgParams - default value: -strip -interlace Plane -density 75 -units pixelsperinch -quality 75%; ImageMagickOptimizePngParams: -strip +dither -colors 150 -depth 8 -type TrueColor.
for specific template you can disable default options optimize:|; or set other options/
<inp2:m_DefineElement name="link_item"> <a href="<inp2:m_Param name="image_url" />" class="aaaa" data-rel="gallery"><inp2:m_Param name="image_tag" /></a> </inp2:m_DefineElement> <inp2:st_ContentBlock num="1" format_img="resize:600x;overwrite:1;link:1024x768;link_element:link_item;link_overwrite:1" />
Specific params for ContentBlock:
resize:800x600; - set Maximal Width Height. if in the editor when you add images with large sizes, they will be changed to the
examples:
in cms block added image tag <img src="pathToImage" style="width:2048px; height:1536px" />
resize:600x; image will ba scaled for width 600px and tag style will be changed <img src="pathToImageScaled" style="width:600px; height:450px" />
"style" not overwrite if it is set in percent (width:100%;) or used max-width: min-width: or max-height: min-height: (for responsive templates)
If original image 2048x1536 and to cms block added image tag <img src="pathToImage" style="width:600px; height:375px" /> after parse will be after parse <img src="pathToImageScaled" style="width:600px; height:450px" />
link:1024x768; options for add link for view full size image after click. For scripts like slimbox http://www.digitalia.be/software/slimbox2/
Example:
combination for resize:600x; and link:1024x768;
original image width:2048px; height:1536px result after parse:
<a href="URL_to_IMAGE_1024x768" class="aaaa" data-rel="gallery"> <img src="pathToImageScaled" style="width:600px; height:450px" /> </a>
If link options exists overwrite automatically disabled for tag <img /> within the tag <a> </a>
link_element:link_item; parse element 'link_item' for href insert tag <a href="">...</a>
link_overwrite:1 scale image to the size specified in the tag link:1024x768; and overwrite original if it is active: overwrite:1; automaticaly disabled. gravity for full image is disabled.
Code:
UPDATE `ami3_SystemSettings` SET `Heading` = 'la_section_ImageMagic' DisplayOrder = 12.01 WHERE VariableId = 37; INSERT INTO `ami3_SystemSettings` (`VariableName`, `VariableValue`, `ModuleOwner`, `Section`, `Heading`, `Prompt`, `ElementType`, `Validation`, `ValueList`, `DisplayOrder`, `GroupDisplayOrder`, `Install`, `HintLabel`) VALUES ('ImageMagickPath', '/usr/bin', 'In-Portal', 'in-portal:configure_advanced', 'la_section_ImageMagic', 'la_config_ImageMagickPath', 'text', '', '', 12.02, 0, 0, NULL), ('ImageMagickOptimizeJpgParams', '-strip -interlace Plane -density 75 -units pixelsperinch -quality 75%', 'In-Portal', 'in-portal:configure_advanced', 'la_section_ImageMagic', 'la_config_ImageMagickOptimizeJpgParams', 'text', '', '', 12.03, 0, 0, NULL), ('ImageMagickOptimizePngParams', '-strip +dither -colors 150 -depth 8 -type TrueColor', 'In-Portal', 'in-portal:configure_advanced', 'la_section_ImageMagic', 'la_config_ImageMagickOptimizePngParams', 'text', '', '', 12.04, 0, 0, NULL), ('ImageMagickWMFilename', '', 'In-Portal', 'in-portal:configure_advanced', 'la_section_ImageMagic', 'la_config_ImageMagickWMFilename', 'text', '', '', 12.05, 0, 0, NULL), ('ImageMagickWMhorzMargin', '', 'In-Portal', 'in-portal:configure_advanced', 'la_section_ImageMagic', 'la_config_ImageMagickWMhorzMargin', 'text', '', '', 12.06, 0, 0, NULL), ('ImageMagickWMvertMargin', '', 'In-Portal', 'in-portal:configure_advanced', 'la_section_ImageMagic', 'la_config_ImageMagickWMvertMargin', 'text', '', '', 12.07, 0, 0, NULL); INSERT INTO `ami3_LanguageLabels` (`Phrase`, `PhraseKey`, `l1_Translation`, `l2_Translation`, `l3_Translation`, `l4_Translation`, `l5_Translation`, `l1_HintTranslation`, `l2_HintTranslation`, `l3_HintTranslation`, `l4_HintTranslation`, `l5_HintTranslation`, `l1_ColumnTranslation`, `l2_ColumnTranslation`, `l3_ColumnTranslation`, `l4_ColumnTranslation`, `l5_ColumnTranslation`, `l1_TranslateFrom`, `l2_TranslateFrom`, `l3_TranslateFrom`, `l4_TranslateFrom`, `l5_TranslateFrom`, `PhraseType`, `LastChanged`, `LastChangeIP`, `Module`) VALUES ('la_section_ImageMagic', 'LA_SECTION_IMAGEMAGIC', 'Image Magic Params', NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, 0, 0, 0, 1, NULL, '', 'Custom'), ('la_config_ImageMagickPath', 'LA_CONFIG_IMAGEMAGICKPATH', 'Image Magic Path', NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, 0, 0, 0, 1, NULL, '', 'Custom'), ('la_config_ImageMagickOptimizeJpgParams', 'LA_CONFIG_IMAGEMAGICKOPTIMIZEJPGPARAMS', 'Params for JPG optimization', NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, 0, 0, 0, 1, NULL, '', 'Custom'), ('la_config_ImageMagickOptimizePngParams', 'LA_CONFIG_IMAGEMAGICKOPTIMIZEPNGPARAMS', 'Params for PNG optimization', NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, 0, 0, 0, 1, NULL, '', 'Custom'), ('la_config_ImageMagickWMFilename', 'LA_CONFIG_IMAGEMAGICKWMFILENAME', 'WaterMark filename', NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, 0, 0, 0, 1, NULL, '', 'Custom'), ('la_config_ImageMagickWMhorzMargin', 'LA_CONFIG_IMAGEMAGICKWMHORZMARGIN', 'WaterMark Horizontal Margin', NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, 0, 0, 0, 1, 1477518783, '192.168.1.100', 'Custom'), ('la_config_ImageMagickWMvertMargin', 'LA_CONFIG_IMAGEMAGICKWMVERTMARGIN', 'WaterMark Vertical Margin', NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, 0, 0, 0, 1, NULL, '', 'Custom'),
added option ImageMagickPath if Linux installation not like CentOs RedHat Fedora
function _transformContentBlockData(&$data, $params) { if (!$this->Application->IsAdmin && $this->Application->ConfigValue('ForceImageMagickResize')) { $formatImg = getArrayValue($params,'format_img'); if ($formatImg) { $data = $this->OptimizeContentBlockImages($data,$formatImg); } } return $data; } function OptimizeContentBlockImages(&$data, $formatImg) { if ($this->Application->IsAdmin) return $data; $max_width=null; $max_height=null; $aImg = array(); $format_parts = explode(';', $formatImg); foreach ($format_parts as $format_part) { if (preg_match('/^resize:(\d*)x(\d*)$/', $format_part, $regs)) { $max_width = $regs[1]; $max_height = $regs[2]; } elseif (preg_match('/^link:(\d*)x(\d*)$/', $format_part, $regs)) { $addLink = 1; $full_width = $regs[1]; $full_height = $regs[2]; } elseif (preg_match('/^link_element:(.*)$/', $format_part, $regs)) { $link_element = $regs[1]; }elseif (preg_match('/^link_overwrite:(.*)$/', $format_part, $regs)) { $link_overwrite = $regs[1]; } } preg_match_all('/<img[^>]+>/i',$data, $result); foreach($result[0] as $img_tag) { $tag_width = null; $tag_height = null; $unit=null; $new_width=null;$new_height=null;$newFormatImg=null; preg_match_all('/(width|height|style|src)=("[^"]*")/i', $img_tag, $aImg); $aSrc = parse_url(str_replace('"', '', $aImg[2][array_search('src', $aImg[1])])); if (isset($aSrc['host']) && $aSrc['host']) continue; // remote url preg_match('/[\s\"\;\']width\s*:\s*(\d{1,4})\s*px/i',$aImg[2][array_search('style', $aImg[1])],$aWidth); if ($aWidth[1]) { $tag_width = str_replace(array('height', 'px', ':', ' ', '"'), '', $aWidth[1]); $unit = 'px'; preg_match('/[\s\"\;\']height\s*:\s*(\d{1,4})\s*px/i',$aImg[2][array_search('style', $aImg[1])],$aHeight); if ($aHeight[1]) { $tag_height = str_replace(array('height', 'px', ':', ' ', '"'), '', $aHeight[1]); $unit = 'px'; } } else { preg_match('/(\d{1,4})\s*px/',$aImg[2][array_search('width', $aImg[1])],$aWidth); if ($aWidth[1]) { $tag_width = str_replace(array('height', 'px', ':', ' ', '"'), '', $aWidth[1]); $unit = 'px'; } preg_match('/(\d{1,4})\s*px/',$aImg[2][array_search('height', $aImg[1])],$aHeight); if ($aHeight[1]) { $tag_height = str_replace(array('height', 'px', ':', ' ', '"'), '', $aHeight[1]); $unit = 'px'; } } $filename = $aSrc['path']; $aImgInfo = getimagesize(FULL_PATH.'/'.$filename); if (!$aImgInfo) continue; if ($unit == 'px') { if ($max_width && $tag_width) { if ($aImgInfo[0] > min($max_width,$tag_width)) { $new_width = min($max_width,$tag_width); } } elseif (!$max_width && $tag_width) { if ($aImgInfo[0] > $tag_width) { $new_width = $tag_width; } } elseif ($max_width && !$tag_width) { if ($aImgInfo[0] > $max_width) { $new_width = $max_width; } } if ($max_height && $tag_height) { if ($aImgInfo[1] > min($max_height,$tag_height)) { $new_height = min($max_height,$tag_height); } } elseif (!$max_height && $tag_height) { if ($aImgInfo[1] > $tag_height) { $new_height = $tag_height; } } elseif ($max_height && !$tag_height) { if ($aImgInfo[1] > $max_height) { $new_height = $max_height; } } } else { if ($aImgInfo[0] > $max_width) { $new_width = $max_width; } if ($aImgInfo[1] > $max_height) { $new_height = $max_height; } } if ($new_width || $new_height) { $ImageHelper = $this->Application->recallObject('ImageHelper'); $newFormatImg = $formatImg; $newFormatImg = $ImageHelper->updateFormat($newFormatImg,'link','del'); $newFormatImg = $ImageHelper->updateFormat($newFormatImg,'link_element','del'); $newFormatImg = $ImageHelper->updateFormat($newFormatImg,'link_overwrite','del'); if (!$max_width && !$max_height) { $newFormatImg = $ImageHelper->updateFormat($newFormatImg,'resize','del'); $newFormatImg = $ImageHelper->updateFormat($newFormatImg,'resize:'.$new_width.'x'.$new_height,'add'); } elseif ($max_width || $max_height) { $newFormatImg = $ImageHelper->updateFormat($newFormatImg,'resize','del'); $newFormatImg = $ImageHelper->updateFormat($newFormatImg,'resize:'.$new_width.'x'.$new_height,'add'); } if ($addLink) { // if exists link to big picture remove overwrite for small pictures $newFormatImg = $ImageHelper->updateFormat($newFormatImg,'overwrite','del'); } $imageUrl = $ImageHelper->resizeImage(FULL_PATH.$filename,$newFormatImg); $newFilename = parse_url($imageUrl,PHP_URL_PATH); if ($newFilename) { if ($unit == 'px') { $image_info = @getimagesize(FULL_PATH.$newFilename); $actual_width = $image_info[0]; $actual_height = $image_info[1]; $replaceFrom = array('/width\s*:\s*(\d{1,4})\s*px/i','/height\s*:\s*(\d{1,4})\s*px/i', '/width\=\"(\d{1,4})\s*px"/', '/height\="(\d{1,4})\s*px\"/'); $replaceTo = array('width: '.$actual_width.'px','height: '.$actual_height.'px', 'width="'.$actual_width.'"','height="'.$actual_height.'"'); $newImg_tag = preg_replace($replaceFrom,$replaceTo,$img_tag); $newImg_tag = str_replace($filename,$newFilename,$newImg_tag); } else { $newImg_tag = str_replace($filename,$newFilename,$img_tag); } if ($addLink) { $aFrom = array('.','+','*','?','[','^',']','$','(',')','{','}','=','!','<','>','|',':','-','/'); $aTo = array('\.','\+','\*','\?','\[','\^','\]','\$','\(','\)','\{','\}','\=','\!','\<','\>','\|','\:','\-','\/'); $regex = str_replace($aFrom,$aTo,$img_tag); preg_match('/'.$regex.'[\r\n\t\s*]{0,}\<\/a>/iu',$data,$aLink); if (!$aLink[0]) // check other link exists { $newFormatImg = $ImageHelper->updateFormat($newFormatImg,'gravity','del'); $newFormatImg = $ImageHelper->updateFormat($newFormatImg,'resize','del'); $newFormatImg = $ImageHelper->updateFormat($newFormatImg,'resize:'.$full_width.'x'.$full_height,'add'); if ($link_overwrite) { $newFormatImg = $ImageHelper->updateFormat($newFormatImg,'overwrite:1','add'); } $imageUrl = $ImageHelper->resizeImage(FULL_PATH.$filename,$newFormatImg); if ($imageUrl) { $block_params = array('name'=>$link_element, 'image_url' => $imageUrl, 'image_tag' => $newImg_tag); $newImg_tag = $this->Application->ParseBlock($block_params); } } } $data = str_replace($img_tag,$newImg_tag,$data ); } } } /* there was a block to save the changes to the table PageContent ... The operator could see the actual changes and modify them. Now to save thumbnails another path AND ckeditor not see the pictures after the scale ..... After parse this block set special flag and the unit is not reparse. Фfter change update flag set as NULL and function to work again .... */ return $data; }
/** * Update format string * * @params string $format sample format: "resize:300x500;overwrite:1;link:1024x768;" * @params string $part comand part for remove param sample format: "overwrite" or new command with param for add command sample format: "resize:300x500" * @params string $operation two operation type "add" or "del" * @return string sample result "resize:300x500;link:1024x768;" */ function updateFormat(&$format, $part = null, $operation = null) { if (!$operation || !$part) return $format; $format_parts = explode(';', $format); if (!$format_parts) $format_parts = array(); foreach ($format_parts as $i => $format_part) { if ($operation == 'del') { if (preg_match('/^'.$part.'\:/',$format_part)) { unset($format_parts[$i]); } } } if ($operation == 'add') { $format_parts[] = $part; } return implode(';',$format_parts); } /** * Parses format string into array * * @param string $format sample format: "resize:300x500;wm:inc/wm.png|c|-20" * @return Array sample result: Array('max_width' => 300, 'max_height' => 500, 'wm_filename' => 'inc/wm.png', 'h_margin' => 'c', 'v_margin' => -20) */ function parseFormat(&$format) { $res = Array (); if (strpos($format,'gravity:') !== false) { // Not overwrite original if use crop; $format = $this->updateFormat($format, 'overwrite', 'del'); } $res['wm_filename'] = ($this->Application->ConfigValue('ImageMagickWMFilename'))?FULL_PATH.THEMES_PATH.$this->Application->ConfigValue('ImageMagickWMFilename'):null; $res['h_margin'] = ($this->Application->ConfigValue('ImageMagickWMhorzMargin'))?$this->Application->ConfigValue('ImageMagickWMhorzMargin'):null; $res['v_margin'] = ($this->Application->ConfigValue('ImageMagickWMvertMargin'))?$this->Application->ConfigValue('ImageMagickWMvertMargin'):null; $res['optimize_JPG'] = $this->Application->ConfigValue('ImageMagickOptimizeJpgParams'); $res['optimize_PNG'] = $this->Application->ConfigValue('ImageMagickOptimizePngParams'); $format_parts = explode(';', $format); foreach ($format_parts as $format_part) { if (preg_match('/^resize:(\d*)x(\d*)$/', $format_part, $regs)) { $res['max_width'] = $regs[1]; $res['max_height'] = $regs[2]; } elseif (preg_match('/^overwrite:(\d*)$/', $format_part, $regs)) { $res['overwrite'] = $regs[1]; } elseif (preg_match('/^wm:([^\|]*)\|([^\|]*)\|([^\|]*)$/', $format_part, $regs)) { $res['wm_filename'] = FULL_PATH.THEMES_PATH.'/'.$regs[1]; $res['h_margin'] = strtolower($regs[2]); $res['v_margin'] = strtolower($regs[3]); } elseif (preg_match('/^crop:([^\|]*)\|([^\|]*)$/', $format_part, $regs)) { $res['crop_x'] = strtolower($regs[1]); $res['crop_y'] = strtolower($regs[2]); } elseif (preg_match('/^gravity:(.*)/', $format_part, $regs)) { $res['gravity'] = strtolower($regs[1]); // NorthWest, North, NorthEast, West, Center, East, SouthWest, South, SouthEast } elseif (preg_match('/^optimize:([^\|]*)\|([^\|]*)$/', $format_part, $regs)) { $res['optimize_JPG'] = $regs[1]; $res['optimize_PNG'] = $regs[2]; } elseif ($format_part == 'img_size' || $format_part == 'img_sizes') { $res['image_size'] = true; } elseif (preg_match('/^fill:(.*)$/', $format_part, $regs)) { $res['fill'] = $regs[1]; } elseif ( preg_match('/^default:(.*)$/', $format_part, $regs) ) { $default_image = FULL_PATH . THEMES_PATH . '/' . $regs[1]; if ( strpos($default_image, '../') !== false ) { $default_image = realpath($default_image); } $res['default'] = $default_image; } } return $res; } function ResizeImage($src_image, $max_width, $max_height = false) { //..... $src_path = dirname($src_image); //instead of: $transform_keys = Array ('crop_x', 'crop_y', 'fill', 'wm_filename'); $transform_keys = Array ('crop_x', 'crop_y', 'fill', 'wm_filename', 'gravity','optimize_JPG','optimize_PNG'); //..... } function ScaleImage($src_image, $params) { $image_info = $this->getImageInfo($src_image); if (!$image_info) { return false; } $mime_type = $image_info['mime']; /*list ($params['max_width'], $params['max_height'], $resized) = $this->GetImageDimensions($src_image, $params['max_width'], $params['max_height'], $params); if (!$resized) { // image dimensions are smaller or equals to required dimensions return false; }*/ if (!$this->Application->ConfigValue('ForceImageMagickResize') && function_exists('imagecreatefromjpeg')) { //...... //...... } else { $crop_command = null; $optimize_params = null; $overwrite = getArrayValue($params,'overwrite'); $ImageMagickPath = ($this->Application->ConfigValue('ImageMagickPath'))?$this->Application->ConfigValue('ImageMagickPath'):''; //crop if (isset($params['gravity'])) { $gravity = ($params['gravity'])?$params['gravity']:null; } if ($gravity) { $width = $params['max_width']; $height = $params['max_height']; if ($width >= $height) $height = $width; else $width = $height; $crop_command = '-gravity '.$gravity.' -crop '.$params['max_width'].'x'.$params['max_height'].'+0+0 +repage'; } else { $width = $params['target_width']; $height = $params['target_height']; } $optimize_JPG_params = ($params['optimize_JPG'])?$params['optimize_JPG']:null; $optimize_PNG_params = ($params['optimize_PNG'])?$params['optimize_PNG']:null; // if overwrite exists set original filename instead generated $dst_image = ($overwrite)?$src_image:$params['dst_image']; if ($mime_type == 'image/jpeg') { $convert_command = $ImageMagickPath.'/convert '.$optimize_JPG_params.' '.str_replace(' ','\ ',$src_image).' -resize '.$width.'x'.$height.' '.$crop_command.' '.str_replace(' ','\ ',$dst_image); } elseif (in_array($mime_type, array('image/gif','image/png'))) { $convert_command = $ImageMagickPath.'/convert '.$optimize_PNG_params.' '.str_replace(' ','\ ',$src_image).' -resize '.$width.'x'.$height.' '.$crop_command.' '.str_replace(' ','\ ',$dst_image); } else { $convert_command = $ImageMagickPath.'/convert '.str_replace(' ','\ ',$src_image).' -resize '.$width.'x'.$height.' '.$crop_command.' '.str_replace(' ','\ ',$dst_image); } exec($convert_command, $shell_output, $exec_status); $wm_filename = ($params['wm_filename'])?$params['wm_filename']:null; // if watermark and overwrite exists copy new image to thumb path for vatermarking if (!$exec_status && !$gravity && $wm_filename && $overwrite) { copy($dst_image, $params['dst_image']); } if (!$exec_status && $wm_filename && !$gravity) { $file_info = getimagesize($wm_filename); $PosH = $params['target_width'] - $params['h_margin'] - $file_info[0]; $PosV = $params['target_height'] - $params['v_margin'] - $file_info[1]; $command = ' -geometry +'.$PosH.'+'.$PosV.' '.$wm_filename.' '.$params['dst_image'].' '.$params['dst_image']; exec($ImageMagickPath.'/composite'.$command, $shell_output, $exec_status); } if ($overwrite && $exec_status == 0 && !$wm_filename) { return $exec_status != 0; // return original filename } return $exec_status == 0; } return false; }
I think that for sites which few pages it's not true. it can be useful to someone else :)