2019-06-07 08:59:07 +00:00
/ *
*
* ( c ) Copyright Ascensio System Limited 2010 - 2018
*
* This program is freeware . You can redistribute it and / or modify it under the terms of the GNU
* General Public License ( GPL ) version 3 as published by the Free Software Foundation ( https : //www.gnu.org/copyleft/gpl.html).
* In accordance with Section 7 ( a ) of the GNU GPL its Section 15 shall be amended to the effect that
* Ascensio System SIA expressly excludes the warranty of non - infringement of any third - party rights .
*
* THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY ; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR
* FITNESS FOR A PARTICULAR PURPOSE . For more details , see GNU GPL at https : //www.gnu.org/copyleft/gpl.html
*
* You can contact Ascensio System SIA by email at sales @onlyoffice . com
*
* The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display
* Appropriate Legal Notices , as required under Section 5 of the GNU GPL version 3.
*
* Pursuant to Section 7 § 3 ( b ) of the GNU GPL you must retain the original ONLYOFFICE logo which contains
* relevant author attributions when distributing the software . If the display of the logo in its graphic
* form is not reasonably feasible for technical reasons , you must include the words "Powered by ONLYOFFICE"
* in every copy of the program you distribute .
* Pursuant to Section 7 § 3 ( e ) we decline to grant you any rights under trademark law for use of our trademarks .
*
* /
using System ;
using System.Collections.Concurrent ;
using System.Collections.Generic ;
using System.Drawing ;
using System.Drawing.Drawing2D ;
using System.Drawing.Imaging ;
using System.IO ;
using System.Linq ;
using System.Text.RegularExpressions ;
using ASC.Common.Caching ;
using ASC.Common.Logging ;
using ASC.Common.Threading.Workers ;
using ASC.Core ;
2019-08-08 09:26:58 +00:00
using ASC.Core.Tenants ;
2019-06-07 08:59:07 +00:00
using ASC.Data.Storage ;
using ASC.Web.Core.Utility.Skins ;
using ASC.Web.Studio.Utility ;
namespace ASC.Web.Core.Users
{
internal class ResizeWorkerItem
{
public ResizeWorkerItem ( Guid userId , byte [ ] data , long maxFileSize , Size size , IDataStore dataStore , UserPhotoThumbnailSettings settings )
{
2019-08-15 13:56:54 +00:00
UserId = userId ;
Data = data ;
MaxFileSize = maxFileSize ;
Size = size ;
DataStore = dataStore ;
Settings = settings ;
2019-06-07 08:59:07 +00:00
}
2019-08-15 13:56:54 +00:00
public Size Size { get ; }
2019-06-07 08:59:07 +00:00
2019-08-15 13:56:54 +00:00
public IDataStore DataStore { get ; }
2019-06-07 08:59:07 +00:00
2019-08-15 13:56:54 +00:00
public long MaxFileSize { get ; }
2019-06-07 08:59:07 +00:00
2019-08-15 13:56:54 +00:00
public byte [ ] Data { get ; }
2019-06-07 08:59:07 +00:00
2019-08-15 13:56:54 +00:00
public Guid UserId { get ; }
2019-06-07 08:59:07 +00:00
2019-08-15 13:56:54 +00:00
public UserPhotoThumbnailSettings Settings { get ; }
2019-06-07 08:59:07 +00:00
public override bool Equals ( object obj )
{
2019-08-15 14:36:08 +00:00
if ( obj is null ) return false ;
2019-06-07 08:59:07 +00:00
if ( ReferenceEquals ( this , obj ) ) return true ;
if ( obj . GetType ( ) ! = typeof ( ResizeWorkerItem ) ) return false ;
return Equals ( ( ResizeWorkerItem ) obj ) ;
}
public bool Equals ( ResizeWorkerItem other )
{
2019-08-15 14:36:08 +00:00
if ( other is null ) return false ;
2019-06-07 08:59:07 +00:00
if ( ReferenceEquals ( this , other ) ) return true ;
return other . UserId . Equals ( UserId ) & & other . MaxFileSize = = MaxFileSize & & other . Size . Equals ( Size ) ;
}
public override int GetHashCode ( )
{
unchecked
{
2019-08-15 13:03:57 +00:00
var result = UserId . GetHashCode ( ) ;
2019-06-07 08:59:07 +00:00
result = ( result * 397 ) ^ MaxFileSize . GetHashCode ( ) ;
result = ( result * 397 ) ^ Size . GetHashCode ( ) ;
return result ;
}
}
}
public class UserPhotoManager
{
2019-08-28 09:15:33 +00:00
private static readonly ConcurrentDictionary < CacheSize , ConcurrentDictionary < Guid , string > > Photofiles = new ConcurrentDictionary < CacheSize , ConcurrentDictionary < Guid , string > > ( ) ;
2019-07-22 12:31:09 +00:00
private static readonly ICacheNotify < UserPhotoManagerCacheItem > CacheNotify ;
2019-06-07 08:59:07 +00:00
static UserPhotoManager ( )
{
try
{
2019-07-22 12:31:09 +00:00
CacheNotify = new KafkaCache < UserPhotoManagerCacheItem > ( ) ;
2019-06-07 08:59:07 +00:00
2019-07-22 12:31:09 +00:00
CacheNotify . Subscribe ( ( data ) = >
2019-06-07 08:59:07 +00:00
{
2019-08-27 14:57:06 +00:00
var userId = new Guid ( data . UserID . ToByteArray ( ) ) ;
2019-08-28 09:15:33 +00:00
Photofiles . GetOrAdd ( data . Size , ( r ) = > new ConcurrentDictionary < Guid , string > ( ) ) [ userId ] = data . FileName ;
2019-07-22 12:31:09 +00:00
} , CacheNotifyAction . InsertOrUpdate ) ;
CacheNotify . Subscribe ( ( data ) = >
{
2019-08-27 14:57:06 +00:00
var userId = new Guid ( data . UserID . ToByteArray ( ) ) ;
2019-07-22 12:31:09 +00:00
try
2019-06-07 08:59:07 +00:00
{
2019-09-13 13:15:51 +00:00
foreach ( var s in ( CacheSize [ ] ) Enum . GetValues ( typeof ( CacheSize ) ) )
{
Photofiles . TryGetValue ( s , out var dict ) ;
dict ? . TryRemove ( userId , out _ ) ;
}
SetCacheLoadedForTenant ( false , data . TenantId ) ;
2019-06-07 08:59:07 +00:00
}
2019-07-22 12:31:09 +00:00
catch { }
} , CacheNotifyAction . Remove ) ;
2019-06-07 08:59:07 +00:00
}
catch ( Exception )
{
2019-08-15 12:04:42 +00:00
2019-06-07 08:59:07 +00:00
}
}
public static string GetDefaultPhotoAbsoluteWebPath ( )
{
return WebImageSupplier . GetAbsoluteWebPath ( _defaultAvatar ) ;
}
2019-08-08 09:26:58 +00:00
public static string GetRetinaPhotoURL ( int tenantId , Guid userID )
2019-06-07 08:59:07 +00:00
{
2019-08-08 09:26:58 +00:00
return GetRetinaPhotoURL ( tenantId , userID , out _ ) ;
2019-07-24 12:34:23 +00:00
}
2019-08-08 09:26:58 +00:00
public static string GetRetinaPhotoURL ( int tenantId , Guid userID , out bool isdef )
2019-07-24 12:34:23 +00:00
{
2019-08-08 09:26:58 +00:00
return GetSizedPhotoAbsoluteWebPath ( tenantId , userID , RetinaFotoSize , out isdef ) ;
2019-06-07 08:59:07 +00:00
}
2019-08-08 09:26:58 +00:00
public static string GetMaxPhotoURL ( int tenantId , Guid userID )
2019-06-07 08:59:07 +00:00
{
2019-08-08 09:26:58 +00:00
return GetMaxPhotoURL ( tenantId , userID , out _ ) ;
2019-07-24 12:34:23 +00:00
}
2019-08-08 09:26:58 +00:00
public static string GetMaxPhotoURL ( int tenantId , Guid userID , out bool isdef )
2019-07-24 12:34:23 +00:00
{
2019-08-08 09:26:58 +00:00
return GetSizedPhotoAbsoluteWebPath ( tenantId , userID , MaxFotoSize , out isdef ) ;
2019-06-07 08:59:07 +00:00
}
2019-08-08 09:26:58 +00:00
public static string GetBigPhotoURL ( int tenantId , Guid userID )
2019-06-07 08:59:07 +00:00
{
2019-08-08 09:26:58 +00:00
return GetBigPhotoURL ( tenantId , userID , out _ ) ;
2019-07-24 12:34:23 +00:00
}
2019-08-08 09:26:58 +00:00
public static string GetBigPhotoURL ( int tenantId , Guid userID , out bool isdef )
2019-07-24 12:34:23 +00:00
{
2019-08-08 09:26:58 +00:00
return GetSizedPhotoAbsoluteWebPath ( tenantId , userID , BigFotoSize , out isdef ) ;
2019-06-07 08:59:07 +00:00
}
2019-08-08 09:26:58 +00:00
public static string GetMediumPhotoURL ( int tenantId , Guid userID )
2019-06-07 08:59:07 +00:00
{
2019-08-08 09:26:58 +00:00
return GetMediumPhotoURL ( tenantId , userID , out _ ) ;
2019-07-24 12:34:23 +00:00
}
2019-08-08 09:26:58 +00:00
public static string GetMediumPhotoURL ( int tenantId , Guid userID , out bool isdef )
2019-07-24 12:34:23 +00:00
{
2019-08-08 09:26:58 +00:00
return GetSizedPhotoAbsoluteWebPath ( tenantId , userID , MediumFotoSize , out isdef ) ;
2019-06-07 08:59:07 +00:00
}
2019-08-08 09:26:58 +00:00
public static string GetSmallPhotoURL ( int tenantId , Guid userID )
2019-06-07 08:59:07 +00:00
{
2019-08-08 09:26:58 +00:00
return GetSmallPhotoURL ( tenantId , userID , out _ ) ;
2019-07-24 12:34:23 +00:00
}
2019-08-08 09:26:58 +00:00
public static string GetSmallPhotoURL ( int tenantId , Guid userID , out bool isdef )
2019-07-24 12:34:23 +00:00
{
2019-08-08 09:26:58 +00:00
return GetSizedPhotoAbsoluteWebPath ( tenantId , userID , SmallFotoSize , out isdef ) ;
2019-06-07 08:59:07 +00:00
}
2019-08-08 09:26:58 +00:00
public static string GetSizedPhotoUrl ( int tenantId , Guid userId , int width , int height )
2019-06-07 08:59:07 +00:00
{
2019-08-08 09:26:58 +00:00
return GetSizedPhotoAbsoluteWebPath ( tenantId , userId , new Size ( width , height ) ) ;
2019-06-07 08:59:07 +00:00
}
public static string GetDefaultSmallPhotoURL ( )
{
return GetDefaultPhotoAbsoluteWebPath ( SmallFotoSize ) ;
}
public static string GetDefaultMediumPhotoURL ( )
{
return GetDefaultPhotoAbsoluteWebPath ( MediumFotoSize ) ;
}
public static string GetDefaultBigPhotoURL ( )
{
return GetDefaultPhotoAbsoluteWebPath ( BigFotoSize ) ;
}
public static string GetDefaultMaxPhotoURL ( )
{
return GetDefaultPhotoAbsoluteWebPath ( MaxFotoSize ) ;
}
public static string GetDefaultRetinaPhotoURL ( )
{
return GetDefaultPhotoAbsoluteWebPath ( RetinaFotoSize ) ;
}
2019-08-27 14:57:06 +00:00
public static Size OriginalFotoSize { get ; } = new Size ( 1280 , 1280 ) ;
2019-06-07 08:59:07 +00:00
2019-08-27 14:57:06 +00:00
public static Size RetinaFotoSize { get ; } = new Size ( 360 , 360 ) ;
2019-06-07 08:59:07 +00:00
2019-08-27 14:57:06 +00:00
public static Size MaxFotoSize { get ; } = new Size ( 200 , 200 ) ;
2019-06-07 08:59:07 +00:00
2019-08-27 14:57:06 +00:00
public static Size BigFotoSize { get ; } = new Size ( 82 , 82 ) ;
2019-06-07 08:59:07 +00:00
2019-08-27 14:57:06 +00:00
public static Size MediumFotoSize { get ; } = new Size ( 48 , 48 ) ;
2019-06-07 08:59:07 +00:00
2019-08-27 14:57:06 +00:00
public static Size SmallFotoSize { get ; } = new Size ( 32 , 32 ) ;
2019-06-07 08:59:07 +00:00
2019-08-15 14:36:08 +00:00
private static readonly string _defaultRetinaAvatar = "default_user_photo_size_360-360.png" ;
private static readonly string _defaultAvatar = "default_user_photo_size_200-200.png" ;
private static readonly string _defaultSmallAvatar = "default_user_photo_size_32-32.png" ;
private static readonly string _defaultMediumAvatar = "default_user_photo_size_48-48.png" ;
private static readonly string _defaultBigAvatar = "default_user_photo_size_82-82.png" ;
private static readonly string _tempDomainName = "temp" ;
2019-06-07 08:59:07 +00:00
2019-08-08 09:26:58 +00:00
public static bool UserHasAvatar ( Tenant tenant , Guid userID )
2019-06-07 08:59:07 +00:00
{
2019-08-08 09:26:58 +00:00
var path = GetPhotoAbsoluteWebPath ( tenant , userID ) ;
2019-06-07 08:59:07 +00:00
var fileName = Path . GetFileName ( path ) ;
return fileName ! = _defaultAvatar ;
}
2019-08-15 12:04:42 +00:00
2019-08-08 09:26:58 +00:00
public static string GetPhotoAbsoluteWebPath ( Tenant tenant , Guid userID )
2019-06-07 08:59:07 +00:00
{
2019-08-27 14:57:06 +00:00
var path = SearchInCache ( tenant . TenantId , userID , Size . Empty , out _ ) ;
2019-06-07 08:59:07 +00:00
if ( ! string . IsNullOrEmpty ( path ) ) return path ;
try
{
2019-08-08 09:26:58 +00:00
var data = CoreContext . UserManager . GetUserPhoto ( tenant . TenantId , userID ) ;
2019-06-07 08:59:07 +00:00
string photoUrl ;
string fileName ;
if ( data = = null | | data . Length = = 0 )
{
photoUrl = GetDefaultPhotoAbsoluteWebPath ( ) ;
fileName = "default" ;
}
else
{
2019-08-08 09:26:58 +00:00
photoUrl = SaveOrUpdatePhoto ( tenant , userID , data , - 1 , new Size ( - 1 , - 1 ) , false , out fileName ) ;
2019-06-07 08:59:07 +00:00
}
AddToCache ( userID , Size . Empty , fileName ) ;
return photoUrl ;
}
catch
{
}
return GetDefaultPhotoAbsoluteWebPath ( ) ;
}
2019-08-08 09:26:58 +00:00
internal static Size GetPhotoSize ( Tenant tenant , Guid userID )
2019-06-07 08:59:07 +00:00
{
2019-08-08 09:26:58 +00:00
var virtualPath = GetPhotoAbsoluteWebPath ( tenant , userID ) ;
2019-06-07 08:59:07 +00:00
if ( virtualPath = = null ) return Size . Empty ;
try
{
var sizePart = virtualPath . Substring ( virtualPath . LastIndexOf ( '_' ) ) ;
sizePart = sizePart . Trim ( '_' ) ;
sizePart = sizePart . Remove ( sizePart . LastIndexOf ( '.' ) ) ;
2019-08-15 13:16:39 +00:00
return new Size ( int . Parse ( sizePart . Split ( '-' ) [ 0 ] ) , int . Parse ( sizePart . Split ( '-' ) [ 1 ] ) ) ;
2019-06-07 08:59:07 +00:00
}
catch
{
return Size . Empty ;
}
}
2019-08-08 09:26:58 +00:00
private static string GetSizedPhotoAbsoluteWebPath ( int tenantId , Guid userID , Size size )
2019-07-24 12:34:23 +00:00
{
2019-08-08 09:26:58 +00:00
return GetSizedPhotoAbsoluteWebPath ( tenantId , userID , size , out _ ) ;
2019-07-24 12:34:23 +00:00
}
2019-08-08 09:26:58 +00:00
private static string GetSizedPhotoAbsoluteWebPath ( int tenantId , Guid userID , Size size , out bool isdef )
2019-06-07 08:59:07 +00:00
{
2019-08-27 14:57:06 +00:00
var res = SearchInCache ( tenantId , userID , size , out isdef ) ;
2019-08-05 13:29:58 +00:00
if ( ! string . IsNullOrEmpty ( res ) ) return res ;
2019-06-07 08:59:07 +00:00
try
{
2019-08-08 09:26:58 +00:00
var data = CoreContext . UserManager . GetUserPhoto ( tenantId , userID ) ;
2019-06-07 08:59:07 +00:00
if ( data = = null | | data . Length = = 0 )
{
//empty photo. cache default
var photoUrl = GetDefaultPhotoAbsoluteWebPath ( size ) ;
AddToCache ( userID , size , "default" ) ;
2019-07-24 12:34:23 +00:00
isdef = true ;
2019-06-07 08:59:07 +00:00
return photoUrl ;
}
//Enqueue for sizing
2019-08-08 09:26:58 +00:00
SizePhoto ( tenantId , userID , data , - 1 , size ) ;
2019-06-07 08:59:07 +00:00
}
catch { }
2019-07-24 12:34:23 +00:00
isdef = false ;
2019-06-07 08:59:07 +00:00
return GetDefaultPhotoAbsoluteWebPath ( size ) ;
}
2019-08-27 14:57:06 +00:00
private static string GetDefaultPhotoAbsoluteWebPath ( Size size ) = >
size switch
{
Size ( var w , var h ) when w = = RetinaFotoSize . Width & & h = = RetinaFotoSize . Height = > WebImageSupplier . GetAbsoluteWebPath ( _defaultRetinaAvatar ) ,
Size ( var w , var h ) when w = = MaxFotoSize . Width & & h = = MaxFotoSize . Height = > WebImageSupplier . GetAbsoluteWebPath ( _defaultAvatar ) ,
Size ( var w , var h ) when w = = BigFotoSize . Width & & h = = BigFotoSize . Height = > WebImageSupplier . GetAbsoluteWebPath ( _defaultBigAvatar ) ,
Size ( var w , var h ) when w = = SmallFotoSize . Width & & h = = SmallFotoSize . Height = > WebImageSupplier . GetAbsoluteWebPath ( _defaultSmallAvatar ) ,
Size ( var w , var h ) when w = = MediumFotoSize . Width & & h = = MediumFotoSize . Height = > WebImageSupplier . GetAbsoluteWebPath ( _defaultMediumAvatar ) ,
_ = > GetDefaultPhotoAbsoluteWebPath ( )
} ;
2019-06-07 08:59:07 +00:00
//Regex for parsing filenames into groups with id's
private static readonly Regex ParseFile =
new Regex ( @"^(?'module'\{{0,1}([0-9a-fA-F]){8}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){12}\}{0,1}){0,1}" +
@"(?'user'\{{0,1}([0-9a-fA-F]){8}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){12}\}{0,1}){1}" +
@"_(?'kind'orig|size){1}_(?'size'(?'width'[0-9]{1,5})-{1}(?'height'[0-9]{1,5})){0,1}\..*" , RegexOptions . Compiled ) ;
private static readonly HashSet < int > TenantDiskCache = new HashSet < int > ( ) ;
private static readonly object DiskCacheLoaderLock = new object ( ) ;
2019-08-27 14:57:06 +00:00
private static bool IsCacheLoadedForTenant ( int tenantId )
2019-06-07 08:59:07 +00:00
{
2019-08-27 14:57:06 +00:00
//
return TenantDiskCache . Contains ( tenantId ) ;
2019-06-07 08:59:07 +00:00
}
2019-09-13 13:15:51 +00:00
private static bool SetCacheLoadedForTenant ( bool isLoaded , int tenantId )
2019-06-07 08:59:07 +00:00
{
2019-09-13 13:15:51 +00:00
return isLoaded ? TenantDiskCache . Add ( tenantId ) : TenantDiskCache . Remove ( tenantId ) ;
2019-06-07 08:59:07 +00:00
}
2019-08-27 14:57:06 +00:00
private static string SearchInCache ( int tenantId , Guid userId , Size size , out bool isDef )
2019-06-07 08:59:07 +00:00
{
2019-08-27 14:57:06 +00:00
if ( ! IsCacheLoadedForTenant ( tenantId ) )
LoadDiskCache ( tenantId ) ;
2019-06-07 08:59:07 +00:00
2019-08-05 13:29:58 +00:00
isDef = false ;
2019-08-27 14:57:06 +00:00
string fileName = null ;
2019-08-28 09:15:33 +00:00
Photofiles . TryGetValue ( ToCache ( size ) , out var photo ) ;
2019-06-07 08:59:07 +00:00
2019-08-27 14:57:06 +00:00
if ( size ! = Size . Empty )
{
2019-08-28 09:15:33 +00:00
photo ? . TryGetValue ( userId , out fileName ) ;
2019-08-27 14:57:06 +00:00
}
else
{
fileName = photo ?
. Select ( x = > x . Value )
. FirstOrDefault ( x = > ! string . IsNullOrEmpty ( x ) & & x . Contains ( "_orig_" ) ) ;
2019-06-07 08:59:07 +00:00
}
2019-08-05 13:29:58 +00:00
if ( fileName ! = null & & fileName . StartsWith ( "default" ) )
{
isDef = true ;
return GetDefaultPhotoAbsoluteWebPath ( size ) ;
}
2019-06-07 08:59:07 +00:00
if ( ! string . IsNullOrEmpty ( fileName ) )
{
2019-08-27 14:57:06 +00:00
var store = GetDataStore ( tenantId ) ;
2019-06-07 08:59:07 +00:00
return store . GetUri ( fileName ) . ToString ( ) ;
}
return null ;
}
2019-08-27 14:57:06 +00:00
private static void LoadDiskCache ( int tenantId )
2019-06-07 08:59:07 +00:00
{
lock ( DiskCacheLoaderLock )
{
2019-08-27 14:57:06 +00:00
if ( ! IsCacheLoadedForTenant ( tenantId ) )
2019-06-07 08:59:07 +00:00
{
try
{
2019-08-27 14:57:06 +00:00
var listFileNames = GetDataStore ( tenantId ) . ListFilesRelative ( "" , "" , "*.*" , false ) ;
2019-06-07 08:59:07 +00:00
foreach ( var fileName in listFileNames )
{
//Try parse fileName
if ( fileName ! = null )
{
var match = ParseFile . Match ( fileName ) ;
if ( match . Success & & match . Groups [ "user" ] . Success )
{
var parsedUserId = new Guid ( match . Groups [ "user" ] . Value ) ;
var size = Size . Empty ;
if ( match . Groups [ "width" ] . Success & & match . Groups [ "height" ] . Success )
{
//Parse size
size = new Size ( int . Parse ( match . Groups [ "width" ] . Value ) , int . Parse ( match . Groups [ "height" ] . Value ) ) ;
}
AddToCache ( parsedUserId , size , fileName ) ;
}
}
}
2019-09-13 13:15:51 +00:00
SetCacheLoadedForTenant ( true , tenantId ) ;
2019-06-07 08:59:07 +00:00
}
catch ( Exception err )
{
LogManager . GetLogger ( "ASC.Web.Photo" ) . Error ( err ) ;
}
}
}
}
private static void ClearCache ( Guid userID )
{
if ( CacheNotify ! = null )
{
2019-08-27 14:57:06 +00:00
CacheNotify . Publish ( new UserPhotoManagerCacheItem { UserID = Google . Protobuf . ByteString . CopyFrom ( userID . ToByteArray ( ) ) } , CacheNotifyAction . Remove ) ;
2019-06-07 08:59:07 +00:00
}
}
2019-08-27 14:57:06 +00:00
private static void AddToCache ( Guid userID , Size size , string fileName )
2019-06-07 08:59:07 +00:00
{
if ( CacheNotify ! = null )
{
2019-08-27 14:57:06 +00:00
CacheNotify . Publish ( new UserPhotoManagerCacheItem { UserID = Google . Protobuf . ByteString . CopyFrom ( userID . ToByteArray ( ) ) , Size = ToCache ( size ) , FileName = fileName } , CacheNotifyAction . InsertOrUpdate ) ;
2019-06-07 08:59:07 +00:00
}
}
public static void ResetThumbnailSettings ( Guid userId )
{
var thumbSettings = new UserPhotoThumbnailSettings ( ) . GetDefault ( ) as UserPhotoThumbnailSettings ;
thumbSettings . SaveForUser ( userId ) ;
}
2019-08-08 09:26:58 +00:00
public static string SaveOrUpdatePhoto ( Tenant tenant , Guid userID , byte [ ] data )
2019-06-07 08:59:07 +00:00
{
2019-08-16 09:08:46 +00:00
return SaveOrUpdatePhoto ( tenant , userID , data , - 1 , OriginalFotoSize , true , out _ ) ;
2019-06-07 08:59:07 +00:00
}
2019-09-13 11:41:15 +00:00
public static string SaveOrUpdateCroppedPhoto ( Tenant tenant , Guid userID , byte [ ] data , byte [ ] defaultData )
{
return SaveOrUpdateCroppedPhoto ( tenant , userID , data , defaultData , - 1 , OriginalFotoSize , true , out _ ) ;
}
2019-06-07 08:59:07 +00:00
2019-08-08 09:26:58 +00:00
public static void RemovePhoto ( Tenant tenant , Guid idUser )
2019-06-07 08:59:07 +00:00
{
2019-09-13 13:15:51 +00:00
var storage = GetDataStore ( tenant . TenantId ) ;
storage . DeleteFiles ( "" , idUser + "*.*" , false ) ;
2019-08-08 09:26:58 +00:00
CoreContext . UserManager . SaveUserPhoto ( tenant , idUser , null ) ;
2019-06-07 08:59:07 +00:00
ClearCache ( idUser ) ;
}
2019-08-08 09:26:58 +00:00
private static string SaveOrUpdatePhoto ( Tenant tenant , Guid userID , byte [ ] data , long maxFileSize , Size size , bool saveInCoreContext , out string fileName )
2019-06-07 08:59:07 +00:00
{
2019-08-15 13:05:50 +00:00
data = TryParseImage ( data , maxFileSize , size , out var imgFormat , out var width , out var height ) ;
2019-06-07 08:59:07 +00:00
var widening = CommonPhotoManager . GetImgFormatName ( imgFormat ) ;
fileName = string . Format ( "{0}_orig_{1}-{2}.{3}" , userID , width , height , widening ) ;
if ( saveInCoreContext )
{
2019-08-08 09:26:58 +00:00
CoreContext . UserManager . SaveUserPhoto ( tenant , userID , data ) ;
2019-06-07 08:59:07 +00:00
SetUserPhotoThumbnailSettings ( userID , width , height ) ;
ClearCache ( userID ) ;
}
2019-08-27 14:57:06 +00:00
var store = GetDataStore ( tenant . TenantId ) ;
2019-06-07 08:59:07 +00:00
var photoUrl = GetDefaultPhotoAbsoluteWebPath ( ) ;
if ( data ! = null & & data . Length > 0 )
{
using ( var stream = new MemoryStream ( data ) )
{
photoUrl = store . Save ( fileName , stream ) . ToString ( ) ;
}
//Queue resizing
2019-08-08 09:26:58 +00:00
SizePhoto ( tenant . TenantId , userID , data , - 1 , SmallFotoSize , true ) ;
SizePhoto ( tenant . TenantId , userID , data , - 1 , MediumFotoSize , true ) ;
SizePhoto ( tenant . TenantId , userID , data , - 1 , BigFotoSize , true ) ;
SizePhoto ( tenant . TenantId , userID , data , - 1 , MaxFotoSize , true ) ;
2019-09-13 11:41:15 +00:00
SizePhoto ( tenant . TenantId , userID , data , - 1 , RetinaFotoSize , true ) ;
}
return photoUrl ;
}
private static string SaveOrUpdateCroppedPhoto ( Tenant tenant , Guid userID , byte [ ] data , byte [ ] defaultData , long maxFileSize , Size size , bool saveInCoreContext , out string fileName )
{
data = TryParseImage ( data , maxFileSize , size , out var imgFormat , out var width , out var height ) ;
var widening = CommonPhotoManager . GetImgFormatName ( imgFormat ) ;
fileName = string . Format ( "{0}_orig_{1}-{2}.{3}" , userID , width , height , widening ) ;
if ( saveInCoreContext )
{
CoreContext . UserManager . SaveUserPhoto ( tenant , userID , defaultData ) ;
var max = Math . Max ( Math . Max ( width , height ) , SmallFotoSize . Width ) ;
var min = Math . Max ( Math . Min ( width , height ) , SmallFotoSize . Width ) ;
var pos = ( max - min ) / 2 ;
var settings = new UserPhotoThumbnailSettings (
width > = height ? new Point ( pos , 0 ) : new Point ( 0 , pos ) ,
new Size ( min , min ) ) ;
settings . SaveForUser ( userID ) ;
ClearCache ( userID ) ;
}
var store = GetDataStore ( tenant . TenantId ) ;
var photoUrl = GetDefaultPhotoAbsoluteWebPath ( ) ;
if ( data ! = null & & data . Length > 0 )
{
using ( var stream = new MemoryStream ( data ) )
{
photoUrl = store . Save ( fileName , stream ) . ToString ( ) ;
}
//Queue resizing
SizePhoto ( tenant . TenantId , userID , data , - 1 , SmallFotoSize , true ) ;
SizePhoto ( tenant . TenantId , userID , data , - 1 , MediumFotoSize , true ) ;
SizePhoto ( tenant . TenantId , userID , data , - 1 , BigFotoSize , true ) ;
SizePhoto ( tenant . TenantId , userID , data , - 1 , MaxFotoSize , true ) ;
2019-08-08 09:26:58 +00:00
SizePhoto ( tenant . TenantId , userID , data , - 1 , RetinaFotoSize , true ) ;
2019-06-07 08:59:07 +00:00
}
return photoUrl ;
}
private static void SetUserPhotoThumbnailSettings ( Guid userId , int width , int height )
{
var settings = UserPhotoThumbnailSettings . LoadForUser ( userId ) ;
if ( ! settings . IsDefault ) return ;
var max = Math . Max ( Math . Max ( width , height ) , SmallFotoSize . Width ) ;
var min = Math . Max ( Math . Min ( width , height ) , SmallFotoSize . Width ) ;
var pos = ( max - min ) / 2 ;
settings = new UserPhotoThumbnailSettings (
width > = height ? new Point ( pos , 0 ) : new Point ( 0 , pos ) ,
new Size ( min , min ) ) ;
settings . SaveForUser ( userId ) ;
}
private static byte [ ] TryParseImage ( byte [ ] data , long maxFileSize , Size maxsize , out ImageFormat imgFormat , out int width , out int height )
{
if ( data = = null | | data . Length < = 0 ) throw new UnknownImageFormatException ( ) ;
if ( maxFileSize ! = - 1 & & data . Length > maxFileSize ) throw new ImageSizeLimitException ( ) ;
data = ImageHelper . RotateImageByExifOrientationData ( data ) ;
try
{
2019-08-15 15:08:40 +00:00
using var stream = new MemoryStream ( data ) ;
using var img = new Bitmap ( stream ) ;
imgFormat = img . RawFormat ;
width = img . Width ;
height = img . Height ;
var maxWidth = maxsize . Width ;
var maxHeight = maxsize . Height ;
if ( ( maxHeight ! = - 1 & & img . Height > maxHeight ) | | ( maxWidth ! = - 1 & & img . Width > maxWidth ) )
2019-06-07 08:59:07 +00:00
{
2019-08-15 15:08:40 +00:00
#region calulate height and width
2019-06-07 08:59:07 +00:00
2019-08-15 15:08:40 +00:00
if ( width > maxWidth & & height > maxHeight )
2019-06-07 08:59:07 +00:00
{
2019-08-15 15:08:40 +00:00
if ( width > height )
2019-06-07 08:59:07 +00:00
{
height = ( int ) ( ( double ) height * ( double ) maxWidth / ( double ) width + 0.5 ) ;
width = maxWidth ;
}
2019-08-15 15:08:40 +00:00
else
2019-06-07 08:59:07 +00:00
{
width = ( int ) ( ( double ) width * ( double ) maxHeight / ( double ) height + 0.5 ) ;
height = maxHeight ;
}
2019-08-15 15:08:40 +00:00
}
2019-06-07 08:59:07 +00:00
2019-08-15 15:08:40 +00:00
if ( width > maxWidth & & height < = maxHeight )
{
height = ( int ) ( ( double ) height * ( double ) maxWidth / ( double ) width + 0.5 ) ;
width = maxWidth ;
}
2019-06-07 08:59:07 +00:00
2019-08-15 15:08:40 +00:00
if ( width < = maxWidth & & height > maxHeight )
{
width = ( int ) ( ( double ) width * ( double ) maxHeight / ( double ) height + 0.5 ) ;
height = maxHeight ;
2019-06-07 08:59:07 +00:00
}
2019-08-15 15:08:40 +00:00
#endregion
using var b = new Bitmap ( width , height ) ;
using var gTemp = Graphics . FromImage ( b ) ;
gTemp . InterpolationMode = InterpolationMode . HighQualityBicubic ;
gTemp . PixelOffsetMode = PixelOffsetMode . HighQuality ;
gTemp . SmoothingMode = SmoothingMode . HighQuality ;
gTemp . DrawImage ( img , 0 , 0 , width , height ) ;
data = CommonPhotoManager . SaveToBytes ( b ) ;
2019-06-07 08:59:07 +00:00
}
2019-08-15 15:08:40 +00:00
return data ;
2019-06-07 08:59:07 +00:00
}
catch ( OutOfMemoryException )
{
throw new ImageSizeLimitException ( ) ;
}
catch ( ArgumentException error )
{
throw new UnknownImageFormatException ( error ) ;
}
}
//note: using auto stop queue
private static readonly WorkerQueue < ResizeWorkerItem > ResizeQueue = new WorkerQueue < ResizeWorkerItem > ( 2 , TimeSpan . FromSeconds ( 30 ) , 1 , true ) ; //TODO: configure
2019-08-08 09:26:58 +00:00
private static string SizePhoto ( int tenantId , Guid userID , byte [ ] data , long maxFileSize , Size size )
2019-06-07 08:59:07 +00:00
{
2019-08-08 09:26:58 +00:00
return SizePhoto ( tenantId , userID , data , maxFileSize , size , false ) ;
2019-06-07 08:59:07 +00:00
}
2019-08-08 09:26:58 +00:00
private static string SizePhoto ( int tenantId , Guid userID , byte [ ] data , long maxFileSize , Size size , bool now )
2019-06-07 08:59:07 +00:00
{
if ( data = = null | | data . Length < = 0 ) throw new UnknownImageFormatException ( ) ;
if ( maxFileSize ! = - 1 & & data . Length > maxFileSize ) throw new ImageWeightLimitException ( ) ;
2019-08-27 14:57:06 +00:00
var resizeTask = new ResizeWorkerItem ( userID , data , maxFileSize , size , GetDataStore ( tenantId ) , UserPhotoThumbnailSettings . LoadForUser ( userID ) ) ;
2019-06-07 08:59:07 +00:00
if ( now )
{
//Resize synchronously
ResizeImage ( resizeTask ) ;
2019-08-08 09:26:58 +00:00
return GetSizedPhotoAbsoluteWebPath ( tenantId , userID , size ) ;
2019-06-07 08:59:07 +00:00
}
else
{
if ( ! ResizeQueue . GetItems ( ) . Contains ( resizeTask ) )
{
//Add
ResizeQueue . Add ( resizeTask ) ;
if ( ! ResizeQueue . IsStarted )
{
ResizeQueue . Start ( ResizeImage ) ;
}
}
return GetDefaultPhotoAbsoluteWebPath ( size ) ;
//NOTE: return default photo here. Since task will update cache
}
}
private static void ResizeImage ( ResizeWorkerItem item )
{
try
{
var data = item . Data ;
2019-08-15 15:08:40 +00:00
using var stream = new MemoryStream ( data ) ;
using var img = Image . FromStream ( stream ) ;
var imgFormat = img . RawFormat ;
if ( item . Size ! = img . Size )
2019-06-07 08:59:07 +00:00
{
2019-08-15 15:08:40 +00:00
using var img2 = item . Settings . IsDefault ?
CommonPhotoManager . DoThumbnail ( img , item . Size , true , true , true ) :
UserPhotoThumbnailManager . GetBitmap ( img , item . Size , item . Settings ) ;
data = CommonPhotoManager . SaveToBytes ( img2 ) ;
}
else
{
data = CommonPhotoManager . SaveToBytes ( img ) ;
}
2019-06-07 08:59:07 +00:00
2019-08-15 15:08:40 +00:00
var widening = CommonPhotoManager . GetImgFormatName ( imgFormat ) ;
var fileName = string . Format ( "{0}_size_{1}-{2}.{3}" , item . UserId , item . Size . Width , item . Size . Height , widening ) ;
2019-06-07 08:59:07 +00:00
2019-08-15 15:08:40 +00:00
using var stream2 = new MemoryStream ( data ) ;
item . DataStore . Save ( fileName , stream2 ) . ToString ( ) ;
2019-06-07 08:59:07 +00:00
2019-08-15 15:08:40 +00:00
AddToCache ( item . UserId , item . Size , fileName ) ;
2019-06-07 08:59:07 +00:00
}
catch ( ArgumentException error )
{
throw new UnknownImageFormatException ( error ) ;
}
}
2019-08-27 14:57:06 +00:00
public static string GetTempPhotoAbsoluteWebPath ( int tenantId , string fileName )
2019-06-07 08:59:07 +00:00
{
2019-08-27 14:57:06 +00:00
return GetDataStore ( tenantId ) . GetUri ( _tempDomainName , fileName ) . ToString ( ) ;
2019-06-07 08:59:07 +00:00
}
2019-08-27 14:57:06 +00:00
public static string SaveTempPhoto ( int tenantId , byte [ ] data , long maxFileSize , int maxWidth , int maxHeight )
2019-06-07 08:59:07 +00:00
{
2019-08-15 13:05:50 +00:00
data = TryParseImage ( data , maxFileSize , new Size ( maxWidth , maxHeight ) , out var imgFormat , out var width , out var height ) ;
2019-06-07 08:59:07 +00:00
var fileName = Guid . NewGuid ( ) + "." + CommonPhotoManager . GetImgFormatName ( imgFormat ) ;
2019-08-27 14:57:06 +00:00
var store = GetDataStore ( tenantId ) ;
2019-08-15 15:08:40 +00:00
using var stream = new MemoryStream ( data ) ;
return store . Save ( _tempDomainName , fileName , stream ) . ToString ( ) ;
2019-06-07 08:59:07 +00:00
}
2019-08-27 14:57:06 +00:00
public static byte [ ] GetTempPhotoData ( int tenantId , string fileName )
2019-06-07 08:59:07 +00:00
{
2019-08-27 14:57:06 +00:00
using var s = GetDataStore ( tenantId ) . GetReadStream ( _tempDomainName , fileName ) ;
2019-08-15 15:08:40 +00:00
var data = new MemoryStream ( ) ;
var buffer = new byte [ 1024 * 10 ] ;
while ( true )
2019-06-07 08:59:07 +00:00
{
2019-08-15 15:08:40 +00:00
var count = s . Read ( buffer , 0 , buffer . Length ) ;
if ( count = = 0 ) break ;
data . Write ( buffer , 0 , count ) ;
2019-06-07 08:59:07 +00:00
}
2019-08-15 15:08:40 +00:00
return data . ToArray ( ) ;
2019-06-07 08:59:07 +00:00
}
2019-08-27 14:57:06 +00:00
public static string GetSizedTempPhotoAbsoluteWebPath ( int tenantId , string fileName , int newWidth , int newHeight )
2019-06-07 08:59:07 +00:00
{
2019-08-27 14:57:06 +00:00
var store = GetDataStore ( tenantId ) ;
2019-06-07 08:59:07 +00:00
if ( store . IsFile ( _tempDomainName , fileName ) )
{
2019-08-15 15:08:40 +00:00
using var s = store . GetReadStream ( _tempDomainName , fileName ) ;
using var img = Image . FromStream ( s ) ;
var imgFormat = img . RawFormat ;
byte [ ] data ;
2019-06-07 08:59:07 +00:00
2019-08-15 15:08:40 +00:00
if ( img . Width ! = newWidth | | img . Height ! = newHeight )
{
using var img2 = CommonPhotoManager . DoThumbnail ( img , new Size ( newWidth , newHeight ) , true , true , true ) ;
data = CommonPhotoManager . SaveToBytes ( img2 ) ;
}
else
{
data = CommonPhotoManager . SaveToBytes ( img ) ;
2019-06-07 08:59:07 +00:00
}
2019-08-15 15:08:40 +00:00
var widening = CommonPhotoManager . GetImgFormatName ( imgFormat ) ;
var index = fileName . LastIndexOf ( '.' ) ;
var fileNameWithoutExt = ( index ! = - 1 ) ? fileName . Substring ( 0 , index ) : fileName ;
var trueFileName = fileNameWithoutExt + "_size_" + newWidth . ToString ( ) + "-" + newHeight . ToString ( ) + "." + widening ;
using var stream = new MemoryStream ( data ) ;
return store . Save ( _tempDomainName , trueFileName , stream ) . ToString ( ) ;
2019-06-07 08:59:07 +00:00
}
return GetDefaultPhotoAbsoluteWebPath ( new Size ( newWidth , newHeight ) ) ;
}
2019-08-27 14:57:06 +00:00
public static void RemoveTempPhoto ( int tenantId , string fileName )
2019-06-07 08:59:07 +00:00
{
var index = fileName . LastIndexOf ( '.' ) ;
var fileNameWithoutExt = ( index ! = - 1 ) ? fileName . Substring ( 0 , index ) : fileName ;
try
{
2019-08-27 14:57:06 +00:00
var store = GetDataStore ( tenantId ) ;
2019-06-07 08:59:07 +00:00
store . DeleteFiles ( _tempDomainName , "" , fileNameWithoutExt + "*.*" , false ) ;
}
catch { } ;
}
2019-08-08 09:26:58 +00:00
public static Bitmap GetPhotoBitmap ( int tenantId , Guid userID )
2019-06-07 08:59:07 +00:00
{
try
{
2019-08-08 09:26:58 +00:00
var data = CoreContext . UserManager . GetUserPhoto ( tenantId , userID ) ;
2019-06-07 08:59:07 +00:00
if ( data ! = null )
{
2019-08-15 15:08:40 +00:00
using var s = new MemoryStream ( data ) ;
return new Bitmap ( s ) ;
2019-06-07 08:59:07 +00:00
}
}
catch { }
return null ;
}
2019-08-27 14:57:06 +00:00
public static string SaveThumbnail ( int tenantId , Guid userID , Image img , ImageFormat format )
2019-06-07 08:59:07 +00:00
{
var moduleID = Guid . Empty ;
var widening = CommonPhotoManager . GetImgFormatName ( format ) ;
var size = img . Size ;
var fileName = string . Format ( "{0}{1}_size_{2}-{3}.{4}" , ( moduleID = = Guid . Empty ? "" : moduleID . ToString ( ) ) , userID , img . Width , img . Height , widening ) ;
2019-08-27 14:57:06 +00:00
var store = GetDataStore ( tenantId ) ;
2019-06-07 08:59:07 +00:00
string photoUrl ;
using ( var s = new MemoryStream ( CommonPhotoManager . SaveToBytes ( img ) ) )
{
img . Dispose ( ) ;
photoUrl = store . Save ( fileName , s ) . ToString ( ) ;
}
AddToCache ( userID , size , fileName ) ;
return photoUrl ;
}
2019-08-27 14:57:06 +00:00
public static byte [ ] GetUserPhotoData ( int tenantId , Guid userId , Size size )
2019-06-07 08:59:07 +00:00
{
try
{
var pattern = string . Format ( "{0}_size_{1}-{2}.*" , userId , size . Width , size . Height ) ;
2019-08-27 14:57:06 +00:00
var fileName = GetDataStore ( tenantId ) . ListFilesRelative ( "" , "" , pattern , false ) . FirstOrDefault ( ) ;
2019-06-07 08:59:07 +00:00
if ( string . IsNullOrEmpty ( fileName ) ) return null ;
2019-08-27 14:57:06 +00:00
using var s = GetDataStore ( tenantId ) . GetReadStream ( "" , fileName ) ;
2019-08-15 15:08:40 +00:00
var data = new MemoryStream ( ) ;
var buffer = new byte [ 1024 * 10 ] ;
while ( true )
2019-06-07 08:59:07 +00:00
{
2019-08-15 15:08:40 +00:00
var count = s . Read ( buffer , 0 , buffer . Length ) ;
if ( count = = 0 ) break ;
data . Write ( buffer , 0 , count ) ;
2019-06-07 08:59:07 +00:00
}
2019-08-15 15:08:40 +00:00
return data . ToArray ( ) ;
2019-06-07 08:59:07 +00:00
}
catch ( Exception err )
{
LogManager . GetLogger ( "ASC.Web.Photo" ) . Error ( err ) ;
return null ;
}
}
2019-08-27 14:57:06 +00:00
private static IDataStore GetDataStore ( int tenantId )
2019-06-07 08:59:07 +00:00
{
2019-08-27 14:57:06 +00:00
return StorageFactory . GetStorage ( tenantId . ToString ( ) , "userPhotos" ) ;
2019-06-07 08:59:07 +00:00
}
2019-08-27 14:57:06 +00:00
private static Size FromCahe ( CacheSize cacheSize ) = >
cacheSize switch
{
CacheSize . Big = > BigFotoSize ,
CacheSize . Max = > MaxFotoSize ,
CacheSize . Medium = > MediumFotoSize ,
CacheSize . Original = > OriginalFotoSize ,
CacheSize . Retina = > RetinaFotoSize ,
CacheSize . Small = > SmallFotoSize ,
_ = > OriginalFotoSize ,
} ;
private static CacheSize ToCache ( Size size ) = >
size switch
{
Size ( var w , var h ) when w = = RetinaFotoSize . Width & & h = = RetinaFotoSize . Height = > CacheSize . Retina ,
Size ( var w , var h ) when w = = MaxFotoSize . Width & & h = = MaxFotoSize . Height = > CacheSize . Max ,
Size ( var w , var h ) when w = = BigFotoSize . Width & & h = = BigFotoSize . Height = > CacheSize . Big ,
Size ( var w , var h ) when w = = SmallFotoSize . Width & & h = = SmallFotoSize . Height = > CacheSize . Small ,
Size ( var w , var h ) when w = = MediumFotoSize . Width & & h = = MediumFotoSize . Height = > CacheSize . Medium ,
_ = > CacheSize . Original
} ;
2019-06-07 08:59:07 +00:00
}
#region Exception Classes
public class UnknownImageFormatException : Exception
{
public UnknownImageFormatException ( ) : base ( "unknown image file type" ) { }
public UnknownImageFormatException ( Exception inner ) : base ( "unknown image file type" , inner ) { }
}
public class ImageWeightLimitException : Exception
{
public ImageWeightLimitException ( ) : base ( "image width is too large" ) { }
}
public class ImageSizeLimitException : Exception
{
public ImageSizeLimitException ( ) : base ( "image size is too large" ) { }
}
#endregion
/// <summary>
/// Helper class for manipulating images.
/// </summary>
public static class ImageHelper
{
/// <summary>
/// Rotate the given image byte array according to Exif Orientation data
/// </summary>
/// <param name="data">source image byte array</param>
/// <param name="updateExifData">set it to TRUE to update image Exif data after rotation (default is TRUE)</param>
/// <returns>The rotated image byte array. If no rotation occurred, source data will be returned.</returns>
public static byte [ ] RotateImageByExifOrientationData ( byte [ ] data , bool updateExifData = true )
{
try
{
using var stream = new MemoryStream ( data ) ;
using var img = new Bitmap ( stream ) ;
var fType = RotateImageByExifOrientationData ( img , updateExifData ) ;
if ( fType ! = RotateFlipType . RotateNoneFlipNone )
{
using var tempStream = new MemoryStream ( ) ;
img . Save ( tempStream , System . Drawing . Imaging . ImageFormat . Png ) ;
data = tempStream . ToArray ( ) ;
}
}
catch ( Exception err )
{
2019-08-15 12:04:42 +00:00
LogManager . GetLogger ( "ASC.Web.Photo" ) . Error ( err ) ;
2019-06-07 08:59:07 +00:00
}
return data ;
}
2019-08-15 12:04:42 +00:00
2019-06-07 08:59:07 +00:00
/// <summary>
/// Rotate the given image file according to Exif Orientation data
/// </summary>
/// <param name="sourceFilePath">path of source file</param>
/// <param name="targetFilePath">path of target file</param>
/// <param name="targetFormat">target format</param>
/// <param name="updateExifData">set it to TRUE to update image Exif data after rotation (default is TRUE)</param>
/// <returns>The RotateFlipType value corresponding to the applied rotation. If no rotation occurred, RotateFlipType.RotateNoneFlipNone will be returned.</returns>
public static RotateFlipType RotateImageByExifOrientationData ( string sourceFilePath , string targetFilePath , ImageFormat targetFormat , bool updateExifData = true )
{
// Rotate the image according to EXIF data
2019-08-16 08:44:03 +00:00
using var bmp = new Bitmap ( sourceFilePath ) ;
2019-06-07 08:59:07 +00:00
var fType = RotateImageByExifOrientationData ( bmp , updateExifData ) ;
if ( fType ! = RotateFlipType . RotateNoneFlipNone )
{
bmp . Save ( targetFilePath , targetFormat ) ;
}
return fType ;
}
/// <summary>
/// Rotate the given bitmap according to Exif Orientation data
/// </summary>
/// <param name="img">source image</param>
/// <param name="updateExifData">set it to TRUE to update image Exif data after rotation (default is TRUE)</param>
/// <returns>The RotateFlipType value corresponding to the applied rotation. If no rotation occurred, RotateFlipType.RotateNoneFlipNone will be returned.</returns>
public static RotateFlipType RotateImageByExifOrientationData ( Image img , bool updateExifData = true )
{
const int orientationId = 0x0112 ;
var fType = RotateFlipType . RotateNoneFlipNone ;
if ( img . PropertyIdList . Contains ( orientationId ) )
{
var pItem = img . GetPropertyItem ( orientationId ) ;
fType = GetRotateFlipTypeByExifOrientationData ( pItem . Value [ 0 ] ) ;
if ( fType ! = RotateFlipType . RotateNoneFlipNone )
{
img . RotateFlip ( fType ) ;
if ( updateExifData ) img . RemovePropertyItem ( orientationId ) ; // Remove Exif orientation tag
}
}
return fType ;
}
/// <summary>
/// Return the proper System.Drawing.RotateFlipType according to given orientation EXIF metadata
/// </summary>
/// <param name="orientation">Exif "Orientation"</param>
/// <returns>the corresponding System.Drawing.RotateFlipType enum value</returns>
public static RotateFlipType GetRotateFlipTypeByExifOrientationData ( int orientation )
{
2019-08-15 15:13:25 +00:00
return orientation switch
{
1 = > RotateFlipType . RotateNoneFlipNone ,
2 = > RotateFlipType . RotateNoneFlipX ,
3 = > RotateFlipType . Rotate180FlipNone ,
4 = > RotateFlipType . Rotate180FlipX ,
5 = > RotateFlipType . Rotate90FlipX ,
6 = > RotateFlipType . Rotate90FlipNone ,
7 = > RotateFlipType . Rotate270FlipX ,
8 = > RotateFlipType . Rotate270FlipNone ,
_ = > RotateFlipType . RotateNoneFlipNone ,
} ;
2019-06-07 08:59:07 +00:00
}
}
2019-08-27 14:57:06 +00:00
public static class SizeExtend
{
public static void Deconstruct ( this Size size , out int w , out int h ) = >
( w , h ) = ( size . Width , size . Height ) ;
}
2019-06-07 08:59:07 +00:00
}