diff --git a/ChangeLog b/ChangeLog
index 7d6c76c5e3c3b2cf55030a5927f0f5d9f8283466..0fcbf856d1d9d784510ecbbf62365f7a0af7f4c8 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -3,10 +3,11 @@ English Dolibarr ChangeLog
 ***** ChangeLog for 2.9 compared to 2.8 *****
 
 For users:
-New: Add "payment due before" field in invoice exports
+- New: Add "payment due before" field in invoice exports
+- New: Add feature to resize or crop image files (for products photos)
 
 For developers:
-
+- More comments in code
 
 
 ***** ChangeLog for 2.8 compared to 2.7 *****
diff --git a/htdocs/core/photos_resize.php b/htdocs/core/photos_resize.php
index aacd0c548b7f1d491c96c603c4cfda1686d90044..0e6bad3b1146e4774e6671e18d6f9d51e2cd8ee0 100644
--- a/htdocs/core/photos_resize.php
+++ b/htdocs/core/photos_resize.php
@@ -1,8 +1,6 @@
 <?php
-/* Copyright (C) 2001-2007 Rodolphe Quiedeville <rodolphe@quiedeville.org>
- * Copyright (C) 2004-2009 Laurent Destailleur  <eldy@users.sourceforge.net>
- * Copyright (C) 2005      Eric Seigne          <eric.seigne@ryxeo.com>
- * Copyright (C) 2005-2009 Regis Houssin        <regis@dolibarr.fr>
+/* Copyright (C) 2010 Laurent Destailleur  <eldy@users.sourceforge.net>
+ * Copyright (C) 2009 Meos
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -26,19 +24,17 @@ require_once(DOL_DOCUMENT_ROOT."/product.class.php");
 
 $langs->load("products");
 
-// Security check
+$modulepart=$_REQUEST['modulepart']?$_REQUEST['modulepart']:'produit|service';
 if (isset($_GET["id"]))
 {
 	$id = isset($_GET["id"])?$_GET["id"]:'';
 }
-$result=restrictedArea($user,'produit|service',$id,'product','','',$fieldid);
-
-
 $original_file = isset($_REQUEST["file"])?urldecode($_REQUEST["file"]):'';
 
-$langs->load("products");
-
-if (!$user->rights->produit->lire) accessforbidden();
+// Security check
+if ($modulepart=='produit|service') $result=restrictedArea($user,'produit|service',$id,'product','','',$fieldid);
+else accessforbidden('Bad value for modulepart');
+if ($modulepart=='produit|service' && (! $user->rights->produit->lire && ! $user->rights->service->lire)) accessforbidden();
 
 
 
@@ -48,8 +44,8 @@ if (!$user->rights->produit->lire) accessforbidden();
 
 if ($_POST["action"] == 'confirm_resize' && (isset($_POST["file"]) != "") && (isset($_POST["sizex"]) != "") && (isset($_POST["sizey"]) != ""))
 {
-	$fullpath=$conf->produit->dir_output."/".$_POST["file"];
-	$result=dol_imageResize($fullpath,$_POST['sizex'],$_POST['sizey']);
+	$fullpath=$conf->produit->dir_output."/".$original_file;
+	$result=dol_imageResizeOrCrop($fullpath,0,$_POST['sizex'],$_POST['sizey']);
 
 	if ($result == $fullpath)
 	{
@@ -65,16 +61,22 @@ if ($_POST["action"] == 'confirm_resize' && (isset($_POST["file"]) != "") && (is
 }
 
 // Crop d'une image
-if ($_POST["action"] == 'confirm_crop' && $_POST["file"])
+if ($_POST["action"] == 'confirm_crop')
 {
-	// TODO Add function to crop image in images.lib.php
-/*	$thumb = new Imagick($conf->produit->dir_output."/".urldecode($_POST["file"]));
-	$thumb->cropImage($_POST['w'], $_POST['h'], $_POST['x'], $_POST['y']);
-	$thumb->writeImage($conf->produit->dir_output."/".urldecode($_POST["file"]));
-	$thumb->destroy();
-*/
-	header("Location: ".DOL_URL_ROOT."/product/photos.php?id=".$_POST["product"].'&action=addthumb&file='.urldecode($_POST["file"]));
-	exit;
+	$fullpath=$conf->produit->dir_output."/".$original_file;
+	$result=dol_imageResizeOrCrop($fullpath,1,$_POST['w'],$_POST['h'],$_POST['x'],$_POST['y']);
+
+	if ($result == $fullpath)
+	{
+		header("Location: ".DOL_URL_ROOT."/product/photos.php?id=".$_POST["product"].'&action=addthumb&file='.urldecode($_POST["file"]));
+		exit;
+	}
+	else
+	{
+		$mesg=$result;
+		$_GET['file']=$_POST["file"];
+		$_GET['id']=$_POST["id"];
+	}
 }
 
 
