diff --git a/UnitTest/UnitTest.cs b/UnitTest/UnitTest.cs
index be21958..68fac3e 100644
--- a/UnitTest/UnitTest.cs
+++ b/UnitTest/UnitTest.cs
@@ -2,7 +2,8 @@ using SixLabors.Fonts;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.PixelFormats;
using Watermark.Net.src.WatermarkNet.Core;
-using Watermark.Net.src.WatermarkNet.Types;
+using Watermark.Net.src.WatermarkNet.Enums;
+using Watermark.Net.src.WatermarkNet.Models.Definitions;
namespace UnitTest
{
@@ -13,7 +14,6 @@ namespace UnitTest
public void TextWatermarkTest()
{
var watermarker = new Watermarker();
- var watermark = new TextWatermark();
var availableFont = SystemFonts.Families.FirstOrDefault();
if (availableFont == default)
@@ -21,11 +21,13 @@ namespace UnitTest
throw new Exception("No available fonts found in the system");
}
- watermark.Text = "Test";
- watermark.Color = Color.White;
- watermark.Font = availableFont.CreateFont(1);
- watermark.Position = Watermark.Net.src.WatermarkNet.Enums.ImagePosition.BottomCenter;
- watermark.RotateAngle = 90;
+ var watermark = new TextWatermark{
+ Font = availableFont.CreateFont(1),
+ Text = "Test",
+ Style = { Color = Color.White },
+ Layout = { Position = ImagePosition.BottomCenter , RotateAngle = 90 }
+ };
+
var resultedImage = watermarker.ProcessImage("TestImages/2.png", "test/text", watermark);
Assert.IsTrue(File.Exists(resultedImage.Path));
@@ -36,11 +38,12 @@ namespace UnitTest
public void ImageWatermarkTest()
{
var watermarker = new Watermarker();
- var watermark = new ImageWatermark();
+ var watermark = new ImageWatermark {
+ ImagePath = "TestImages/sample_wm.png",
+ Layout = { Position = ImagePosition.Center, Scale = 1 },
+ Style = { Opacity = 1 }
+ };
- watermark.ImagePath = "TestImages/sample_wm.png";
- watermark.Position = Watermark.Net.src.WatermarkNet.Enums.ImagePosition.Center;
- watermark.Scale = 1;
var resultedImage = watermarker.ProcessImage("TestImages/2.png", "test/image", watermark);
Assert.IsTrue(File.Exists(resultedImage.Path));
@@ -51,7 +54,6 @@ namespace UnitTest
public void TextWatermarkDirectoryProccessTest()
{
var watermarker = new Watermarker("test/text/pave");
- var watermark = new TextWatermark();
var availableFont = SystemFonts.Families.FirstOrDefault();
if (availableFont == default)
@@ -59,12 +61,13 @@ namespace UnitTest
throw new Exception("No available fonts found in the system");
}
- watermark.Text = "Test";
- watermark.Color = Rgba32.ParseHex("FFFFFF50");
- watermark.Font = availableFont.CreateFont(1);
- watermark.Scale = 1f;
- watermark.Position = Watermark.Net.src.WatermarkNet.Enums.ImagePosition.TopLeft;
- watermark.Pave = true;
+ var watermark = new TextWatermark {
+ Text = "Test",
+ Font = availableFont.CreateFont(1),
+ Style = { Color = Rgba32.ParseHex("FFFFFF50"), Pave = true },
+ Layout = { Scale = 1f, Position = ImagePosition.TopLeft }
+ };
+
watermarker.ProcessDirectory("TestImages", watermark);
Assert.IsTrue(Directory.GetFiles(watermarker.OutputDir)?.Length > 0);
@@ -74,12 +77,12 @@ namespace UnitTest
public void ImageWatermarkDirectoryProccessTest()
{
var watermarker = new Watermarker("test/image/pave");
- var watermark = new ImageWatermark();
+ var watermark = new ImageWatermark {
+ ImagePath = "TestImages/sample_wm.png",
+ Layout = { Position = ImagePosition.Center, Scale = 1},
+ Style = { Pave = true, Opacity = 1}
+ };
- watermark.ImagePath = "TestImages/sample_wm.png";
- watermark.Position = Watermark.Net.src.WatermarkNet.Enums.ImagePosition.Center;
- watermark.Scale = 1;
- watermark.Pave = true;
watermarker.ProcessDirectory("TestImages", watermark);
Assert.IsTrue(Directory.GetFiles(watermarker.OutputDir)?.Length > 0);
diff --git a/Watermark.Net/Watermark.Net.sln b/Watermark.Net/Watermark.Net.sln
index 7935ff0..e4c5aa0 100644
--- a/Watermark.Net/Watermark.Net.sln
+++ b/Watermark.Net/Watermark.Net.sln
@@ -1,14 +1,12 @@
Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio Version 17
-VisualStudioVersion = 17.4.33213.308
+# Visual Studio Version 18
+VisualStudioVersion = 18.5.11709.299 stable
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Watermark.Net", "Watermark.Net.csproj", "{5000C6D8-A7CD-4815-A724-EEE51AD8AFDA}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UnitTest", "..\UnitTest\UnitTest.csproj", "{26E6D8A0-CFB1-4A30-A4DD-D6DBBDADB388}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Watrmark.Net CLI", "..\Watrmark.Net CLI\Watrmark.Net CLI.csproj", "{EC1F992E-6B68-45E7-8879-8576F728A6F3}"
-EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -23,10 +21,6 @@ Global
{26E6D8A0-CFB1-4A30-A4DD-D6DBBDADB388}.Debug|Any CPU.Build.0 = Debug|Any CPU
{26E6D8A0-CFB1-4A30-A4DD-D6DBBDADB388}.Release|Any CPU.ActiveCfg = Release|Any CPU
{26E6D8A0-CFB1-4A30-A4DD-D6DBBDADB388}.Release|Any CPU.Build.0 = Release|Any CPU
- {EC1F992E-6B68-45E7-8879-8576F728A6F3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {EC1F992E-6B68-45E7-8879-8576F728A6F3}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {EC1F992E-6B68-45E7-8879-8576F728A6F3}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {EC1F992E-6B68-45E7-8879-8576F728A6F3}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/Watermark.Net/src/WatermarkNet.Common/Watermarker.cs b/Watermark.Net/src/WatermarkNet.Common/Watermarker.cs
index a8e6d9d..8b139c2 100644
--- a/Watermark.Net/src/WatermarkNet.Common/Watermarker.cs
+++ b/Watermark.Net/src/WatermarkNet.Common/Watermarker.cs
@@ -4,6 +4,7 @@ using SixLabors.ImageSharp.Drawing.Processing;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
using Watermark.Net.src.WatermarkNet.Enums;
+using Watermark.Net.src.WatermarkNet.Models.Definitions;
using Watermark.Net.src.WatermarkNet.Types;
using static System.Net.Mime.MediaTypeNames;
using static System.Runtime.InteropServices.JavaScript.JSType;
@@ -36,7 +37,7 @@ namespace Watermark.Net.src.WatermarkNet.Core
/// Watermark configuration.
/// List of processed images with watermark information.
public List ProcessDirectory(string directory, T watermark)
- where T : WatermarkImageBase
+ where T : IWatermarkDefinition
{
List processedImages = new List();
foreach (var imageFile in Directory.GetFiles(directory))
@@ -80,8 +81,8 @@ namespace Watermark.Net.src.WatermarkNet.Core
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);
+ var scaledWmWidth = (int)Math.Round(watermarkImage.Width * watermark.Layout.Scale);
+ var scaledWmHeight = (int)Math.Round(watermarkImage.Height * watermark.Layout.Scale);
watermarkImage.Mutate(x => x.Resize(new Size(scaledWmWidth, scaledWmHeight)));
using (var markedImage = targetImage.Clone(ctx => this.ApplyScalingWaterMarkImage(ctx, watermark, watermarkImage, targetImage)))
@@ -255,32 +256,32 @@ namespace Watermark.Net.src.WatermarkNet.Core
float scalingFactor = Math.Min(imgSize.Width / size.Width, imgSize.Height / size.Height);
// Create a new font
- SixLabors.Fonts.Font scaledFont = new SixLabors.Fonts.Font(watermark.Font, scalingFactor / 16 * (watermark.Font.Size * watermark.Scale));
+ SixLabors.Fonts.Font scaledFont = new SixLabors.Fonts.Font(watermark.Font, scalingFactor / 16 * (watermark.Font.Size * watermark.Layout.Scale));
//processingContext.SetGraphicsOptions(new GraphicsOptions { AlphaCompositionMode = SixLabors.ImageSharp.PixelFormats.PixelAlphaCompositionMode.Clear});
//If set, apply backround color
- if (watermark.BackroundColor != null)
- processingContext.BackgroundColor((Color)watermark.BackroundColor);
+ if (watermark.Style.Color != null)
+ processingContext.BackgroundColor((Color)watermark.Style.Color);
var textOptions = new RichTextOptions(scaledFont)
{
ColorFontSupport = ColorFontSupport.MicrosoftColrFormat,
- Origin = CalcWatermarkOrigin(imgSize.Width, imgSize.Height, scaledFont.Size, watermark.Position),
- HorizontalAlignment = HorizontalAlignmentFromPosition(watermark.Position),
+ Origin = CalcWatermarkOrigin(imgSize.Width, imgSize.Height, scaledFont.Size, watermark.Layout.Position),
+ HorizontalAlignment = HorizontalAlignmentFromPosition(watermark.Layout.Position),
VerticalAlignment = VerticalAlignment.Top,
};
- if (watermark.Pave)
+ if (watermark.Style.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);
+ processingContext.DrawText(textOptions, watermark.Text, watermark.Style.Color);
}
return processingContext;
}
return processingContext
- .DrawText(textOptions, watermark.Text, watermark.Color);
+ .DrawText(textOptions, watermark.Text, watermark.Style.Color);
}
///
@@ -311,20 +312,20 @@ namespace Watermark.Net.src.WatermarkNet.Core
: watermarkImage.Height / scaleFactor;
//If set, apply backround color
- if (watermark.BackroundColor != null)
- processingContext.BackgroundColor((Color)watermark.BackroundColor);
+ if (watermark.Style.Color != null)
+ processingContext.BackgroundColor((Color)watermark.Style.Color);
watermarkImage.Mutate(x => x.Resize(new Size((int)scaledWmWidth, (int)scaledWmHeight)));
- var wmPositionOrigin = CalcWatermarkOrigin(targetImage.Width, targetImage.Height, watermarkImage.Width, watermarkImage.Height, watermark.Position);
+ var wmPositionOrigin = CalcWatermarkOrigin(targetImage.Width, targetImage.Height, watermarkImage.Width, watermarkImage.Height, watermark.Layout.Position);
- if (watermark.Pave)
+ if (watermark.Style.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);
+ processingContext.DrawImage(watermarkImage, wmPositionOrigin, watermark.Style.Opacity);
}
catch (Exception ex)
{
@@ -334,7 +335,7 @@ namespace Watermark.Net.src.WatermarkNet.Core
return processingContext;
}
- return processingContext.DrawImage(watermarkImage, wmPositionOrigin, watermark.Opacity);
+ return processingContext.DrawImage(watermarkImage, wmPositionOrigin, watermark.Style.Opacity);
}
}
}
\ No newline at end of file
diff --git a/Watermark.Net/src/WatermarkNet.Models/Definitions/IWatermarkDefinition.cs b/Watermark.Net/src/WatermarkNet.Models/Definitions/IWatermarkDefinition.cs
new file mode 100644
index 0000000..e3eb823
--- /dev/null
+++ b/Watermark.Net/src/WatermarkNet.Models/Definitions/IWatermarkDefinition.cs
@@ -0,0 +1,16 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Watermark.Net.src.WatermarkNet.Models.Layout;
+using Watermark.Net.src.WatermarkNet.Models.Styling;
+
+namespace Watermark.Net.src.WatermarkNet.Models.Definitions
+{
+ public interface IWatermarkDefinition
+ {
+ WatermarkLayout Layout { get; }
+ WatermarkStyle Style { get; }
+ }
+}
diff --git a/Watermark.Net/src/WatermarkNet.Models/Definitions/ImageWatermark.cs b/Watermark.Net/src/WatermarkNet.Models/Definitions/ImageWatermark.cs
new file mode 100644
index 0000000..b22b33f
--- /dev/null
+++ b/Watermark.Net/src/WatermarkNet.Models/Definitions/ImageWatermark.cs
@@ -0,0 +1,28 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Watermark.Net.src.WatermarkNet.Models.Layout;
+using Watermark.Net.src.WatermarkNet.Models.Styling;
+
+namespace Watermark.Net.src.WatermarkNet.Models.Definitions
+{
+ public class ImageWatermark : IWatermarkDefinition
+ {
+ public required string ImagePath { get; init; }
+
+ public WatermarkLayout Layout
+ {
+ get;
+ init;
+ } = new();
+
+
+ public WatermarkStyle Style
+ {
+ get;
+ init;
+ } = new();
+ }
+}
diff --git a/Watermark.Net/src/WatermarkNet.Models/Definitions/TextWatermark.cs b/Watermark.Net/src/WatermarkNet.Models/Definitions/TextWatermark.cs
new file mode 100644
index 0000000..e444183
--- /dev/null
+++ b/Watermark.Net/src/WatermarkNet.Models/Definitions/TextWatermark.cs
@@ -0,0 +1,26 @@
+using SixLabors.Fonts;
+using SixLabors.ImageSharp;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection.Metadata.Ecma335;
+using System.Text;
+using System.Threading.Tasks;
+using Watermark.Net.src.WatermarkNet.Models.Layout;
+using Watermark.Net.src.WatermarkNet.Models.Styling;
+
+namespace Watermark.Net.src.WatermarkNet.Models.Definitions
+{
+ public class TextWatermark : IWatermarkDefinition
+ {
+ public required string Text { get; set; }
+
+ public required Font Font { get; set; }
+
+ public WatermarkLayout Layout { get; init; }
+ = new();
+
+ public WatermarkStyle Style { get; init; }
+ = new();
+ }
+}
diff --git a/Watermark.Net/src/WatermarkNet.Models/Layout/WatermarkLayout.cs b/Watermark.Net/src/WatermarkNet.Models/Layout/WatermarkLayout.cs
new file mode 100644
index 0000000..5766c38
--- /dev/null
+++ b/Watermark.Net/src/WatermarkNet.Models/Layout/WatermarkLayout.cs
@@ -0,0 +1,20 @@
+using Watermark.Net.src.WatermarkNet.Enums;
+
+namespace Watermark.Net.src.WatermarkNet.Models.Layout
+{
+ public class WatermarkLayout
+ {
+ public ImagePosition Position { get; set; }
+
+ public float Scale { get; set; }
+
+ public int RotateAngle { get; set; }
+
+ ///
+ /// Gets or sets the padding space around the watermark in pixels.
+ /// Default: 10px
+ ///
+ /// Thrown when value is negative.
+ public float Padding { get; set; }
+ }
+}
diff --git a/Watermark.Net/src/WatermarkNet.Models/Styling/WatermarkStyle.cs b/Watermark.Net/src/WatermarkNet.Models/Styling/WatermarkStyle.cs
new file mode 100644
index 0000000..911610c
--- /dev/null
+++ b/Watermark.Net/src/WatermarkNet.Models/Styling/WatermarkStyle.cs
@@ -0,0 +1,21 @@
+using SixLabors.Fonts;
+using SixLabors.ImageSharp;
+
+namespace Watermark.Net.src.WatermarkNet.Models.Styling
+{
+ public class WatermarkStyle
+ {
+ ///
+ /// Gets or sets the transparency level of the watermark.
+ /// Range: 0.0 (fully transparent) to 1.0 (completely opaque).
+ /// Default: 1.0
+ ///
+ ///
+ /// Thrown when value is outside the 0.0-1.0 range.
+ ///
+ public float Opacity { get; set; }
+
+ public bool Pave { get; set; }
+ public Color Color { get; set; }
+ }
+}
diff --git a/Watermark.Net/src/WatermarkNet.Types/ImageWatermark.cs b/Watermark.Net/src/WatermarkNet.Types/ImageWatermark.cs
deleted file mode 100644
index 8beb8e0..0000000
--- a/Watermark.Net/src/WatermarkNet.Types/ImageWatermark.cs
+++ /dev/null
@@ -1,75 +0,0 @@
-using System;
-using System.IO;
-
-namespace Watermark.Net.src.WatermarkNet.Types
-{
- ///
- /// Represents a configurable image-based watermark with scaling, positioning, and transparency controls.
- ///
- public class ImageWatermark : WatermarkImageBase
- {
- private string _imagePath;
- private float? _opacity;
-
- ///
- /// Gets or sets the absolute path to the watermark image file.
- /// Supported formats: PNG, JPEG, BMP, GIF.
- ///
- ///
- /// Thrown when setting a value that doesn't point to an existing file.
- ///
- public string ImagePath
- {
- get => _imagePath;
- set
- {
- if (!File.Exists(value))
- throw new FileNotFoundException("Watermark image not found", value);
-
- _imagePath = value;
- }
- }
-
- ///
- /// Gets or sets the transparency level of the watermark.
- /// Range: 0.0 (fully transparent) to 1.0 (completely opaque).
- /// Default: 1.0
- ///
- ///
- /// Thrown when value is outside the 0.0-1.0 range.
- ///
- public float Opacity
- {
- get => _opacity ?? 1f;
- set
- {
- if (value < 0 || value > 1)
- throw new ArgumentOutOfRangeException(nameof(Opacity),
- "Opacity must be between 0.0 and 1.0");
-
- _opacity = value;
- }
- }
-
- ///
- /// Gets or sets the scale factor applied to the watermark image relative to the target image.
- /// Default: 0.2 (20% of target image width)
- ///
- public float Scale { get; set; } = 0.2f;
-
- ///
- /// Initializes a new instance of the ImageWatermark class with specified image path.
- ///
- /// Path to the watermark image file.
- public ImageWatermark(string imagePath) : this()
- {
- ImagePath = imagePath;
- }
-
- ///
- /// Initializes a new instance of the ImageWatermark class with default values.
- ///
- public ImageWatermark() { }
-
- }
-}
\ No newline at end of file
diff --git a/Watermark.Net/src/WatermarkNet.Types/TextWatermark.cs b/Watermark.Net/src/WatermarkNet.Types/TextWatermark.cs
deleted file mode 100644
index 1ca68d3..0000000
--- a/Watermark.Net/src/WatermarkNet.Types/TextWatermark.cs
+++ /dev/null
@@ -1,88 +0,0 @@
-using SixLabors.Fonts;
-using SixLabors.ImageSharp;
-
-namespace Watermark.Net.src.WatermarkNet.Types
-{
- ///
- /// Represents a configurable text-based watermark with styling, positioning, and rendering options.
- ///
- public class TextWatermark : WatermarkImageBase
- {
- private string _text;
- private Font _font;
- private float _padding;
- private Color _color;
-
- ///
- /// Gets or sets the color of the text watermark.
- /// Default: White (#FFFFFF)
- ///
- public Color Color
- {
- get => _color;
- set => _color = value;
- }
-
- ///
- /// Gets or sets the text content for the watermark.
- ///
- /// Thrown when value is null or whitespace.
- public string Text
- {
- get => _text;
- set
- {
- if (string.IsNullOrWhiteSpace(value))
- throw new ArgumentException("Watermark text cannot be empty", nameof(Text));
- _text = value;
- }
- }
-
- ///
- /// Gets or sets the font used to render the text watermark.
- /// Default: Arial 12pt
- ///
- /// Thrown when value is null.
- public Font Font
- {
- get => _font;
- set => _font = value ?? throw new ArgumentNullException(nameof(Font));
- }
-
- ///
- /// Gets or sets the padding space around the text watermark in pixels.
- /// Default: 10px
- ///
- /// Thrown when value is negative.
- public float Padding
- {
- get => _padding;
- set
- {
- if (value < 0) throw new ArgumentOutOfRangeException(nameof(Padding), "Padding cannot be negative");
- _padding = value;
- }
- }
-
- ///
- /// Gets or sets the rotation angle for the text watermark in degrees.
- /// Default: 0 (no rotation)
- ///
- public float Rotation { get; set; } = 0;
-
- ///
- /// Initializes a new instance of the TextWatermark class with default values.
- ///
- public TextWatermark()
- {
- var availableFont = SystemFonts.Families.FirstOrDefault();
- if (availableFont == default)
- {
- throw new Exception("No available fonts found in the system");
- }
- _color = Color.White;
- _font = availableFont.CreateFont(1);
- _padding = 10f;
- }
- }
-}
\ No newline at end of file
diff --git a/Watermark.Net/src/WatermarkNet.Types/WatermarkImageBase.cs b/Watermark.Net/src/WatermarkNet.Types/WatermarkImageBase.cs
deleted file mode 100644
index a9dfbc0..0000000
--- a/Watermark.Net/src/WatermarkNet.Types/WatermarkImageBase.cs
+++ /dev/null
@@ -1,60 +0,0 @@
-using SixLabors.ImageSharp;
-using Watermark.Net.src.WatermarkNet.Enums;
-
-namespace Watermark.Net.src.WatermarkNet.Types
-{
- public class WatermarkImageBase
- {
- protected ImagePosition _postion;
- protected float _scale;
- protected int _rotateAngle;
- protected bool _pave;
- protected Color? _backround;
-
- public ImagePosition Position
- {
- get { return _postion; }
- set
- {
- _postion= value;
- }
- }
-
- public float Scale
- {
- get { return _scale; }
- set
- {
- if(value <= 0) { throw new ArgumentOutOfRangeException("Scale", "Image scale can not be less or equal zero."); }
- _scale= value;
- }
- }
-
- public int RotateAngle
- {
- get { return _rotateAngle; }
- set
- {
- if (Math.Abs(value) > 180) { throw new ArgumentOutOfRangeException("RotateAngle", "Image rotate angle can not be larger than 180°."); }
- _rotateAngle= value;
- }
- }
-
- public bool Pave
- {
- get { return _pave; }
- set { _pave = value; }
- }
-
- public Color? BackroundColor
- {
- get { return _backround; }
- set { _backround = value; }
- }
-
- public WatermarkImageBase()
- {
- _scale= 1.0f;
- }
- }
-}