Jump to content

C# QR Code Generator

SoftwareNinja

Hi all,

 

I am looking for a c# QR Code generator that is not basic..

 

I have seen some QR Codes around the web that have like rounded corners or dotted styles. I was wondering if someone has come across a library for this?

 

I have been using QRCoder but they look to basic and I'm not sure if there is a way to style them within the library either.

 

Any help would be great, thanks.

Link to comment
Share on other sites

Link to post
Share on other sites

As a workaround, I'd look into image generation/manipulation libraries instead.

 

A QR code is simply a 2D array containing 1s and 0s. To get something fancy, just create a new image and then put a rounded dot wherever there is a 1 in your array. Or use it to round the corners off of your QR code image once you've created it etc.

 

Separation of concerns: Use the QR code library that is best at creating QR codes and then use some other libraries to turn the raw code/array into a fancy image.

Remember to either quote or @mention others, so they are notified of your reply

Link to comment
Share on other sites

Link to post
Share on other sites

23 minutes ago, Eigenvektor said:

As a workaround, I'd look into image generation/manipulation libraries instead.

 

A QR code is simply a 2D array containing 1s and 0s. To get something fancy, just create a new image and then put a rounded dot wherever there is a 1 in your array. Or use it to round the corners off of your QR code image once you've created it etc.

 

Separation of concerns: Use the QR code library that is best at creating QR codes and then use some other libraries to turn the raw code/array into a fancy image.

Have you any examples of something like this or recommendations of libraries to turn code/arrays to fancy image?

Link to comment
Share on other sites

Link to post
Share on other sites

For paid options there's https://scanova.io/blog/qr-code-sdk/ and https://www.unitag.io/qrcode.  I expect writing your own library to 'prettify' your existing generations is also not too difficult. It will depend on your exact setup/target but I expect using an SVG library or similar shouldn't be too bad. The QR standard is pretty straightforward, so as long as your generation library gives you the information for all the dots, you should be able to just take that and repurpose them in the colour/shape you want. 

Link to comment
Share on other sites

Link to post
Share on other sites

On 8/10/2020 at 9:05 PM, ScottDurkin said:

Have you any examples of something like this or recommendations of libraries to turn code/arrays to fancy image?

I haven't used C# in some time, so I don't know any drawing libraries to recommend. As @AMartin223 said, any library that can generate an image and draw shapes onto it should do.

 

Basically my idea is:

- Use the QR code library to generate a QR code (ideally as an array of "bits" or booleans)

- Iterate over that array

- Draw a shape onto the canvas at the appropriate position for each "1" in your array

- Save that as an image

Remember to either quote or @mention others, so they are notified of your reply

Link to comment
Share on other sites

Link to post
Share on other sites

Sorry am a bit late to the party on this ScottDurkin not sure if you have gotten your solution yet.

 

As other's have suggested, it is easier just taking the bits and just dumping it into an image...but there is a caveat.  How QR codes are read by devices may vary, so not all solutions will work (or have varying degrees).  An example being, I just tried using a circle instead of rectangles...my phone had to read the QR code from further back (up close it saw the white spaces and didn't recognize it as a QR code)...with that said it is still pretty versatile (changing the circle to 1.1x the size actually fixed all issues my phone had).

 

Example of the library I used.

https://github.com/codebude/QRCoder

 

There is an example there as well...but it uses the class QRCode to draw to a bitmap.  I just used the QRCodeData and generated the bitmap that way.