@@ -92,17 +94,18 @@ if ($mesg) print '<div class="error">'.$mesg.'</div>';
 $infoarray=dol_getImageSize($conf->produit->dir_output."/".urldecode($_GET["file"]));
 $height=$infoarray['height'];
 $width=$infoarray['width'];
-print $langs->trans("Size").': </p>
-   <ul>
+print $langs->trans("CurrentInformationOnImage").':';
+print '<ul>
    <li>'.$langs->trans("Width").': '.$width.' px</li>
    <li>'.$langs->trans("Height").': '.$height.' px</li>
    </ul>';
 
 print '<br>';
-print_fiche_titre($langs->trans("Resize"),'','');
 
 print '<form name="redim_file" action="'.$_SERVER["PHP_SELF"].'?id='.$_GET['id'].'" method="POST">';
 
+print '<fieldset id="redim_file">';
+print '<legend>'.$langs->trans("Resize").'</legend>';
 print $langs->trans("ResizeDesc").'<br>';
 print $langs->trans("NewLength").': <input class="flat" name="sizex" size="10" type="text" > px <br /> ';
 print $langs->trans("NewHeight").': <input class="flat" name="sizey" size="10" type="text" > px &nbsp; <br />';
@@ -111,16 +114,17 @@ print '<input type="hidden" name="action" value="confirm_resize" />';
 print '<input type="hidden" name="product" value="'.$_GET['id'].'" />';
 print '<input type="hidden" name="id" value="'.$_GET['id'].'" />';
 print '<br><input class="button" name="sendit" value="'.dol_escape_htmltag($langs->trans("Resize")).'" type="submit" />';
+print '</fieldset>';
 print '<br></form>';
-print '<br>';
 
 /*
  * Recadrage d'une image
  */
 
 print '<br>';
-print_fiche_titre($langs->trans("Recenter"),'','');
 
+print '<fieldset id="redim_file">';
+print '<legend>'.$langs->trans("Recenter").'</legend>';
 print $langs->trans("DefineNewAreaToPick").'...<br>';
 print '<br>';
 print '<img style="border: 1px solid #888888;" src="'.DOL_URL_ROOT.'/viewimage.php?modulepart=product&file='.$original_file.'" alt="Taille origine" id="cropbox" />';
@@ -145,6 +149,7 @@ print '<form action="'.$_SERVER["PHP_SELF"].'?id='.$_GET['id'].'" method="post"
 	  <input type="hidden" name="id" value="'.$_GET['id'].'" />
       <br><input type="submit" class="button" value="'.dol_escape_htmltag($langs->trans("Recenter")).'" />
    </form>';
+print '</fieldset>';
 
 llxFooter('$Date$ - $Revision$');
 ?>
\ No newline at end of file
diff --git a/htdocs/langs/en_US/other.lang b/htdocs/langs/en_US/other.lang
index c697d2f783d313abfa5cb3841cc0bc191783b52b..27e5af34344e9baf8dd435477e82a8f474d3268a 100644
--- a/htdocs/langs/en_US/other.lang
+++ b/htdocs/langs/en_US/other.lang
@@ -120,6 +120,7 @@ NewLength=New width
 NewHeight=New height
 NewSizeAfterCropping=New size after cropping
 DefineNewAreaToPick=Define new area on image to pick (left click on image then drag until you reach the opposite corner)
+CurrentInformationOnImage=Informations on current image
 
 ##### Bookmark #####
 Bookmark=Bookmark
diff --git a/htdocs/langs/fr_FR/other.lang b/htdocs/langs/fr_FR/other.lang
index 72603482b5afd6f9d668ee4926bbee8030f7c64f..ff1cf51a3a26b99f6b2465388768c35db7135f1f 100644
--- a/htdocs/langs/fr_FR/other.lang
+++ b/htdocs/langs/fr_FR/other.lang
@@ -120,6 +120,7 @@ NewLength=Nouvelle largeur
 NewHeight=Nouvelle hauteur
 NewSizeAfterCropping=Nouvelles dimensions après recadrage
 DefineNewAreaToPick=Définissez la zone d'image à conserver (clic gauche sur l'image puis drag vers les coin opposé)
+CurrentInformationOnImage=Informations courante sur l'image
 
 ##### Bookmark #####
 Bookmark=Marque page
