using SixLabors.Fonts; using SixLabors.ImageSharp; using SixLabors.ImageSharp.Drawing.Processing; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; using Watermark.Net.src.WatermarkNet.Enums; using Watermark.Net.src.WatermarkNet.Types; using static System.Net.Mime.MediaTypeNames; using static System.Runtime.InteropServices.JavaScript.JSType; using Image = SixLabors.ImageSharp.Image; namespace Watermark.Net.src.WatermarkNet.Core { public class Watermarker { private string _outputDir; /// /// Gets or sets the output directory for processed images. /// public string OutputDir { get { return _outputDir; } set { _outputDir = value; } } public Watermarker() { } public Watermarker(string outputDir) { _outputDir = outputDir; } /// /// Processes all images in a directory applying watermark. /// /// Source directory containing images to process. /// Watermark configuration. /// List of processed images with watermark information. public List ProcessDirectory(string directory, T watermark) where T : WatermarkImageBase { List processedImages = new List(); foreach (var imageFile in Directory.GetFiles(directory)) { //Do not process directories and hidden files if (File.GetAttributes(imageFile).HasFlag(FileAttributes.Directory) || File.GetAttributes(imageFile).HasFlag(FileAttributes.Hidden)) continue; WmarkedImage? resultedImage = null; if (typeof(T).IsAssignableTo(typeof(ImageWatermark))) { var concreateWatermark = (ImageWatermark)Convert.ChangeType(watermark, typeof(ImageWatermark)); resultedImage = ProcessImage(imageFile, this.OutputDir, concreateWatermark); } if (typeof(T).IsAssignableTo(typeof(TextWatermark))) { var concreateWatermark = (TextWatermark)Convert.ChangeType(watermark, typeof(TextWatermark)); resultedImage = ProcessImage(imageFile, this.OutputDir, concreateWatermark); } if (resultedImage != null) processedImages.Add(resultedImage); } return processedImages; } /// /// Applies an image watermark to a single image. /// /// Path to source image file. /// Output directory for processed image. /// Image watermark configuration. /// Processed image information or null on failure. /// Thrown when source image or watermark image is missing. public WmarkedImage? ProcessImage(string imagePath, string outputDirectory, ImageWatermark watermark) { if (!File.Exists(imagePath)) { throw new FileNotFoundException("Source file not found!", imagePath); } if (!File.Exists(watermark.ImagePath)) { throw new FileNotFoundException("Watermark file not found!", imagePath); } if (!Directory.Exists(outputDirectory)) { Directory.CreateDirectory(outputDirectory); } WmarkedImage? resultedImage = null; using (var targetImage = Image.Load(imagePath)) using (var watermarkImage = Image.Load(watermark.ImagePath)) { var scaledWmWidth = (int)Math.Round(watermarkImage.Width * watermark.Scale); var scaledWmHeight = (int)Math.Round(watermarkImage.Height * watermark.Scale); watermarkImage.Mutate(x => x.Resize(new Size(scaledWmWidth, scaledWmHeight))); using (var markedImage = targetImage.Clone(ctx => this.ApplyScalingWaterMarkImage(ctx, watermark, watermarkImage, targetImage))) { resultedImage = new WmarkedImage(markedImage, outputDirectory + Path.DirectorySeparatorChar + Path.GetFileName(imagePath)); markedImage.Save(resultedImage.Path); } } return resultedImage; } /// /// Applies a text watermark to a single image. /// /// Path to source image file. /// Output directory for processed image. /// Text watermark configuration. /// Processed image information or null on failure. /// Thrown when source image is missing. public WmarkedImage? ProcessImage(string imagePath, string outputDirectory, TextWatermark watermark) { if(!File.Exists(imagePath)) { throw new FileNotFoundException("Source file not found!", imagePath); } if (!Directory.Exists(outputDirectory)) { Directory.CreateDirectory(outputDirectory); } WmarkedImage? resultedImage = null; using (var targetImage = Image.Load(imagePath)) { using (var markedImage = targetImage.Clone(ctx => this.ApplyScalingWaterMarkText(ctx, watermark))) { resultedImage = new WmarkedImage(markedImage, outputDirectory + Path.DirectorySeparatorChar + Path.GetFileName(imagePath)); markedImage.Save(resultedImage.Path); } } return resultedImage; } /// /// Calculates origin point for text watermark based on position and size. /// /// Target image width. /// Target image height. /// Watermark text size. /// Position on target image. /// Calculated origin point coordinates. private PointF CalcWatermarkOrigin(int width, int height, float watermarkSize, ImagePosition position) { var origin = new PointF(0, 0); //Static value 1 pt is 72 px per inch var pixelsPerInch = 72; var wmHeight = watermarkSize / pixelsPerInch; switch (position) { case ImagePosition.TopLeft: origin = new PointF(watermarkSize / 2, wmHeight / 2); break; case ImagePosition.TopCenter: origin = new PointF(width / 2, wmHeight / 2); break; case ImagePosition.TopRight: origin = new PointF(width - watermarkSize * 2, wmHeight / 2); break; case ImagePosition.CenterLeft: origin = new PointF(watermarkSize / 2, height / 2.5f); break; case ImagePosition.Center: origin = new PointF(width / 2, height / 2.5f); break; case ImagePosition.CenterRight: origin = new PointF(width - watermarkSize * 2, height / 2.5f); break; case ImagePosition.BottomLeft: origin = new PointF(watermarkSize / 2, height - watermarkSize * 2); break; case ImagePosition.BottomCenter: origin = new PointF(width / 2, height - watermarkSize * 2); break; case ImagePosition.BottomRight: origin = new PointF(width - watermarkSize * 2, height - watermarkSize * 2); break; default: break; } return origin; } /// /// Calculates origin point for image watermark based on position and dimensions. /// /// Target image width. /// Target image height. /// Watermark image width. /// Watermark image height. /// Position on target image. /// Calculated origin point coordinates. private Point CalcWatermarkOrigin(int width, int height, int wmWidth,int wmHeight, ImagePosition position) { var origin = new Point(0, 0); var wmPaddingX = width - wmWidth; var paddingSide = wmPaddingX / 2; switch (position) { case ImagePosition.TopLeft: origin = new Point(wmWidth / 2, wmHeight / 2); break; case ImagePosition.TopCenter: origin = new Point((int)paddingSide, wmHeight / 2); break; case ImagePosition.TopRight: origin = new Point(width - wmWidth * 2, wmHeight / 2); break; case ImagePosition.CenterLeft: origin = new Point(wmWidth / 2, height / 2); break; case ImagePosition.Center: origin = new Point((int)paddingSide, (int)(height / 2)); break; case ImagePosition.CenterRight: origin = new Point(width - wmWidth * 2, height / 2); break; case ImagePosition.BottomLeft: origin = new Point(wmWidth / 2, height - wmHeight); break; case ImagePosition.BottomCenter: origin = new Point((int)paddingSide, height - wmHeight); break; case ImagePosition.BottomRight: origin = new Point(width - wmWidth * 2, height - wmHeight); break; default: break; } return origin; } /// /// Determines horizontal alignment based on watermark position. /// /// Watermark position on image. /// Corresponding horizontal alignment setting. private HorizontalAlignment HorizontalAlignmentFromPosition(ImagePosition imagePosition) { switch (imagePosition) { case ImagePosition.TopCenter: return HorizontalAlignment.Center; break; case ImagePosition.Center: return HorizontalAlignment.Center; break; case ImagePosition.BottomCenter: return HorizontalAlignment.Center; break; } return HorizontalAlignment.Left; } /// /// Applies text watermark to image with automatic scaling and positioning. /// /// Image processing context. /// Text watermark configuration. /// Processing context with applied watermark. private IImageProcessingContext ApplyScalingWaterMarkText(IImageProcessingContext processingContext, TextWatermark watermark) { Size imgSize = processingContext.GetCurrentSize(); FontRectangle size = TextMeasurer.MeasureSize(watermark.Text, new TextOptions(watermark.Font)); // Find out how much we need to scale the text to fill the space (up or down) float scalingFactor = Math.Min(imgSize.Width / size.Width, imgSize.Height / size.Height); // Create a new font Font scaledFont = new Font(watermark.Font, scalingFactor / 16 * (watermark.Font.Size * watermark.Scale)); ImagePosition[] centerImagePositions = { ImagePosition.CenterLeft, ImagePosition.CenterRight, ImagePosition.Center }; //processingContext.SetGraphicsOptions(new GraphicsOptions { AlphaCompositionMode = SixLabors.ImageSharp.PixelFormats.PixelAlphaCompositionMode.Clear}); //If set, apply backround color if (watermark.BackroundColor != null) processingContext.BackgroundColor((Color)watermark.BackroundColor); var textOptions = new RichTextOptions(scaledFont) { ColorFontSupport = ColorFontSupport.MicrosoftColrFormat, Origin = CalcWatermarkOrigin(imgSize.Width, imgSize.Height, scaledFont.Size, watermark.Position), HorizontalAlignment = HorizontalAlignmentFromPosition(watermark.Position), VerticalAlignment = VerticalAlignment.Top, }; if (watermark.Pave) { foreach (ImagePosition position in (ImagePosition[])Enum.GetValues(typeof(ImagePosition))) { textOptions.Origin = CalcWatermarkOrigin(imgSize.Width, imgSize.Height, scaledFont.Size, position); textOptions.HorizontalAlignment = HorizontalAlignmentFromPosition(position); processingContext.DrawText(textOptions, watermark.Text, watermark.Color); } return processingContext; } return processingContext .DrawText(textOptions, watermark.Text, watermark.Color); } /// /// Applies image watermark to target image with scaling and positioning. /// /// Image processing context. /// Image watermark configuration. /// Watermark image instance. /// Target image being processed. /// Processing context with applied watermark. private IImageProcessingContext ApplyScalingWaterMarkImage(IImageProcessingContext processingContext, ImageWatermark watermark, Image watermarkImage, Image targetImage) { var scaleFactor = 1f; if (targetImage.Width > targetImage.Height) scaleFactor = (float)targetImage.Width / targetImage.Height; else scaleFactor = (float) targetImage.Height / targetImage.Width; var wmPaddingX = (targetImage.Width - watermarkImage.Width) / 2; var wmPaddingY = (targetImage.Height - watermarkImage.Height) / 2; //scaleFactor = 1; var minWmPadding = 50; var scaledWmWidth = wmPaddingX > minWmPadding && wmPaddingY > minWmPadding ? watermarkImage.Width * scaleFactor : watermarkImage.Width / scaleFactor; var scaledWmHeight = wmPaddingX > minWmPadding && wmPaddingY > minWmPadding ? watermarkImage.Height * scaleFactor : watermarkImage.Height / scaleFactor; //If set, apply backround color if (watermark.BackroundColor != null) processingContext.BackgroundColor((Color)watermark.BackroundColor); watermarkImage.Mutate(x => x.Resize(new Size((int)scaledWmWidth, (int)scaledWmHeight))); var wmPositionOrigin = CalcWatermarkOrigin(targetImage.Width, targetImage.Height, watermarkImage.Width, watermarkImage.Height, watermark.Position); if (watermark.Pave) { foreach (ImagePosition position in (ImagePosition[])Enum.GetValues(typeof(ImagePosition))) { wmPositionOrigin = CalcWatermarkOrigin(targetImage.Width, targetImage.Height, watermarkImage.Width, watermarkImage.Height, position); try { processingContext.DrawImage(watermarkImage, wmPositionOrigin, watermark.Opacity); } catch (Exception ex) { Console.WriteLine(ex.ToString()); } } return processingContext; } return processingContext.DrawImage(watermarkImage, wmPositionOrigin, watermark.Opacity); } } }