This is my modified form example (note that I added a ComboBox called comboBoxDrawType so if you copy paste my code it won't work unless you add it in...using values 0 1 2 3 4 5 as options).  Apologies, I only spent a small amount of time on this...but look at my modified private Bitmap GetGraphic function.  That is where there is rounded corners/circles/rectangles/weird looking ones that still work.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using QRCoder;
using System.Drawing.Imaging;
using System.IO;

namespace QRCoderDemo
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            comboBoxECC.SelectedIndex = 0; //Pre-select ECC level "L"
            comboBoxDrawType.SelectedIndex = 0;
            RenderQrCode();
        }

        private void buttonGenerate_Click(object sender, EventArgs e)
        {
            RenderQrCode();
        }

        private void RenderQrCode()
        {
            string level = comboBoxECC.SelectedItem.ToString();
            if (comboBoxDrawType.SelectedItem is null)
                return;
            QRCodeGenerator.ECCLevel eccLevel = (QRCodeGenerator.ECCLevel)(level == "L" ? 0 : level == "M" ? 1 : level == "Q" ? 2 : 3);
            using (QRCodeGenerator qrGenerator = new QRCodeGenerator())
            {
                using (QRCodeData qrCodeData = qrGenerator.CreateQrCode(textBoxQRCode.Text, eccLevel))
                {
                        pictureBoxQRCode.BackgroundImage = GetGraphic(qrCodeData, 20, Color.Black, Color.White, Color.Red, int.Parse(comboBoxDrawType.SelectedItem.ToString()),
                            true);

                         this.pictureBoxQRCode.Size = new System.Drawing.Size(pictureBoxQRCode.Width, pictureBoxQRCode.Height);
                        //Set the SizeMode to center the image.
                        this.pictureBoxQRCode.SizeMode = PictureBoxSizeMode.CenterImage;

                        pictureBoxQRCode.SizeMode = PictureBoxSizeMode.StretchImage;
                }
            }
        }

        private Bitmap GetIconBitmap()
        {
            Bitmap img = null;
            if (iconPath.Text.Length > 0)
            {
                try
                {
                    img = new Bitmap(iconPath.Text);
                }
                catch (Exception)
                {
                }
            }
            return img;
        }

        private void selectIconBtn_Click(object sender, EventArgs e)
        {
            OpenFileDialog openFileDlg = new OpenFileDialog();
            openFileDlg.Title = "Select icon";
            openFileDlg.Multiselect = false;
            openFileDlg.CheckFileExists = true;
            if (openFileDlg.ShowDialog() == System.Windows.Forms.DialogResult.OK)
            {
                iconPath.Text = openFileDlg.FileName;
                if (iconSize.Value == 0)
                {
                    iconSize.Value = 15;
                }
            }
            else
            {
                iconPath.Text = "";
            }
        }


        private void btn_save_Click(object sender, EventArgs e)
        {

            // Displays a SaveFileDialog so the user can save the Image
            SaveFileDialog saveFileDialog1 = new SaveFileDialog();
            saveFileDialog1.Filter = "Bitmap Image|*.bmp|PNG Image|*.png|JPeg Image|*.jpg|Gif Image|*.gif";
            saveFileDialog1.Title = "Save an Image File";
            saveFileDialog1.ShowDialog();

            // If the file name is not an empty string open it for saving.
            if (saveFileDialog1.FileName != "")
            {
                // Saves the Image via a FileStream created by the OpenFile method.
                using (FileStream fs = (System.IO.FileStream) saveFileDialog1.OpenFile())
                {
                    // Saves the Image in the appropriate ImageFormat based upon the
                    // File type selected in the dialog box.
                    // NOTE that the FilterIndex property is one-based.

                    ImageFormat imageFormat = null;
                    switch (saveFileDialog1.FilterIndex)
                    {
                        case 1:
                            imageFormat = ImageFormat.Bmp;
                            break;
                        case 2:
                            imageFormat = ImageFormat.Png;
                            break;
                        case 3:
                            imageFormat = ImageFormat.Jpeg;
                            break;
                        case 4:
                            imageFormat = ImageFormat.Gif;
                            break;
                        default:
                            throw new NotSupportedException("File extension is not supported");
                    }

                    pictureBoxQRCode.BackgroundImage.Save(fs, imageFormat);
                    fs.Close();
                }
            }





        }

        public void ExportToBmp(string path)
        {

        }

        private void textBoxQRCode_TextChanged(object sender, EventArgs e)
        {
            RenderQrCode();
        }

        private void comboBoxECC_SelectedIndexChanged(object sender, EventArgs e)
        {
            RenderQrCode();
        }


        private Bitmap GetGraphic(QRCodeData qrCodeData, int pixelsPerModule, Color darkColor, Color lightColor, Color backgroundColor, int drawStyle=0, bool drawQuietZones = true)
        {
            var size = (qrCodeData.ModuleMatrix.Count + (drawQuietZones ? 0 : 8));
            var offset = drawQuietZones ? 0 : 4;

            var bmp = new Bitmap(size*pixelsPerModule, size * pixelsPerModule);
            using (var gfx = Graphics.FromImage(bmp))
            using (var lightBrush = new SolidBrush(lightColor))
            using (var darkBrush = new SolidBrush(darkColor)) {
                gfx.Clear(backgroundColor); //Added this since there aren't rectangles anymore
                for (var x = 0; x < size; x++)
                {
                    for(var y = 0; y < size; y++)
                    {
                        var module = qrCodeDataIsOn(qrCodeData, x, y);

                        var brushColor = lightBrush;
                        if (module)
                            brushColor = darkBrush;

                        switch (drawStyle)
                        {
                            case 0:
                            default:
                                gfx.FillRectangle(brushColor, getRectFromXY(x, y, offset, pixelsPerModule));
                                break;
                            case 1:
                                gfx.FillEllipse(brushColor, getRectFromXY(x, y, offset, pixelsPerModule));
                                break;
                            case 2: //Fill 10% more of the drawing...prevents white spaces which might trick QR readers
                                gfx.FillEllipse(brushColor, getRectFromXY(x, y, offset, pixelsPerModule, 1.1f));
                                break;
                            case 3: //Fill 200% more of the drawing...just because it looks weird
                                gfx.FillEllipse(brushColor, getRectFromXY(x, y, offset, pixelsPerModule, 2.0f));
                                break;
                            case 4: //Fill 400% more of the drawing...just because it looks weird
                                gfx.FillEllipse(brushColor, getRectFromXY(x, y, offset, pixelsPerModule, 4.0f));
                                break;
                            case 5: //Rounded
                                gfx.FillEllipse(brushColor, getRectFromXY(x, y, offset, pixelsPerModule));
                                if (qrCodeDataIsOn(qrCodeData, x - 1, y))//There is something to the left, so we actually should have drawn a square
                                {
                                    //Only drawing a rectangle 1/4 the size but drawing 2 of them so we cover the left side
                                    gfx.FillRectangle(brushColor, getRectFromXY(x, y, offset, pixelsPerModule, 0.5f));
                                    gfx.FillRectangle(brushColor, getRectFromXY(x, y + 0.5f, offset, pixelsPerModule, 0.5f));
                                }
                                if (qrCodeDataIsOn(qrCodeData, x + 1, y))//There is something to the right, so we actually should have drawn a square
                                {
                                    //Only drawing a rectangle 1/4 the size but drawing 2 of them so we cover the right side
                                    gfx.FillRectangle(brushColor, getRectFromXY(x + 0.5f, y, offset, pixelsPerModule, 0.5f));
                                    gfx.FillRectangle(brushColor, getRectFromXY(x + 0.5f, y + 0.5f, offset, pixelsPerModule, 0.5f));
                                }
                                if (qrCodeDataIsOn(qrCodeData, x, y - 1))
                                {
                                    gfx.FillRectangle(brushColor, getRectFromXY(x, y, offset, pixelsPerModule, 0.5f));
                                    gfx.FillRectangle(brushColor, getRectFromXY(x + 0.5f, y, offset, pixelsPerModule, 0.5f));
                                }
                                if (qrCodeDataIsOn(qrCodeData, x, y + 1))
                                {
                                    gfx.FillRectangle(brushColor, getRectFromXY(x, y + 0.5f, offset, pixelsPerModule, 0.5f));
                                    gfx.FillRectangle(brushColor, getRectFromXY(x + 0.5f, y + 0.5f, offset, pixelsPerModule, 0.5f));
                                }
                                break;
                        }
                    }
                }

                gfx.Save();
            }

            return bmp;
        }

        private RectangleF getRectFromXY(float x, float y, float offset, float pixelsPerModule, float dotOversizeFactor = 1.0f)
        {
            return new RectangleF((x + offset) * pixelsPerModule, (y + offset) * pixelsPerModule, pixelsPerModule * dotOversizeFactor, pixelsPerModule * dotOversizeFactor);
        }

        //Doing in this way so I don't have to worry about going out of index either above or below
        private bool qrCodeDataIsOn(QRCodeData qrCodeData, int x, int y)
        {
            if (x < 0 || y < 0 || qrCodeData.ModuleMatrix.Count <= x || qrCodeData.ModuleMatrix.Count <= y)
                return false;
            return qrCodeData.ModuleMatrix[y][x];
        }

        private void comboBoxDrawType_SelectedIndexChanged(object sender, EventArgs e)
        {
            RenderQrCode();
        }
    }
}

 

3735928559 - Beware of the dead beef

Link to comment
Share on other sites

Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×