Search code examples
c#watermarkdrawstring

C#: Using drawstring to annotate images works but not able to get text in bottom left corner, only bottom right


I'm making a batch watermarking tool for myself and some others at work and using the following code allows me to annotate text on the bottom right of the image but I'm not able to make it annotate on the bottom left without manually adjusting the coordinates which differs for any given image. Also changing StringAlignment.Far to StringAlignment.Near etc doesn't do anything but possibly annotate the text outside the image somewhere that doesn't show up.

MSDN has some explanation but it is not helping me. Any help would be great I've been fighting this for some time now.

private void button1_Click(object sender, EventArgs e)
{
    foreach (string images in Directory.GetFiles(textBox1.Text))
    {
        System.Drawing.Image img = System.Drawing.Image.FromFile(images);

        Graphics gr = Graphics.FromImage(img);

        Font font = new Font("Times New Roman", (float)25, 
            System.Drawing.FontStyle.Regular);
        System.Drawing.Color color = System.Drawing.Color.Red;

        StringFormat stringFormat = new StringFormat();
        stringFormat.Alignment = StringAlignment.Far;
        stringFormat.LineAlignment = StringAlignment.Far;

        gr.SmoothingMode = SmoothingMode.AntiAlias;

        gr.DrawString("WATERMARK GOES HERE"+ images, font, 
            new System.Drawing.SolidBrush(color), 
            new System.Drawing.Point(img.Width - 0, img.Height - 0), 
            stringFormat);

        MemoryStream outputStream = new MemoryStream();
        img.Save(images+"Stamped.jpg");
    }

    MessageBox.Show("done");
}

Solution

    • Name your controls. Don't use "button1", "textbox1", etc.
    • Use the "using" statement. Writing "System.Drawing.Point" and other fully qualified names just increases the size of your code and makes it harder to read.
    • You are creating a new instance of a SolidBrush class for each image you are watermarking. You should create the brush before the loop and just use it in the loop, then dispose of it afterwards.
    • Your declaration of a MemoryStream does nothing and is used nowhere.

    As for the watermarking itself, you should decide if you want it to scale with image size, or to be a consistent size. Or you can have it have a maximum/minimum size. That's your preference.

    private void watermark_btn_Click(object sender, EventArgs e)
    {
        string watermarkText = "ShowThisWatermark";
    
        using (Font font = new Font("Times New Roman", (float)25, FontStyle.Regular))
        using (SolidBrush brush = new SolidBrush(Color.Red))
        foreach (string file in Directory.GetFiles(directory_txt.Text))
        {
            try
            {
                Bitmap b = new Bitmap(file);
    
                using (Graphics g = Graphics.FromImage(b))
                {
                    g.SmoothingMode = SmoothingMode.AntiAlias;
    
                    SizeF measuredSize = g.MeasureString(watermarkText, font);
    
                    // Use this to watermark the bottom-left corner
                    g.DrawString(watermarkText, font, brush, 0, b.Height - measuredSize.Height);
    
                    // Use this to watermark the bottom-right corner
                    g.DrawString(watermarkText, font, brush, b.Width - measuredSize.Width, b.Height - measuredSize.Height);
                }
    
                b.Save(Path.GetFileNameWithoutExtension(file) + "_stamped" + Path.GetExtension(file));
            }
            catch
            {
                continue;
            }
        }
    }
    

    The try/catch is a lazy way of skipping files which aren't images. Since Directory.GetFiles returns all files in the directory, a non-image file would cause an exception. This could be done in a much neater fashion, but since that was not the nature of your question I kept it simple.