diff --git a/htdocs/lib/images.lib.php b/htdocs/lib/images.lib.php
index ba7276ba60bd10090731b2103c2b2b75e17d26d3..c8257ec193d545313e00eea5de9a51ae487d9b7a 100644
--- a/htdocs/lib/images.lib.php
+++ b/htdocs/lib/images.lib.php
@@ -26,7 +26,7 @@
 
 
 /**
- *    	\brief     	Return size of image file on disk
+ *    	\brief     	Return size of image file on disk (Supported extensions are gif, jpg, png and bmp)
  * 		\param		$file		Full path name of file
  * 		\return		Array		array('width'=>width, 'height'=>height)
  */
@@ -50,21 +50,23 @@ function dol_getImageSize($file)
 
 
 /**
- *    	\brief     	Resize an image file
- *    	\brief     	Supported extensions are jpg and png
- *    	\param     	file           	Chemin du fichier image a redimensionner
- *    	\param     	newWidth       	Largeur maximum que dois faire la miniature (-1=unchanged)
- *    	\param     	newHeight      	Hauteur maximum que dois faire l'image (-1=unchanged)
+ *    	\brief     	Resize or crop an image file (Supported extensions are gif, jpg, png and bmp)
+ *    	\param     	file           	Path of file to resize/crop
+ * 		\param		mode			0=Resize, 1=Crop
+ *    	\param     	newWidth       	Largeur maximum que dois faire l'image destination (0=keep ratio)
+ *    	\param     	newHeight      	Hauteur maximum que dois faire l'image destination (0=keep ratio)
+ * 		\param		src_x			Position of croping image in source image (not use if mode=0)
+ * 		\param		src_y			Position of croping image in source image (not use if mode=0)
  *		\return		int				File name if OK, error message if KO
  *		\remarks					With file=myfile.jpg -> myfile_small.jpg
  */
-function dol_imageResize($file, $newWidth, $newHeight)
+function dol_imageResizeOrCrop($file, $mode, $newWidth, $newHeight, $src_x=0, $src_y=0)
 {
 	require_once(DOL_DOCUMENT_ROOT."/lib/functions2.lib.php");
 
 	global $conf,$langs;
 
-	dol_syslog("dol_imageResize file=".$file." newWidth=".$newWidth." newHeight=".$newHeight);
+	dol_syslog("dol_imageResizeOrCrop file=".$file." mode=".$mode." newWidth=".$newWidth." newHeight=".$newHeight." src_x=".$src_x." src_y=".$src_y);
 
 	// Clean parameters
 	$file=trim($file);
@@ -82,14 +84,19 @@ function dol_imageResize($file, $newWidth, $newHeight)
 	}
 	elseif(image_format_supported($file) < 0)
 	{
-		return 'This file '.$file.' does not seem to be an image format file name.';
+		return 'This filename '.$file.' does not seem to be an image filename.';
 	}
-	elseif(!is_numeric($newWidth) && !is_numeric($newHeight)){
+	elseif(!is_numeric($newWidth) && !is_numeric($newHeight))
+	{
 		return 'Wrong value for parameter newWidth or newHeight';
 	}
-	elseif ($newWidth <= 0 && $newHeight <= 0)
+	elseif ($mode == 0 && $newWidth <= 0 && $newHeight <= 0)
+	{
+		return 'At least newHeight or newWidth must be defined for resizing';
+	}
+	elseif ($mode == 1 && ($newWidth <= 0 || $newHeight <= 0))
 	{
-		return 'At least newHeight or newWidth must be defined';
+		return 'Both newHeight or newWidth must be defined for croping';
 	}
 
 	$fichier = realpath($file); 	// Chemin canonique absolu de l'image
@@ -99,13 +106,16 @@ function dol_imageResize($file, $newWidth, $newHeight)
 	$imgWidth = $infoImg[0]; // Largeur de l'image
 	$imgHeight = $infoImg[1]; // Hauteur de l'image
 
-	if ($newWidth  <= 0)
+	if ($mode == 0)	// If resize, we check parameters
 	{
-		$newWidth=intval(($newHeight / $imgHeight) * $imgWidth);	// Keep ratio
-	}
-	if ($newHeight <= 0)
-	{
-		$newHeight=intval(($newWidth / $imgWidth) * $imgHeight);	// Keep ratio
+		if ($newWidth  <= 0)
+		{
+			$newWidth=intval(($newHeight / $imgHeight) * $imgWidth);	// Keep ratio
+		}
+		if ($newHeight <= 0)
+		{
+			$newHeight=intval(($newWidth / $imgWidth) * $imgHeight);	// Keep ratio
+		}
 	}
 
 	$imgfonction='';
@@ -138,23 +148,23 @@ function dol_imageResize($file, $newWidth, $newHeight)
 	{
 		case 1:	// Gif
 			$img = imagecreatefromgif($fichier);
-			$extImg = '.gif'; // Extension de l'image
-			$newquality='NU';
+			$extImg = '.gif';	// File name extension of image
+			$newquality='NU';	// Quality is not used for this format
 			break;
 		case 2:	// Jpg
 			$img = imagecreatefromjpeg($fichier);
-			$extImg = '.jpg'; // Extension de l'image
-			$newquality=100;
+			$extImg = '.jpg';
+			$newquality=100;	// % quality maximum
 			break;
 		case 3:	// Png
 			$img = imagecreatefrompng($fichier);
 			$extImg = '.png';
-			$newquality=10;
+			$newquality=0;		// No compression (0-9)
 			break;
 		case 4:	// Bmp
 			$img = imagecreatefromwbmp($fichier);
 			$extImg = '.bmp';
-			$newquality='NU';
+			$newquality='NU';	// Quality is not used for this format
 			break;
 	}
 
@@ -201,9 +211,9 @@ function dol_imageResize($file, $newWidth, $newHeight)
 	}
 	if (function_exists("imagefill")) imagefill($imgThumb, 0, 0, $trans_colour);
 
