Index: branches/5.2.x/core/units/helpers/image_helper.php
===================================================================
--- branches/5.2.x/core/units/helpers/image_helper.php
+++ branches/5.2.x/core/units/helpers/image_helper.php
@@ -35,11 +35,15 @@
 		 * 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)
+		 * @return Array sample result: Array('max_width' => 300, 'max_height' => 500, 'wm_filename' => 'inc/wm.png', 'h_margin' => 'c', 'v_margin' => -20, 'quality' => 100, output_format => 'auto', orientation => 'manual')
+		 *
+		 * @throws InvalidArgumentException When requested quality is out of 0..100 range.
+		 * @throws InvalidArgumentException When requested output_format is out of [jpg,png,gif,bmp,auto] range.
+		 * @throws InvalidArgumentException When requested orientation is out of [auto,manual,portrait,landscape] range.
 		 */
 		function parseFormat($format)
 		{
-			$res = Array ();
+			$res = array('quality' => 100, 'output_format' => 'auto', 'orientation' => 'manual');
 
 			$format_parts = explode(';', $format);
 			foreach ($format_parts as $format_part) {
@@ -47,6 +51,35 @@
 					$res['max_width'] = $regs[1];
 					$res['max_height'] = $regs[2];
 				}
+				elseif ( preg_match('/^quality:(.*)$/', $format_part, $regs) ) {
+					$quality = (int)$regs[1];
+
+					if ( $quality > 100 || $quality < 0 || (string)$quality !== $regs[1] ) {
+						throw new InvalidArgumentException(
+							'Quality value "' . $regs[1] . '" is out of 0..100 integer range.'
+						);
+					}
+
+					$res['quality'] = $quality;
+				}
+				elseif ( preg_match('/^output_format:(.*)$/', $format_part, $regs) ) {
+					if ( !in_array($regs[1], array('jpg', 'png', 'gif', 'bmp', 'auto')) ) {
+						throw new InvalidArgumentException(
+							'Output format value "' . $regs[1] . '" is out of [jpg,png,gif,bmp,auto] range.'
+						);
+					}
+
+					$res['output_format'] = $regs[1];
+				}
+				elseif ( preg_match('/^orientation:(.*)$/', $format_part, $regs) ) {
+					if ( !in_array($regs[1], array('auto', 'manual', 'portrait', 'landscape')) ) {
+						throw new InvalidArgumentException(
+							'Orientation value "' . $regs[1] . '" is out of [auto,manual,portrait,landscape] range.'
+						);
+					}
+
+					$res['orientation'] = $regs[1];
+				}
 				elseif (preg_match('/^wm:([^\|]*)\|([^\|]*)\|([^\|]*)$/', $format_part, $regs)) {
 					$res['wm_filename'] = FULL_PATH.THEMES_PATH.'/'.$regs[1];
 					$res['h_margin'] = strtolower($regs[2]);
@@ -113,6 +146,16 @@
 			}
 
 			if ( !$this->isSVG($src_image) && ($params['max_width'] > 0 || $params['max_height'] > 0) ) {
+				if ( $this->shouldRotateDimensions($src_image, $params['max_width'], $params['max_height'], $params) ) {
+					list ($params['max_width'], $params['max_height']) = array(
+						$params['max_height'], $params['max_width'],
+					);
+
+					if ( isset($params['crop_x']) && isset($params['crop_y']) ) {
+						list ($params['crop_x'], $params['crop_y']) = array($params['crop_y'], $params['crop_x']);
+					}
+				}
+
 				list ($params['target_width'], $params['target_height'], $needs_resize) = $this->GetImageDimensions($src_image, $params['max_width'], $params['max_height'], $params);
 
 				if (!is_numeric($params['max_width'])) {
@@ -136,13 +179,17 @@
 				$transform_keys = Array ('crop_x', 'crop_y', 'fill', 'wm_filename');
 
 				// Resize required OR watermarking required -> change resulting image name !
-				if ( $needs_resize || array_intersect(array_keys($params), $transform_keys) ) {
+				if ( $needs_resize
+					|| array_intersect(array_keys($params), $transform_keys)
+					|| $this->shouldChangeOutputFormat($src_image, $params['output_format'])
+				) {
 					// Escape replacement patterns, like "\<number>".
 					$src_path_escaped = preg_replace('/(\\\[\d]+)/', '\\\\\1', $src_path);
 					$params_hash = kUtil::crc32(serialize($this->fileHelper->makeRelative($params)));
+					$file_extension = $params['output_format'] === 'auto' ? '\\2' : $params['output_format'];
 					$dst_image = preg_replace(
 						'/^' . preg_quote($src_path, '/') . '(.*)\.(.*)$/',
-						$src_path_escaped . '\\1_' . $params_hash . '.\\2',
+						$src_path_escaped . '\\1_' . $params_hash . '.' . $file_extension,
 						$src_image
 					);
 
@@ -217,6 +264,18 @@
 
 				list ($read_function, $write_function, $file_extension) = explode(':', $resize_map[$mime_type]);
 
+				$output_format_map = array(
+					'jpg' => 'imagejpeg:jpg',
+					'png' => 'imagepng:png',
+					'gif' => 'imagegif:gif',
+					'bmp' => 'imagejpeg:bmp',
+				);
+				$output_format = $params['output_format'];
+
+				if ( isset($output_format_map[$output_format]) ) {
+					list ($write_function, $file_extension) = explode(':', $output_format_map[$output_format]);
+				}
+
 				// when source image has large dimensions (over 1MB filesize), then 16M is not enough
 				kUtil::setResourceLimit();
 
@@ -253,7 +312,11 @@
 						return @$write_function($dst_image_rs, $params['dst_image']);
 					}
 
-					return @$write_function($dst_image_rs, $params['dst_image'], $write_function == 'imagepng' ? 0 : 100);
+					if ( $write_function == 'imagepng' ) {
+						$params['quality'] = $this->convertQualityToCompression($params['quality']);
+					}
+
+					return @$write_function($dst_image_rs, $params['dst_image'], $params['quality']);
 				}
 			}
 			else {
@@ -267,6 +330,18 @@
 		}
 
 		/**
+		 * Converts quality to compression
+		 *
+		 * @param integer $quality Quality.
+		 *
+		 * @return integer
+		 */
+		protected function convertQualityToCompression($quality)
+		{
+			return round((100 - $quality) / 10);
+		}
+
+		/**
 		 * Preserve transparency for GIF and PNG images
 		 *
 		 * @param resource $src_image_rs
@@ -557,6 +632,76 @@
 		}
 
 		/**
+		 * Determines when dimensions must be rotated
+		 *
+		 * @param string  $src_image  Source image path.
+		 * @param integer $dst_width  Destination width.
+		 * @param integer $dst_height Destination height.
+		 * @param array   $params     Parameters.
+		 *
+		 * @return boolean
+		 * @throws InvalidArgumentException When orientation is "auto", but some of $dst_width/$dst_height is empty.
+		 */
+		protected function shouldRotateDimensions($src_image, $dst_width, $dst_height, array $params)
+		{
+			$orientation = $params['orientation'];
+
+			if ( $orientation === 'manual' ) {
+				return false;
+			}
+
+			if ( $orientation === 'auto' ) {
+				if ( $dst_width === '' || $dst_height === '' ) {
+					throw new InvalidArgumentException('Both width & height parameters must be specified.');
+				}
+
+				$resized_orientation = $dst_width > $dst_height ? 'landscape' : 'portrait';
+			}
+			else {
+				$resized_orientation = $orientation;
+			}
+
+			list ($src_width, $src_height) = $this->getImageInfo($src_image);
+
+			$src_image_orientation = $src_width > $src_height ? 'landscape' : 'portrait';
+
+			return $src_image_orientation != $resized_orientation;
+		}
+
+		/**
+		 * Determines when output format should be changed.
+		 *
+		 * @param string $src_image     Source image path.
+		 * @param string $output_format Output format.
+		 *
+		 * @return boolean
+		 */
+		protected function shouldChangeOutputFormat($src_image, $output_format)
+		{
+			if ( $output_format === 'auto' ) {
+				return false;
+			}
+
+			$image_info = $this->getImageInfo($src_image);
+
+			if ( !$image_info ) {
+				return false;
+			}
+
+			$resize_map = array(
+				'image/jpeg' => 'jpg',
+				'image/gif' => 'gif',
+				'image/png' => 'png',
+				'image/bmp' => 'bmp',
+				'image/x-ms-bmp' => 'bmp',
+			);
+
+			$current_mime = $image_info['mime'];
+
+			return $resize_map[$current_mime] !== $output_format;
+		}
+
+		/**
 		 * Returns maximal image size (width & height) among fields specified
 		 *
 		 * @param kDBItem $object