// -------------------------------------------------------------------------------------------------------------------- // // Copyright (c) James South. // Licensed under the Apache License, Version 2.0. // // // Encapsulates methods to add a watermark text overlay to an image. // // -------------------------------------------------------------------------------------------------------------------- namespace ImageProcessor.Processors { using System; using System.Collections.Generic; using System.Drawing; using System.Drawing.Text; using ImageProcessor.Common.Exceptions; using ImageProcessor.Imaging; /// /// Encapsulates methods to add a watermark text overlay to an image. /// public class Watermark : IGraphicsProcessor { /// /// Initializes a new instance of the class. /// public Watermark() { this.Settings = new Dictionary(); } /// /// Gets or sets DynamicParameter. /// public dynamic DynamicParameter { get; set; } /// /// Gets or sets any additional settings required by the processor. /// public Dictionary Settings { get; set; } /// /// Processes the image. /// /// /// The current instance of the class containing /// the image to process. /// /// /// The processed image from the current instance of the class. /// public Image ProcessImage(ImageFactory factory) { Bitmap newImage = null; Image image = factory.Image; try { newImage = new Bitmap(image); TextLayer textLayer = this.DynamicParameter; string text = textLayer.Text; int opacity = Math.Min((int)Math.Ceiling((textLayer.Opacity / 100f) * 255), 255); int fontSize = textLayer.FontSize; FontStyle fontStyle = textLayer.Style; bool fallbackUsed = false; using (Graphics graphics = Graphics.FromImage(newImage)) { using (Font font = this.GetFont(textLayer.FontFamily, fontSize, fontStyle)) { using (StringFormat drawFormat = new StringFormat()) { StringFormatFlags? formatFlags = this.GetFlags(textLayer); if (formatFlags != null) { drawFormat.FormatFlags = formatFlags.Value; } using (Brush brush = new SolidBrush(Color.FromArgb(opacity, textLayer.FontColor))) { Point? origin = textLayer.Position; // Work out the size of the text. SizeF textSize = graphics.MeasureString(text, font, new SizeF(image.Width, image.Height), drawFormat); // We need to ensure that there is a position set for the watermark if (origin == null) { int x = textLayer.RightToLeft ? 0 : (int)(image.Width - textSize.Width) / 2; int y = (int)(image.Height - textSize.Height) / 2; origin = new Point(x, y); fallbackUsed = true; } // Set the hinting and draw the text. graphics.TextRenderingHint = TextRenderingHint.AntiAliasGridFit; // Create bounds for the text. RectangleF bounds; if (textLayer.DropShadow) { // Shadow opacity should change with the base opacity. int shadowOpacity = opacity - (int)Math.Ceiling((30 / 100f) * 255); int finalShadowOpacity = shadowOpacity > 0 ? shadowOpacity : 0; using (Brush shadowBrush = new SolidBrush(Color.FromArgb(finalShadowOpacity, Color.Black))) { // Scale the shadow position to match the font size. // Magic number but it's based on artistic preference. int shadowDiff = (int)Math.Ceiling(fontSize / 24f); Point shadowPoint = new Point(origin.Value.X + shadowDiff, origin.Value.Y + shadowDiff); // Set the bounds so any overlapping text will wrap. if (textLayer.RightToLeft && fallbackUsed) { bounds = new RectangleF(shadowPoint, new SizeF(image.Width - ((int)(image.Width - textSize.Width) / 2) - shadowPoint.X, image.Height - shadowPoint.Y)); } else { bounds = new RectangleF(shadowPoint, new SizeF(image.Width - shadowPoint.X, image.Height - shadowPoint.Y)); } graphics.DrawString(text, font, shadowBrush, bounds, drawFormat); } } // Set the bounds so any overlapping text will wrap. if (textLayer.RightToLeft && fallbackUsed) { bounds = new RectangleF(origin.Value, new SizeF(image.Width - ((int)(image.Width - textSize.Width) / 2), image.Height - origin.Value.Y)); } else { bounds = new RectangleF(origin.Value, new SizeF(image.Width - origin.Value.X, image.Height - origin.Value.Y)); } graphics.DrawString(text, font, brush, bounds, drawFormat); } } } image.Dispose(); image = newImage; } } catch (Exception ex) { if (newImage != null) { newImage.Dispose(); } throw new ImageProcessingException("Error processing image with " + this.GetType().Name, ex); } return image; } /// /// Returns the correct for the given parameters. /// /// /// The name of the font. /// /// /// The font size. /// /// /// The style. /// /// /// The correct /// private Font GetFont(FontFamily fontFamily, int fontSize, FontStyle fontStyle) { try { using (fontFamily) { return new Font(fontFamily, fontSize, fontStyle, GraphicsUnit.Pixel); } } catch { using (FontFamily genericFontFamily = FontFamily.GenericSansSerif) { return new Font(genericFontFamily, fontSize, fontStyle, GraphicsUnit.Pixel); } } } /// /// Returns the correct flags for the given text layer. /// /// /// The to return the flags for. /// /// /// The . /// private StringFormatFlags? GetFlags(TextLayer textLayer) { if (textLayer.Vertical && textLayer.RightToLeft) { return StringFormatFlags.DirectionVertical | StringFormatFlags.DirectionRightToLeft; } if (textLayer.Vertical) { return StringFormatFlags.DirectionVertical; } if (textLayer.RightToLeft) { return StringFormatFlags.DirectionRightToLeft; } return null; } } }