-	dol_syslog("dol_imageResize: convert image from ($imgWidth x $imgHeight) to ($newWidth x $newHeight) as $extImg, newquality=$newquality");
+	dol_syslog("dol_imageResizeOrCrop: convert image from ($imgWidth x $imgHeight) at position ($src_x x $src_y) to ($newWidth x $newHeight) as $extImg, newquality=$newquality");
 	//imagecopyresized($imgThumb, $img, 0, 0, 0, 0, $thumbWidth, $thumbHeight, $imgWidth, $imgHeight); // Insere l'image de base redimensionnee
-	imagecopyresampled($imgThumb, $img, 0, 0, 0, 0, $newWidth, $newHeight, $imgWidth, $imgHeight); // Insere l'image de base redimensionnee
+	imagecopyresampled($imgThumb, $img, 0, 0, $src_x, $src_y, $newWidth, $newHeight, ($mode==0?$imgWidth:$newWidth), ($mode==0?$imgHeight:$newHeight)); // Insere l'image de base redimensionnee
 
 	$imgThumbName = $file;
 
@@ -234,13 +244,14 @@ function dol_imageResize($file, $newWidth, $newHeight)
 	// Free memory
 	imagedestroy($imgThumb);
 
+	clearstatcache();	// File was replaced by a modified on, so we clear file caches.
+
 	return $imgThumbName;
 }
 
 
 /**
- *    	\brief     Create a thumbnail from an image file (une small et un mini)
- *    	\brief     Les extensions prises en compte sont jpg et png
+ *    	\brief     Create 2 thumbnails from an image file: one small and one mini (Supported extensions are gif, jpg, png and bmp)
  *    	\param     file           	Chemin du fichier image a redimensionner
  *    	\param     maxWidth       	Largeur maximum que dois faire la miniature (-1=unchanged, 160 par defaut)
  *    	\param     maxHeight      	Hauteur maximum que dois faire l'image (-1=unchanged, 120 par defaut)
diff --git a/htdocs/product.class.php b/htdocs/product.class.php
index 03dea67de58796726e152826138992856b44d835..49fc19b2df4ab10949f8ac68275f9c25d70b8869 100644
--- a/htdocs/product.class.php
+++ b/htdocs/product.class.php
@@ -130,7 +130,7 @@ class Product extends CommonObject
 		$this->stock_reel = 0;
 		$this->seuil_stock_alerte = 0;
 		$this->canvas = '';
-		
+
 		if ($this->id > 0) $this->fetch($this->id);
 	}
 
@@ -2397,7 +2397,7 @@ class Product extends CommonObject
 
 	/**
 	 *    \brief      Affiche toutes les photos du produit (nbmax maximum)
-	 *    \param      sdir        Repertoire a scanner
+	 *    \param      sdir        Directory to scan
 	 *    \param      size        0=taille origine, 1 taille vignette
 	 *    \param      nbmax       Nombre maximum de photos (0=pas de max)
 	 *    \param      nbbyrow     Nombre vignettes par ligne (si mode vignette)
@@ -2408,6 +2408,7 @@ class Product extends CommonObject
 		global $conf,$user,$langs;
 
 		include_once(DOL_DOCUMENT_ROOT ."/lib/files.lib.php");
+		include_once(DOL_DOCUMENT_ROOT ."/lib/images.lib.php");
 
 		$pdir = get_exdir($this->id,2) . $this->id ."/photos/";
 		$dir = $sdir . '/'. $pdir;
@@ -2424,7 +2425,7 @@ class Product extends CommonObject
 			{
 				$photo='';
 
-				if (! utf8_check($file)) $file=utf8_encode($file);	// To be sure date is stored in UTF8 in memory
+				if (! utf8_check($file)) $file=utf8_encode($file);	// To be sure file is stored in UTF8 in memory
 
 				if (dol_is_file($dir.$file))
 				{
@@ -2440,20 +2441,28 @@ class Product extends CommonObject
 							if (! dol_is_file($dirthumb.$photo_vignette)) $photo_vignette='';
 						}
 
+						// Get filesize of original file
+						$imgarray=dol_getImageSize($dir.$photo);
 
 						if ($nbbyrow && $nbphoto == 1) print '<table width="100%" valign="top" align="center" border="0" cellpadding="2" cellspacing="2">';
 
 						if ($nbbyrow && ($nbphoto % $nbbyrow == 1)) print '<tr align=center valign=middle border=1>';
 						if ($nbbyrow) print '<td width="'.ceil(100/$nbbyrow).'%" class="photo">';
 
-						print '<a href="'.DOL_URL_ROOT.'/viewimage.php?modulepart=product&file='.urlencode($pdir.$photo).'" alt="Taille origine" target="_blank">';
+						print "\n";
+						print '<a href="'.DOL_URL_ROOT.'/viewimage.php?modulepart=product&file='.urlencode($pdir.$photo).'" target="_blank">';
 
-						// Si fichier vignette disponible, on l'utilise, sinon on utilise photo origine
-						if ($photo_vignette) {
-							print '<img class="photo" border="0" height="120" src="'.DOL_URL_ROOT.'/viewimage.php?modulepart=product&file='.urlencode($pdirthumb.$photo_vignette).'">';
+						// Show image (width height=120)
+						// Si fichier vignette disponible et image source trop grande, on utilise la vignette, sinon on utilise photo origine
+						$alt=$langs->transnoentitiesnoconv('File').': '.$pdir.$photo;
+						$alt.=' - '.$langs->transnoentitiesnoconv('Size').': '.$imgarray['width'].'x'.$imgarray['height'];
+						if ($photo_vignette && $imgarray['height'] > 120) {
+							print '<!-- Show thumb -->';
+							print '<img class="photo" border="0" height="120" src="'.DOL_URL_ROOT.'/viewimage.php?modulepart=product&file='.urlencode($pdirthumb.$photo_vignette).'" title="'.dol_escape_htmltag($alt).'">';
 						}
 						else {
-							print '<img class="photo" border="0" height="120" src="'.DOL_URL_ROOT.'/viewimage.php?modulepart=product&file='.urlencode($pdir.$photo).'">';
+							print '<!-- Show original file -->';
+							print '<img class="photo" border="0" height="120" src="'.DOL_URL_ROOT.'/viewimage.php?modulepart=product&file='.urlencode($pdir.$photo).'" title="'.dol_escape_htmltag($alt).'">';
 						}
 
 						print '</a>';
@@ -2477,6 +2486,7 @@ class Product extends CommonObject
 								print img_delete().'</a>';
 							}
 						}
+						print "\n";
 
 						if ($nbbyrow) print '</td>';
 						if ($nbbyrow && ($nbphoto % $nbbyrow == 0)) print '</tr>';
@@ -2491,7 +2501,11 @@ class Product extends CommonObject
 						{
 							if ($user->rights->produit->creer || $user->rights->service->creer)
 							{
-								print '<a href="'.$_SERVER["PHP_SELF"].'?id='.$_GET["id"].'&amp;action=delete&amp;file='.urlencode($pdir.$viewfilename).'">';
+								// Link to resize
+			               		print '<a href="'.DOL_URL_ROOT.'/core/photos_resize.php?id='.$_GET["id"].'&amp;file='.urlencode($pdir.$viewfilename).'" title="'.dol_escape_htmltag($langs->trans("Resize")).'">'.img_picto($langs->trans("Resize"),DOL_URL_ROOT.'/theme/common/transform-crop-and-resize','',1).'</a> &nbsp; ';
+
+			               		// Link to delete
+			               		print '<a href="'.$_SERVER["PHP_SELF"].'?id='.$_GET["id"].'&amp;action=delete&amp;file='.urlencode($pdir.$viewfilename).'">';
 								print img_delete().'</a>';
 							}
 						}