THIS ARTICLE IS OBSOLETE.
PLEASE GO TO THE FOLLOWING ARTICLE INSTEAD.
Generate QR Code® barcodes in an SSRS report with the QRCoder library
If you want a thorough understanding of two-dimensional QR Code bar codes, you should probably start with the standards specification ISO/IEC 18004:2006, which can be downloaded from http://raidenii.net/files/datasheets/misc/qr_code.pdf. Alternatively, you can look at source code implementing QR Code encoding. There are a number of open source QR Code libraries available (QrCode.Net, QR Code for C#, ZXing.Net, Quricol, QR Code Library and more), but very little documentation on how to implement them. QR Code for C# (QRCode4CS) is one of the more lightweight libraries, and has no dependcies other than the standard .NET namespaces.
In this article we will:
- Illustrate C# code that uses the QRCode4CS class library to generate QRCode images
- Download and repair the QRCode4CS source code
- Compile the QRCode4CS class library
- Create a Windows Form application that references the QRCode4CS class library to generate a bitmap QR code image
- Run the QR code image generator and save a QR code image file
- Upload the QR code image file to an online QR code reader to demonstrate that the generated QR code image is decipherable
- Create a signed, partially-trusted caller CLR assemby from the QRCode4CS class library
- Create a signed, partially-trusted caller CLR assemby that references the QRCode4CS class library assembly to generate a QR Code byte array
- Deploy the CLR Assemblies to the Global Assembly Cache
- Use the CLR Assemblies to generate QR codes in SSRS
How to use the QRCode4CS class library
This is the extent of the documentation for QRCode4CS on its download page (http://qrcode4cs.codeplex.com/releases/view/74015) at CodePlex.com.
Figure 1: QRCode4CS usage hint
Below are two C# functions that implements the QRCode4CS library with drawing code inserted. The first function below accepts an input string and returns a bitmap image in PNG format. Use this function with Windows form applications.
Figure 2: C# function that returns a bitmap image
The next function below accepts an input string and returns a byte array. Use this function with SSRS.
Figure 2: C# function that returns a byte array
This is what a 34x34 pixel QR code image generated by the above C# functions looks like.
Figure 3: QR Code generated by above functions
Download and repair the QRCode4CS source
The QRCode4CS.cs source file can be downloaded from codeplex.com at http://qrcode4cs.codeplex.com/releases/view/74015.
If you try to compile the downloaded code to a class library in Microsoft Visual C# 2010 Express, you will get the following six identical errors.
Figure 4: Error list when compiling QRCode4CS.cs
This is where two of the errors occur in the code.
Figure 5: Specific errors when compiling QRCode4CS.cs
The errors occur because there is no need to use the Math.Floor() function in the calculation. All the terms in the equation are integers, ensuring that .NET will perform integer division and automatically truncate the remainder of the output. To repair the code, simply delete the string "Math.Floor" in the six places where it occurs.
Below is the code with the offending set of strings removed. It will successfully compile to a class library.
//--------------------------------------------------------------------- // QRCode for C#4.0 Silverlight is translation of QRCode for JavaScript // https://github.com/jeromeetienne/jquery-qrcode/ // // Copyright (c) 2009 Kazuhiko Arase // // URL: http://www.d-project.com/ // // Licensed under the MIT license: // http://www.opensource.org/licenses/mit-license.php // // The word "QR Code" is registered trademark of // DENSO WAVE INCORPORATED // http://www.denso-wave.com/qrcode/faqpatent-e.html // //--------------------------------------------------------------------- namespace QRCode4CS { using System; using System.Collections.Generic; public class Error : Exception { public Error() { } public Error(string message) : base(message) { } public Error(string message, Exception inner) : base(message, inner) { } } public enum QRMode : int { MODE_NUMBER = 1 << 0, MODE_ALPHA_NUM = 1 << 1, MODE_8BIT_BYTE = 1 << 2, MODE_KANJI = 1 << 3 } public enum QRErrorCorrectLevel : int { L = 1, M = 0, Q = 3, H = 2 } public enum QRMaskPattern : int { PATTERN000 = 0, PATTERN001 = 1, PATTERN010 = 2, PATTERN011 = 3, PATTERN100 = 4, PATTERN101 = 5, PATTERN110 = 6, PATTERN111 = 7 } public struct Options { public int Width { get; set; } public int Height { get; set; } public QRErrorCorrectLevel CorrectLevel { get; set; } public int TypeNumber { get; set; } public string Text { get; set; } public Options(string text) : this() { Width = 256; Height = 256; TypeNumber = 4; CorrectLevel = QRErrorCorrectLevel.H; Text = text; } } internal static class QRUtil { internal const int G15 = (1 << 10) | (1 << 8) | (1 << 5) | (1 << 4) | (1 << 2) | (1 << 1) | (1 << 0); internal const int G18 = (1 << 12) | (1 << 11) | (1 << 10) | (1 << 9) | (1 << 8) | (1 << 5) | (1 << 2) | (1 << 0); internal const int G15_MASK = (1 << 14) | (1 << 12) | (1 << 10) | (1 << 4) | (1 << 1); internal static readonly int[][] PATTERN_POSITION_TABLE = new int[][] { new int[] {}, new int [] {6, 18}, new int [] {6, 22}, new int [] {6, 26}, new int [] {6, 30}, new int [] {6, 34}, new int [] {6, 22, 38}, new int [] {6, 24, 42}, new int [] {6, 26, 46}, new int [] {6, 28, 50}, new int [] {6, 30, 54}, new int [] {6, 32, 58}, new int [] {6, 34, 62}, new int [] {6, 26, 46, 66}, new int [] {6, 26, 48, 70}, new int [] {6, 26, 50, 74}, new int [] {6, 30, 54, 78}, new int [] {6, 30, 56, 82}, new int [] {6, 30, 58, 86}, new int [] {6, 34, 62, 90}, new int [] {6, 28, 50, 72, 94}, new int [] {6, 26, 50, 74, 98}, new int [] {6, 30, 54, 78, 102}, new int [] {6, 28, 54, 80, 106}, new int [] {6, 32, 58, 84, 110}, new int [] {6, 30, 58, 86, 114}, new int [] {6, 34, 62, 90, 118}, new int [] {6, 26, 50, 74, 98, 122}, new int [] {6, 30, 54, 78, 102, 126}, new int [] {6, 26, 52, 78, 104, 130}, new int [] {6, 30, 56, 82, 108, 134}, new int [] {6, 34, 60, 86, 112, 138}, new int [] {6, 30, 58, 86, 114, 142}, new int [] {6, 34, 62, 90, 118, 146}, new int [] {6, 30, 54, 78, 102, 126, 150}, new int [] {6, 24, 50, 76, 102, 128, 154}, new int [] {6, 28, 54, 80, 106, 132, 158}, new int [] {6, 32, 58, 84, 110, 136, 162}, new int [] {6, 26, 54, 82, 110, 138, 166}, new int [] {6, 30, 58, 86, 114, 142, 170} }; internal static int GetLengthInBits(QRMode mode, int type) { if (1 <= type && type < 10) { // 1 - 9 switch (mode) { case QRMode.MODE_NUMBER: return 10; case QRMode.MODE_ALPHA_NUM: return 9; case QRMode.MODE_8BIT_BYTE: return 8; case QRMode.MODE_KANJI: return 8; default: throw new Error("mode:" + mode); } } else if (type < 27) { // 10 - 26 switch (mode) { case QRMode.MODE_NUMBER: return 12; case QRMode.MODE_ALPHA_NUM: return 11; case QRMode.MODE_8BIT_BYTE: return 16; case QRMode.MODE_KANJI: return 10; default: throw new Error("mode:" + mode); } } else if (type < 41) { // 27 - 40 switch (mode) { case QRMode.MODE_NUMBER: return 14; case QRMode.MODE_ALPHA_NUM: return 13; case QRMode.MODE_8BIT_BYTE: return 16; case QRMode.MODE_KANJI: return 12; default: throw new Error("mode:" + mode); } } else { throw new Error("type:" + type); } } internal static double GetLostPoint(QRCode qrCode) { int moduleCount = qrCode.GetModuleCount(); double lostPoint = 0; for (int row = 0; row < moduleCount; row++) { for (int col = 0; col < moduleCount; col++) { var sameCount = 0; var dark = qrCode.IsDark(row, col); for (var r = -1; r <= 1; r++) { if (row + r < 0 || moduleCount <= row + r) { continue; } for (var c = -1; c <= 1; c++) { if (col + c < 0 || moduleCount <= col + c) { continue; } if (r == 0 && c == 0) { continue; } if (dark == qrCode.IsDark((int)((int)row + r), (int)((int)col + c))) { sameCount++; } } } if (sameCount > 5) { lostPoint += (int)(3 + sameCount - 5); } } } // LEVEL2 for (int row = 0; row < moduleCount - 1; row++) { for (int col = 0; col < moduleCount - 1; col++) { var count = 0; if (qrCode.IsDark(row, col)) count++; if (qrCode.IsDark(row + 1, col)) count++; if (qrCode.IsDark(row, col + 1)) count++; if (qrCode.IsDark(row + 1, col + 1)) count++; if (count == 0 || count == 4) { lostPoint += 3; } } } // LEVEL3 for (int row = 0; row < moduleCount; row++) { for (int col = 0; col < moduleCount - 6; col++) { if (qrCode.IsDark(row, col) && !qrCode.IsDark(row, col + 1) && qrCode.IsDark(row, col + 2) && qrCode.IsDark(row, col + 3) && qrCode.IsDark(row, col + 4) && !qrCode.IsDark(row, col + 5) && qrCode.IsDark(row, col + 6)) { lostPoint += 40; } } } for (int col = 0; col < moduleCount; col++) { for (int row = 0; row < moduleCount - 6; row++) { if (qrCode.IsDark(row, col) && !qrCode.IsDark(row + 1, col) && qrCode.IsDark(row + 2, col) && qrCode.IsDark(row + 3, col) && qrCode.IsDark(row + 4, col) && !qrCode.IsDark(row + 5, col) && qrCode.IsDark(row + 6, col)) { lostPoint += 40; } } } // LEVEL4 int darkCount = 0; for (int col = 0; col < moduleCount; col++) { for (int row = 0; row < moduleCount; row++) { if (qrCode.IsDark(row, col)) { darkCount++; } } } double ratio = Math.Abs(100.0 * darkCount / moduleCount / moduleCount - 50) / 5; lostPoint += ratio * 10; return lostPoint; } internal static int GetBCHTypeInfo(int data) { int d = (data << 10); int s = 0; while ((s = (int)(QRUtil.GetBCHDigit(d) - QRUtil.GetBCHDigit(QRUtil.G15))) >= 0) { d ^= (Convert.ToInt32(QRUtil.G15) << s); } return (int)((data << 10) | d) ^ QRUtil.G15_MASK; } internal static int GetBCHTypeNumber(int data) { int d = data << 12; while (QRUtil.GetBCHDigit(d) - QRUtil.GetBCHDigit(QRUtil.G18) >= 0) { d ^= (QRUtil.G18 << (QRUtil.GetBCHDigit(d) - QRUtil.GetBCHDigit(QRUtil.G18))); } return (data << 12) | d; } internal static int GetBCHDigit(int dataInt) { int digit = 0; uint data = Convert.ToUInt32(dataInt); while (data != 0) { digit++; data >>= 1; } return digit; } internal static int[] GetPatternPosition(int typeNumber) { return QRUtil.PATTERN_POSITION_TABLE[typeNumber - 1]; } internal static bool GetMask(QRMaskPattern maskPattern, int i, int j) { switch (maskPattern) { case QRMaskPattern.PATTERN000: return (i + j) % 2 == 0; case QRMaskPattern.PATTERN001: return i % 2 == 0; case QRMaskPattern.PATTERN010: return j % 3 == 0; case QRMaskPattern.PATTERN011: return (i + j) % 3 == 0; case QRMaskPattern.PATTERN100: return ((i / 2) + (j / 3)) % 2 == 0; case QRMaskPattern.PATTERN101: return (i * j) % 2 + (i * j) % 3 == 0; case QRMaskPattern.PATTERN110: return ((i * j) % 2 + (i * j) % 3) % 2 == 0; case QRMaskPattern.PATTERN111: return ((i * j) % 3 + (i + j) % 2) % 2 == 0; default: throw new Error("bad maskPattern:" + maskPattern); } } internal static QRPolynomial GetErrorCorrectPolynomial(int errorCorrectLength) { QRPolynomial a = new QRPolynomial(new DataCache() { 1 }, 0); for (int i = 0; i < errorCorrectLength; i++) { a = a.Multiply(new QRPolynomial(new DataCache() { 1, QRMath.GExp(i) }, 0)); } return a; } } internal struct QRPolynomial { private int[] m_num; public QRPolynomial(DataCache num, int shift) : this() { if (num == null) { throw new Error(); } int offset = 0; while (offset < num.Count && num[offset] == 0) { offset++; } this.m_num = new int[num.Count - offset + shift]; for (int i = 0; i < num.Count - offset; i++) { this.m_num = num[(int)(i + offset)]; } } public int Get(int index) { return this.m_num[(int)index]; } public int GetLength() { return (int)this.m_num.Length; } public QRPolynomial Multiply(QRPolynomial e) { var num = new DataCache(this.GetLength() + e.GetLength() - 1); for (int i = 0; i < this.GetLength(); i++) { for (int j = 0; j < e.GetLength(); j++) { num ^= QRMath.GExp(QRMath.GLog(this.Get(i)) + QRMath.GLog(e.Get(j))); } } return new QRPolynomial(num, 0); } public QRPolynomial Mod(QRPolynomial e) { if (Convert.ToInt64(this.GetLength()) - Convert.ToInt64(e.GetLength()) < 0) { return this; } int ratio = QRMath.GLog(this.Get(0)) - QRMath.GLog(e.Get(0)); var num = new DataCache(this.GetLength()); for (int i = 0; i < this.GetLength(); i++) { num = this.Get(i); } for (int i = 0; i < e.GetLength(); i++) { num ^= QRMath.GExp(QRMath.GLog(e.Get(i)) + ratio); } // recursive call return new QRPolynomial(num, 0).Mod(e); } } internal static class QRMath { private static readonly int[] EXP_TABLE; private static readonly int[] LOG_TABLE; static QRMath() { EXP_TABLE = new int[256]; LOG_TABLE = new int[256]; for (int i = 0; i < 8; i++) { QRMath.EXP_TABLE = (int)(1 << (int)i); } for (int i = 8; i < 256; i++) { QRMath.EXP_TABLE = QRMath.EXP_TABLE ^ QRMath.EXP_TABLE ^ QRMath.EXP_TABLE ^ QRMath.EXP_TABLE; } for (int i = 0; i < 255; i++) { QRMath.LOG_TABLE[QRMath.EXP_TABLE] = i; } } internal static int GLog(int n) { if (n < 1) { throw new Error("glog(" + n + ")"); } return QRMath.LOG_TABLE[n]; } internal static int GExp(int n) { while (n < 0) { n += 255; } while (n >= 256) { n -= 255; } return QRMath.EXP_TABLE[n]; } } public struct QR8bitByte { public QRMode Mode { get; private set; } private string m_data { get; set; } public QR8bitByte(string data) : this() { m_data = data; Mode = QRMode.MODE_8BIT_BYTE; } public int Length { get { return m_data.Length; } } public void Write(QRBitBuffer buffer) { for (int i = 0; i < m_data.Length; ++i) { //item buffer.Put(m_data, 8); } ///buffer = Data; } } internal class DataCache : List<int> { public DataCache(int capacity) : base() { for (int i = 0; i < capacity; i++) { base.Add(0); } } public DataCache() : base() { } } internal struct QRRSBlock { private static readonly int[][] RS_BLOCK_TABLE = new int[][] { // L // M // Q // H // 1 new int [] {1, 26, 19}, new int [] {1, 26, 16}, new int [] {1, 26, 13}, new int [] {1, 26, 9}, // 2 new int [] {1, 44, 34}, new int [] {1, 44, 28}, new int [] {1, 44, 22}, new int [] {1, 44, 16}, // 3 new int [] {1, 70, 55}, new int [] {1, 70, 44}, new int [] {2, 35, 17}, new int [] {2, 35, 13}, // 4 new int [] {1, 100, 80}, new int [] {2, 50, 32}, new int [] {2, 50, 24}, new int [] {4, 25, 9}, // 5 new int [] {1, 134, 108}, new int [] {2, 67, 43}, new int [] {2, 33, 15, 2, 34, 16}, new int [] {2, 33, 11, 2, 34, 12}, // 6 new int [] {2, 86, 68}, new int [] {4, 43, 27}, new int [] {4, 43, 19}, new int [] {4, 43, 15}, // 7 new int [] {2, 98, 78}, new int [] {4, 49, 31}, new int [] {2, 32, 14, 4, 33, 15}, new int [] {4, 39, 13, 1, 40, 14}, // 8 new int [] {2, 121, 97}, new int [] {2, 60, 38, 2, 61, 39}, new int [] {4, 40, 18, 2, 41, 19}, new int [] {4, 40, 14, 2, 41, 15}, // 9 new int [] {2, 146, 116}, new int [] {3, 58, 36, 2, 59, 37}, new int [] {4, 36, 16, 4, 37, 17}, new int [] {4, 36, 12, 4, 37, 13}, // 10 new int [] {2, 86, 68, 2, 87, 69}, new int [] {4, 69, 43, 1, 70, 44}, new int [] {6, 43, 19, 2, 44, 20}, new int [] {6, 43, 15, 2, 44, 16} }; public int DataCount { get; private set; } public int TotalCount { get; set; } public QRRSBlock(int totalCount, int dataCount) : this() { TotalCount = totalCount; DataCount = dataCount; } public static List<QRRSBlock> GetRSBlocks(int typeNumber, QRErrorCorrectLevel errorCorrectLevel) { int[] rsBlock = GetRsBlockTable(typeNumber, errorCorrectLevel); if (rsBlock == null) { throw new Error("bad rs block @ typeNumber:" + typeNumber + "/errorCorrectLevel:" + errorCorrectLevel); } int length = (int)rsBlock.Length / 3; var list = new List<QRRSBlock>(); for (int i = 0; i < length; i++) { int count = rsBlock; int totalCount = rsBlock; int dataCount = rsBlock; for (int j = 0; j < count; j++) { list.Add(new QRRSBlock(totalCount, dataCount)); } } return list; } private static int[] GetRsBlockTable(int typeNumber, QRErrorCorrectLevel errorCorrectLevel) { switch (errorCorrectLevel) { case QRErrorCorrectLevel.L: return QRRSBlock.RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 0]; case QRErrorCorrectLevel.M: return QRRSBlock.RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 1]; case QRErrorCorrectLevel.Q: return QRRSBlock.RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 2]; case QRErrorCorrectLevel.H: return QRRSBlock.RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 3]; default: return null; } } } public class QRCode { private const int PAD0 = 0xEC; private const int PAD1 = 0x11; private List<QR8bitByte> m_dataList = new List<QR8bitByte>(); private int m_typeNumber; private DataCache m_dataCache; private int m_moduleCount; private bool?[][] m_modules; private QRErrorCorrectLevel m_errorCorrectLevel; public QRCode(Options options) : this(options.TypeNumber, options.CorrectLevel) { AddData(options.Text); } public QRCode(int typeNumber, QRErrorCorrectLevel level) { m_typeNumber = typeNumber; m_errorCorrectLevel = level; m_dataCache = null; } public void AddData(string data) { m_dataCache = null; m_dataList.Add(new QR8bitByte(data)); } public void Make() { MakeImpl(false, GetBestMaskPattern()); } private QRMaskPattern GetBestMaskPattern() { double minLostPoint = 0; QRMaskPattern pattern = 0; for (int i = 0; i < 8; i++) { this.MakeImpl(true, (QRMaskPattern)i); double lostPoint = QRUtil.GetLostPoint(this); if (i == 0 || minLostPoint > lostPoint) { minLostPoint = lostPoint; pattern = (QRMaskPattern)i; } } return pattern; } private void MakeImpl(bool test, QRMaskPattern maskPattern) { m_moduleCount = this.m_typeNumber * 4 + 17; m_modules = new bool?[m_moduleCount][]; for (int row = 0; row < m_moduleCount; row++) { m_modules[row] = new bool?[(m_moduleCount)]; for (var col = 0; col < m_moduleCount; col++) { m_modules[row][col] = null; //(col + row) % 3; } } this.SetupPositionProbePattern(0, 0); this.SetupPositionProbePattern(m_moduleCount - 7, 0); this.SetupPositionProbePattern(0, m_moduleCount - 7); this.SetupPositionAdjustPattern(); this.SetupTimingPattern(); this.setupTypeInfo(test, maskPattern); if (m_typeNumber >= 7) { this.setupTypeNumber(test); } if (this.m_dataCache == null) { this.m_dataCache = CreateData(this.m_typeNumber, this.m_errorCorrectLevel, this.m_dataList); } MapData(this.m_dataCache, maskPattern); } public bool IsDark(int row, int col) { return m_modules[(int)row][(int)col].Value; } private void SetupTimingPattern() { for (var r = 8; r < this.m_moduleCount - 8; r++) { if (this.m_modules[r][6] != null) { continue; } this.m_modules[r][6] = (r % 2 == 0); } for (var c = 8; c < this.m_moduleCount - 8; c++) { if (this.m_modules[6][c] != null) { continue; } this.m_modules[6][c] = (c % 2 == 0); } } private void setupTypeNumber(bool test) { var bits = QRUtil.GetBCHTypeNumber(m_typeNumber); for (var i = 0; i < 18; i++) { var mod = (!test && ((bits >> i) & 1) == 1); this.m_modules[(int)(i / 3)] = mod; } for (var i = 0; i < 18; i++) { var mod = (!test && ((bits >> i) & 1) == 1); this.m_modules[(int)(i / 3)] = mod; } } private void SetupPositionAdjustPattern() { var pos = QRUtil.GetPatternPosition(m_typeNumber); for (var i = 0; i < pos.Length; i++) { for (var j = 0; j < pos.Length; j++) { var row = pos; var col = pos[j]; if (this.m_modules[row][col] != null) { continue; } for (var r = -2; r <= 2; r++) { for (var c = -2; c <= 2; c++) { if (r == -2 || r == 2 || c == -2 || c == 2 || (r == 0 && c == 0)) { this.m_modules[row + r][col + c] = true; } else { this.m_modules[row + r][col + c] = false; } } } } } } private void setupTypeInfo(bool test, QRMaskPattern maskPattern) { var data = ((int)this.m_errorCorrectLevel << 3) | (int)maskPattern; var bits = QRUtil.GetBCHTypeInfo(data); // vertical for (var i = 0; i < 15; i++) { var mod = (!test && ((bits >> i) & 1) == 1); if (i < 6) { this.m_modules[8] = mod; } else if (i < 8) { this.m_modules[8] = mod; } else { this.m_modules[this.m_moduleCount - 15 + i][8] = mod; } } // horizontal for (var i = 0; i < 15; i++) { var mod = (!test && ((bits >> i) & 1) == 1); if (i < 8) { this.m_modules[8][this.m_moduleCount - i - 1] = mod; } else if (i < 9) { this.m_modules[8][15 - i - 1 + 1] = mod; } else { this.m_modules[8][15 - i - 1] = mod; } } // fixed module this.m_modules[this.m_moduleCount - 8][8] = (!test); } private void MapData(DataCache data, QRMaskPattern maskPattern) { int inc = -1; int row = (int)this.m_moduleCount - 1; int bitIndex = 7; int byteIndex = 0; for (var col = this.m_moduleCount - 1; col > 0; col -= 2) { if (col == 6) col--; while (true) { for (int c = 0; c < 2; c++) { if (this.m_modules[row][col - c] == null) { bool dark = false; if (byteIndex < data.Count) { dark = (((Convert.ToUInt32(data[byteIndex]) >> bitIndex) & 1) == 1); } bool mask = QRUtil.GetMask(maskPattern, (int)row, col - c); if (mask) { dark = !dark; } this.m_modules[row][col - c] = dark; bitIndex--; if (bitIndex == -1) { byteIndex++; bitIndex = 7; } } } row += inc; if (row < 0 || this.m_moduleCount <= row) { row -= inc; inc = -inc; break; } } } } private DataCache CreateData(int typeNumber, QRErrorCorrectLevel errorCorrectLevel, List<QR8bitByte> dataList) { List<QRRSBlock> rsBlocks = QRRSBlock.GetRSBlocks(typeNumber, errorCorrectLevel); var buffer = new QRBitBuffer(); for (int i = 0; i < dataList.Count; i++) { QR8bitByte data = dataList; buffer.Put((int)data.Mode, 4); buffer.Put(data.Length, QRUtil.GetLengthInBits(data.Mode, typeNumber)); data.Write(buffer); } // calc num max data. int totalDataCount = 0; for (var i = 0; i < rsBlocks.Count; i++) { totalDataCount += rsBlocks.DataCount; } if (buffer.GetLengthInBits() > totalDataCount * 8) { throw new Error("code length overflow. (" + buffer.GetLengthInBits() + ">" + totalDataCount * 8 + ")"); } // end code if (buffer.GetLengthInBits() + 4 <= totalDataCount * 8) { buffer.Put(0, 4); } // padding while (buffer.GetLengthInBits() % 8 != 0) { buffer.PutBit(false); } // padding while (true) { if (buffer.GetLengthInBits() >= totalDataCount * 8) { break; } buffer.Put(QRCode.PAD0, 8); if (buffer.GetLengthInBits() >= totalDataCount * 8) { break; } buffer.Put(QRCode.PAD1, 8); } return CreateBytes(buffer, rsBlocks); } private DataCache CreateBytes(QRBitBuffer buffer, List<QRRSBlock> rsBlocks) { int offset = 0; int maxDcCount = 0; int maxEcCount = 0; var dcdata = new DataCache[(rsBlocks.Count)]; var ecdata = new DataCache[(rsBlocks.Count)]; for (int r = 0; r < rsBlocks.Count; r++) { int dcCount = rsBlocks[(int)r].DataCount; int ecCount = rsBlocks[(int)r].TotalCount - dcCount; maxDcCount = Math.Max(maxDcCount, dcCount); maxEcCount = Math.Max(maxEcCount, ecCount); dcdata[r] = new DataCache(dcCount); for (int i = 0; i < dcdata[r].Count; i++) { dcdata[r] = 0xff & buffer.m_buffer[(int)(i + offset)]; } offset += dcCount; QRPolynomial rsPoly = QRUtil.GetErrorCorrectPolynomial(ecCount); QRPolynomial rawPoly = new QRPolynomial(dcdata[r], rsPoly.GetLength() - 1); var modPoly = rawPoly.Mod(rsPoly); ecdata[r] = new DataCache(rsPoly.GetLength() - 1); for (int i = 0; i < ecdata[r].Count; i++) { int modIndex = i + modPoly.GetLength() - (int)ecdata[r].Count; ecdata[r] = (modIndex >= 0) ? modPoly.Get(modIndex) : 0; } } int totalCodeCount = 0; for (int i = 0; i < rsBlocks.Count; i++) { totalCodeCount += rsBlocks[(int)i].TotalCount; } var data = new DataCache(totalCodeCount); int index = 0; for (int i = 0; i < maxDcCount; i++) { for (int r = 0; r < rsBlocks.Count; r++) { if (i < dcdata[r].Count) { data[index++] = dcdata[r]; } } } for (int i = 0; i < maxEcCount; i++) { for (int r = 0; r < rsBlocks.Count; r++) { if (i < ecdata[r].Count) { data[index++] = ecdata[r]; } } } return data; } private void SetupPositionProbePattern(int row, int col) { for (int r = -1; r <= 7; r++) { if (row + r <= -1 || this.m_moduleCount <= row + r) continue; for (int c = -1; c <= 7; c++) { if (col + c <= -1 || this.m_moduleCount <= col + c) continue; if ((0 <= r && r <= 6 && (c == 0 || c == 6)) || (0 <= c && c <= 6 && (r == 0 || r == 6)) || (2 <= r && r <= 4 && 2 <= c && c <= 4)) { this.m_modules[row + r][col + c] = true; } else { this.m_modules[row + r][col + c] = false; } } } } public int GetModuleCount() { return this.m_moduleCount; } internal int getBestMaskPattern() { double minLostPoint = 0; int pattern = 0; for (int i = 0; i < 8; i++) { this.MakeImpl(true, (QRMaskPattern)i); double lostPoint = QRUtil.GetLostPoint(this); if (i == 0 || minLostPoint > lostPoint) { minLostPoint = lostPoint; pattern = i; } } return pattern; } } public class QRBitBuffer { internal List<int> m_buffer = new List<int>(); private int m_length = 0; public bool Get(int index) { int bufIndex = Convert.ToInt32((index / 8)); return ((Convert.ToUInt32(this.m_buffer[bufIndex]) >> (7 - index % 8)) & 1) == 1; } public void Put(int num, int length) { for (var i = 0; i < length; i++) { this.PutBit(((Convert.ToUInt32(num) >> (length - i - 1)) & 1) == 1); } } public int GetLengthInBits() { return m_length; } public void PutBit(bool bit) { int bufIndex = (int)(this.m_length / 8); if (this.m_buffer.Count <= bufIndex) { this.m_buffer.Add(0); } if (bit) { this.m_buffer[bufIndex] |= (int)(Convert.ToUInt32(0x80) >> (this.m_length % 8)); } this.m_length++; } } }
Text Box 1: Corrected QRCode4CS class library source code
3. Create the QRCode4CS class library DLL
Open a new project in Microsoft Visual C# 2010 Express (downloadable at http://go.microsoft.com/?linkid=9709939), select the "Class Library" template, name it "QRCode4CSClassLibrary," then click the "OK" button.
The Class1.cs text editor window is displayed.
Paste the corrected QRCode4CS source code from text box #1 over the contents of the Class1.cs code file.
Build the class library DLL by selecting "Build-Build Solution" from the main menu.
The "Build succeeded" message in the lower left-hand corner of the IDE confirms that the DLL has been created.
Select "File-Save All" from the main menu.
Click the "Save" button.
Select "File-Close Solution" to complete creation of QRCode4CSClassLibrary.
The QRCodeCSClassLibrary.dll assembly has now been created under the QRCodeCSClassLibrary project folder.
Create the QRCode4CS image generator
Open a new project in Microsoft Visual C# 2010 Express, select the "Windows Form Application" template, name it "QRCode4CSImageGenerator," then click the "OK" button.
Browse to the "Form1.Designer.cs" file in the "Solution Explorer" window and double-click on it.
The Form1.Designer.cs code editor window is displayed.
Copy and past the following code over the code in the Form1.Designer.cs code editor window.
namespace QRCode4CSImageGenerator { partial class Form1 { /// <summary> /// Required designer variable. /// </summary> private System.ComponentModel.IContainer components = null; /// <summary> /// Clean up any resources being used. /// </summary> /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param> protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Windows Form Designer generated code /// <summary> /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// </summary> private void InitializeComponent() { this.button1 = new System.Windows.Forms.Button(); this.textBox1 = new System.Windows.Forms.TextBox(); this.button2 = new System.Windows.Forms.Button(); this.button3 = new System.Windows.Forms.Button(); this.label2 = new System.Windows.Forms.Label(); this.pictureBox1 = new System.Windows.Forms.PictureBox(); this.label3 = new System.Windows.Forms.Label(); this.button4 = new System.Windows.Forms.Button(); ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit(); this.SuspendLayout(); // // button1 // this.button1.Location = new System.Drawing.Point(178, 40); this.button1.Name = "button1"; this.button1.Size = new System.Drawing.Size(102, 23); this.button1.TabIndex = 0; this.button1.Text = "Create QR Code"; this.button1.UseVisualStyleBackColor = true; this.button1.Click += new System.EventHandler(this.button1_Click); // // textBox1 // this.textBox1.Location = new System.Drawing.Point(12, 10); this.textBox1.Name = "textBox1"; this.textBox1.Size = new System.Drawing.Size(279, 20); this.textBox1.TabIndex = 0; this.textBox1.TextChanged += new System.EventHandler(this.textBox1_TextChanged); // // button2 // this.button2.Location = new System.Drawing.Point(178, 127); this.button2.Name = "button2"; this.button2.Size = new System.Drawing.Size(102, 23); this.button2.TabIndex = 2; this.button2.Text = "Exit"; this.button2.UseVisualStyleBackColor = true; this.button2.Click += new System.EventHandler(this.button2_Click); // // button3 // this.button3.Location = new System.Drawing.Point(178, 69); this.button3.Name = "button3"; this.button3.Size = new System.Drawing.Size(102, 23); this.button3.TabIndex = 2; this.button3.Text = "Clear QR Code"; this.button3.UseVisualStyleBackColor = true; this.button3.Click += new System.EventHandler(this.button3_Click); // // label2 // this.label2.AutoSize = true; this.label2.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.label2.Location = new System.Drawing.Point(13, 160); this.label2.Name = "label2"; this.label2.Size = new System.Drawing.Size(0, 13); this.label2.TabIndex = 6; // // pictureBox1 // this.pictureBox1.Location = new System.Drawing.Point(30, 60); this.pictureBox1.Name = "pictureBox1"; this.pictureBox1.Size = new System.Drawing.Size(86, 86); this.pictureBox1.TabIndex = 7; this.pictureBox1.TabStop = false; // // label3 // this.label3.AutoSize = true; this.label3.Location = new System.Drawing.Point(43, 180); this.label3.Name = "label3"; this.label3.Size = new System.Drawing.Size(0, 13); this.label3.TabIndex = 8; // // button4 // this.button4.Location = new System.Drawing.Point(178, 98); this.button4.Name = "button4"; this.button4.Size = new System.Drawing.Size(102, 23); this.button4.TabIndex = 9; this.button4.Text = "Save QR Code"; this.button4.UseVisualStyleBackColor = true; this.button4.Click += new System.EventHandler(this.button4_Click); // // Form1 // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.ClientSize = new System.Drawing.Size(303, 206); this.Controls.Add(this.button4); this.Controls.Add(this.label3); this.Controls.Add(this.pictureBox1); this.Controls.Add(this.label2); this.Controls.Add(this.button3); this.Controls.Add(this.button2); this.Controls.Add(this.textBox1); this.Controls.Add(this.button1); this.Name = "Form1"; this.Text = "QR Code Generator"; this.Load += new System.EventHandler(this.Form1_Load); ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).EndInit(); this.ResumeLayout(false); this.PerformLayout(); } #endregion private System.Windows.Forms.Button button1; private System.Windows.Forms.TextBox textBox1; private System.Windows.Forms.Button button2; private System.Windows.Forms.Button button3; private System.Windows.Forms.Label label2; private System.Windows.Forms.PictureBox pictureBox1; private System.Windows.Forms.Label label3; private System.Windows.Forms.Button button4; } }
Text Box 2: C# code to be pasted into the Form1.Designer.cs editor window
After the C# code has been pasted into "Form1.Designer.cs," click on the "Form1.cs [Design]" editor tab.
Right click on the background of the "Form1.cs [Design]" window and select "View Code" from the popup menu.
The Form1.cs editor window is displayed.
Paste the following code over the code in the Form1.cs code editor window.
using System; using System.IO; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; using QRCode4CS; namespace QRCode4CSImageGenerator { public partial class Form1 : Form { public Form1() { InitializeComponent(); Image canvas = new Bitmap(86, 86); Graphics artist = Graphics.FromImage(canvas); artist.Clear(Color.White); pictureBox1.Image = canvas; } private void button1_Click(object sender, EventArgs e) { label3.Text = ""; if (textBox1.Text.Length > 34) { textBox1.Text = textBox1.Text.Substring(0, 34); label3.Text = "String truncated to 34 character length"; } label2.Text = textBox1.Text; pictureBox1.Image = CreateQRCode(textBox1.Text); } private void button2_Click(object sender, EventArgs e) { this.Close(); } private void textBox1_TextChanged(object sender, EventArgs e) { //this.Text = textBox1.Text; } private void button3_Click(object sender, EventArgs e) { textBox1.Text = ""; label2.Text = ""; label3.Text = ""; Image canvas = new Bitmap(86, 86); Graphics artist = Graphics.FromImage(canvas); artist.Clear(Color.White); pictureBox1.Image = canvas; } private void Form1_Load(object sender, EventArgs e) { } private void button4_Click(object sender, EventArgs e) { SaveFileDialog save = new SaveFileDialog(); //save.Filter = "Bitmap files (*.bmp)|*.bmp|All files (*.*)|*.*"; save.Filter = "Bitmap files (*.bmp)|*.bmp|JPG files (*.jpg)|*.jpg|GIF files (*.gif)|*.gif|PNG files (*.png)|*.png|All files (*.*)|*.*"; save.FilterIndex = 4; save.RestoreDirectory = true; if (save.ShowDialog() == DialogResult.OK) { pictureBox1.Image.Save(save.FileName); } } public Image CreateQRCode(string inputString) { QRCode4CS.QRCode qrcode = new QRCode4CS.QRCode(new QRCode4CS.Options(inputString)); qrcode.Make(); Image canvas = new Bitmap(86, 86); Graphics artist = Graphics.FromImage(canvas); artist.Clear(Color.White); for (int row = 0; row < qrcode.GetModuleCount(); row++) { for (int col = 0; col < qrcode.GetModuleCount(); col++) { bool isDark = qrcode.IsDark(row, col); if (isDark == true) { artist.FillRectangle(Brushes.Black, 2 * row + 10, 2 * col + 10, 2 * row + 15, 2 * col + 15); } else { artist.FillRectangle(Brushes.White, 2 * row + 10, 2 * col + 10, 2 * row + 15, 2 * col + 15); } } } artist.FillRectangle(Brushes.White, 0, 76, 86, 86); artist.FillRectangle(Brushes.White, 76, 0, 86, 86); pictureBox1.Image = canvas; artist.Dispose(); return canvas; } } }
Text Box 3: C# code to be pasted into the Form1.cs editor window
After the C# code has been pasted into "Form1.cs," right-click on the "References" folder in the "Solution Explorer" window.
Choose "Add Reference" from the popup menu.
Browse to the "QRCode4CSClassLibrary.dll" created in step number 3 above and click the "OK" button.
Add the System.Drawing namespace to the references.
Add the System.Windows.Forms namespace to the references.
QRCode4CSClassLibrary, System.Drawing and System.Windows.Forms have now been added to the "References" folder.
The "using" statements for QRCode4CSClassLibrary, System.Drawing and System.Windows.Forms were already in the code pasted from text box number 3.
Select "File-Save All" from the top menu.
Click the "Save" button to save the QRCode4CSImageGenerator project.
Run the QR code image generator and save a QR code image file
Click the "Start Debugging" button to run the QR code generator.
The "QR Code Generator" Windows form application pops up.
Enter a string into the text box at the top of the form and click the "Create QR Code" button.
A QR code image is created.
The QRCode4CS algorithm can only handle string up to 34 characters in length. Longer strings will cause the code to crash, so longer strings are simply truncated in this demo application.
Now click the "Save QR Code" button, browse to the folder where you want to save the QR Code image, enter the desired file name, then click the "Save" button.
Open the newly-created image file in an image-viewer program to confirm its content.
Upload the QR code image file to an online QR code reader
Next we will upload the image file to an online QR code decoder application located at: http://zxing.org/w/decode.jspx
Browse to the test.png file created in step number 5 above and click the "Open" button.
Click the lower "Submit Query" button.
Confirm that the online decoder successfully decodes the QR code image.
Create a signed QRCode4CS CLR asssembly with the "AllowPartiallyTrustedCallers" attribute
Open the QRCode4CSClassLibrary solution created in step number 3 above, right-click on the References folder in the Solution Explorer, select "Add Reference..." from the popup menu, browse to and select the "System.Security" .NET namespace, then click the "OK" button.
After adding the "System.Security" reference, double-click on the AssemblyInfo.cs file, then add the "using System.Security;" statment and the [assembly: AllowPartiallyTrusted Callers] attribute statement.
Double-click on the "Properties" folder and change the assembly name to "QRCode4CS," the default namespace to "QRCode4CS, and the target framework to ".NET Framework 3.5."
Click on the "Signing" tab, check the "Sign the assembly" checkbox, select "New" from the "Choose a strong name key file" dropdown list, enter the key file name "QRCode4CSClassLibrary.snk," uncheck te "Protect my key file with a password" checkbox, then click the "OK" button.
Select "Build-Rebuild Solution" from the main menu to create the QRCode4CS signed assembly.
Create a signed CLR assembly with the "AllowPartiallyTrustedCallers" attribute that references the QRCode4CS assembly to generate a QR code byte array
Create a new C# Class Library project named CreateQRCodeReturnByteArray
A template Class1.cs file is generated.
Paste the following C# code over the template code in the Class1.cs file.
namespace CreateQRCodeReturnByteArray { using System; using System.IO; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; using QRCode4CS; public class CreateQRCode { public static Byte[] ReturnByteArray(string inputString, string imageSize) { QRCode4CS.QRCode qrcode = new QRCode4CS.QRCode(new QRCode4CS.Options(inputString)); qrcode.Make(); byte[] imagedata = null; if (imageSize == "L") { Image canvas = new Bitmap(86, 86); Graphics artist = Graphics.FromImage(canvas); artist.Clear(Color.White); for (int row = 0; row < qrcode.GetModuleCount(); row++) { for (int col = 0; col < qrcode.GetModuleCount(); col++) { bool isDark = qrcode.IsDark(row, col); if (isDark == true) { artist.FillRectangle(Brushes.Black, 2 * row + 10, 2 * col + 10, 2 * row + 15, 2 * col + 15); } else { artist.FillRectangle(Brushes.White, 2 * row + 10, 2 * col + 10, 2 * row + 15, 2 * col + 15); } } } artist.FillRectangle(Brushes.White, 0, 76, 86, 86); artist.FillRectangle(Brushes.White, 76, 0, 86, 86); artist.Dispose(); System.IO.MemoryStream ms = new System.IO.MemoryStream(); canvas.Save(ms, System.Drawing.Imaging.ImageFormat.Png); imagedata = ms.GetBuffer(); } else { Image canvas = new Bitmap(43, 43); Graphics artist = Graphics.FromImage(canvas); artist.Clear(Color.White); for (int row = 0; row < qrcode.GetModuleCount(); row++) { for (int col = 0; col < qrcode.GetModuleCount(); col++) { bool isDark = qrcode.IsDark(row, col); if (isDark == true) { artist.FillRectangle(Brushes.Black, row + 5, col + 5, row + 5, col + 5); } else { artist.FillRectangle(Brushes.White, row + 5, col + 5, row + 5, col + 5); } } } artist.FillRectangle(Brushes.White, 0, 38, 43, 43); artist.FillRectangle(Brushes.White, 38, 0, 43, 43); artist.Dispose(); System.IO.MemoryStream ms = new System.IO.MemoryStream(); canvas.Save(ms, System.Drawing.Imaging.ImageFormat.Png); imagedata = ms.GetBuffer(); } return imagedata; } } }
Text box 4: CreateQRCodeReturnByteArray class library code
The "using" statements with red warning squiggles indicate namespaces that need to be added to the project.
Double-click the "References" folder to bring up the "Add Reference" dialog box. Click the "Browse" tab on browse to the "QRCode4CSClassLibrary.dll" assembly generated in step number 7.
Add references to "System.Drawing" and "System.Windows.Forms" using the "Add Reference" dialog box, then delete the reference to "Microsoft.CSharp."
Double-click on the "Properties" folder, then change the target framework to ".NET Framework 3.5."
Click on the "Signing" tab and create a strong name key called "CreateQRCodeReturnByteArray.snk."
Select "Build-Rebuild Solution" from the main menu to create the signed CreateQRCodeReturnByteArray.dll assembly.
Deploy the CLR assemblies to the global assembly cache
Browse to the "bin" subdirectories of each project and copy the DLL files to a convenient location, such as "C:\DLL\."
Deploy each assembly with a command of the form "gacutil -i assembly.dll."
You can browse to the "C:\Windows\assembly\" directory to confirm that the "CreateQRCoderReturnByteArray" and...
..."QRCode4CSLibrary" assemblies have been successfully added to the global assembly cache (GAC).
Go to http://msdn.microsoft.com/en-us/library/vstudio/ex0ss12c%28v=vs.100%29.aspx for further information on the use of the gacutil.exe global assembly cache utility.
Use the CLR assemblies to generate QR Code barcodes in SSRS
In this step we are going to:
- Create a table and populate it with test data that can be used to generate QR codes
- Create a default SSRS report that queries the test data table
- Add references to the CreateQRCodeReturnByteArray, System.Drawing and System.Windows.Forms assemblies
- Add a custom code function that references the CreateQRCodeReturnByteArray assembly to return a byte array to an SSRS report image object
- Add two fields to the default SSRS report table
- Add a background image to one of the new fields that references the custom code to generate a small QR code image
- Add an image object to the other new field that references the custom code to generate a large QR code image
- Run the report to generate QR codes
Run this script in SQL Server Managment Studio to create and populate a table named TestData.
BEGIN TRY DROP TABLE TestData END TRY BEGIN CATCH END CATCH GO CREATE TABLE [dbo].[TestData]( [City] [nvarchar](50) NULL, [Zip] [nvarchar](50) NULL ) ON [PRIMARY] GO INSERT INTO [dbo].[TestData] (City,Zip) VALUES ('Seattle','98101') INSERT INTO [dbo].[TestData] (City,Zip) VALUES ('Bangor','04402') INSERT INTO [dbo].[TestData] (City,Zip) VALUES ('Miami','33133') INSERT INTO [dbo].[TestData] (City,Zip) VALUES ('Houston','77001') INSERT INTO [dbo].[TestData] (City,Zip) VALUES ('Boston','02201') SELECT * FROM [TestDB].[dbo].[TestData]
Text Box 5: T-SQL code to generate and populate the test data table
Create a default SSRS report that queries the new table.
Open the report in "Design" mode.
Select "Report-Report Properties" from the main menu.
Click on the "References" tab and add a reference to the "CreateQRCodeReturnByteArray" assembly by browsing to the dll we committed to the GAC in step 9. Add references to the "System.Drawing" and "System.Windows.Forms" assemblies from the ".NET" tab of the "Add Reference" window.
Click on the "Code" tab of the "Report Properties" window and paste the following function...
Public Function QRCode(ByVal inputString As String, ByVal imageSize As String) as Byte() Return CreateQRCodeReturnByteArray.CreateQRCode.ReturnByteArray(inputString,imageSize) End Function
Text Box 6: Custom code for SSRS report
...into it.
Add two fields to the report table.
Click on the Small QR Code data field to give it the focus.
Browse to the "Background Image" property in the "Properties" window, expand it by clicking the "+" sign, then select or insert the attributes shown below.
Next, drage and drop an Image object into the "Large QR Code" data field.
This is what the Image object looks like before configuration.
Right-click on the empty image and select "Image Properties" menu item from the popup menu.
Configure the "General" image properties as shown below. Insert the "=Code.QRCode(Fields!Zip.Value,"L") expression into the "Use this field" attribute.
Click on the "Size" tab of the "Image Properties" window and select the "Original size" radio button.
Click the "Preview" tab to generate a report containing QR Code barcodes.
Conclusion
This article has demonstrated how to display two-dimensional QR Code barcodes in an SSRS report using the open source library QRCode4CS.
Resource files
The attached archive file SSRS_QRCode.zip contains the three Visual C# Express projects, the two signed DLL assemblies and their source class library files, the SQL statement used to generate the test table and data, and the SSRS RDL report file from the article.