In one of my current projects, I needed to render some barcodes. Google has a nice “Code 128” barcode font which makes rendering a barcode in a website pretty easy. However, in this particular application, I wound up needing to render the barcodes as images since the HTML that I render is fed into a PDF creator that doesn’t support font-face CSS stylings.
The System.Drawing namespace in C# is chock full of useful image manipulation and generation methods. One method in particular allows you to render a string to a bitmap. Using the external Google font is not entirely straight-forward. The particular project that I was working on is a website.
Long ago, I blogged about hosting Google fonts locally. The first thing I did was use this method to pull down the WOFF file for the barcode font. I used an online converter to convert it to TTF. Once I had the TTF, I added it to my Visual Studio solution and set it to Content type/Copy to output always.
In order to utilize an external font file, we must create a private font collection, load the file into the collection, specify the family we want to use, and then create a font from that family.
// Generate images for our barcodes since ABDPdf doesn't support font-face css PrivateFontCollection pfcoll = new PrivateFontCollection(); pfcoll.AddFontFile(HttpContext.Current.Server.MapPath("~/Resources/Libre_Barcode_128_Text-normal-400.ttf")); var fontFamily = pfcoll.Families.First(); var font = new Font(fontFamily, 44, FontStyle.Regular, GraphicsUnit.Pixel);
Pay special attention to my Font constructor and note that I am specify that units are pixels. If you don’t do this, then the GDI libraries will default to using the system’s DPI settings (usually 96 DPI). Your text will wind up looking like early era C-64 text in that case.
Once I have the font loaded, I’m iterating a list of “items” that contain a numeric string. I make the assumption that each character will be roughly 22 pixels wide, and thus calculate the appropriate width of the Bitmap/canvas onto which the barcode string will be drawn.
foreach (var item in items) { var text = item?.NumericString?.Trim() ?? "Unknown"; if (!printData.Barcodes.ContainsKey(text)) { var width = text.Length * 22; var height = 120; var rectf = new RectangleF(0, 0, width, height); var image = new Bitmap(width, height);
Once the canvas is configured, we need the Graphic object that will actually do all of the work. You can see below that I’m setting the quality of rendering, the alignment (which winds up right-justifying my text), and then using DrawString to render the text.
using (Graphics graphics = Graphics.FromImage(image)) { graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; graphics.TextRenderingHint = TextRenderingHint.ClearTypeGridFit; graphics.PixelOffsetMode = PixelOffsetMode.HighQuality; var strFormat = StringFormat.GenericTypographic; strFormat.Alignment = StringAlignment.Far; strFormat.LineAlignment = StringAlignment.Center; var blackBrush = new SolidBrush(Color.Black); var whiteBrush = new SolidBrush(Color.White); graphics.FillRectangle(whiteBrush, rectf); graphics.DrawString(text, font, blackBrush, rectf, strFormat); graphics.Flush();
The images that I render are converted to Base64 strings so that they can be embedded directly into HTML img sources. The barcodes are inserted into a Dictionary>string, string< and passed into the Razor View as a view model.
using (MemoryStream memStr = new MemoryStream()) { image.Save(memStr, ImageFormat.Png); byte[] imageBytes = memStr.ToArray(); string base64String = Convert.ToBase64String(imageBytes); dict.Add(text, base64String); } } } }
With that in place, the Razor view is pretty simple in its use of img tags. I pass in a boolean for when I want to display the original plain-text (using CSS font-face) or if I want to display the images. You can see below how the image src will use the data attribute to indicate the inline base64 string image.
<!DOCTYPE html> <html style="height: 100%;"> <head> <style> @if(!Model.UseBarcodeImages) { <text> .barcode { font-weight: 400; font-family: 'Libre Barcode 128 Text', cursive !important; font-size: 60px; } </text> } </style> </head> <body> <div class="col-xs-6" style="text-align:right;"> @if (Model.UseBarcodeImages) { <img alt="@item.NumericString" src="data:image/png;base64,@Model.Barcodes[item.NumericString]" /><br /> } else { <span class="barcode">@item.NumericString</span><br /> } </div> </body> </html>