Generate Barcodes in Powershell
Work has been pretty busy, and is likely to be for the foreseeable future! We’re remodeling our manufacturing area, adding a new lab on the 2nd floor (we never occupied the 2nd floor before!), adding 10 new wireless access points (VLANS, multiple SIDs, new wireless hardware platform, oh my!), implementing a new ERP system, planning for a new CRM system, rolling out new PC’s and laptops, new conference rooms, planning a remodel in our HQ building and a bunch of other things too! If you noticed I didn’t post anything last week, that’s why. Not to mention, nothing really interesting has come across my desk from Spiceworks or PowerGUI. Then the boss mentioned he was working on something, and the more he talked about it the more I realized I might be able to do what he wanted in Powershell, especially since this wasn’t going to be a one-off kind of thing, but something we’d have to do on a regular basis!
We need Barcodes, lots of Barcodes…
The driving force behind the wireless project I mentioned above was we were going to be implementing barcode reading all throughout the warehouse and manufacturing areas (in separate buildings just to make it more interesting). That meant we would need two different kinds of barcodes, one set to be attached to the shelves and then another to be attached to all of our bins, our thousands and thousands of bins. Yikes. The initial thought my boss had been to find a little plugin for Excel and then simply select all the fields, hit the Generate button and be done with it. He even found a program that would do just that! But there was a problem. When you hit Generate it didn’t put the barcode nice and neat to the right of the selected field. No, it plopped all of them along the top of the spreadsheet piled on top of each other! That would mean hours of work grabbing a picture, locating the text in the spreadsheet and dragging it down.
Of course, looking at this I couldn’t help but start thinking that this is screaming to be an automated process, right? Read the data, generate the barcode, output the data, over and over again. So I mentioned to him that I might be able to do something, assuming I could find a command line application that could generate a barcode. In my head I was thinking people have been making barcodes for years, surely someone has written something? I was even ok if we had to pay for it!
Zint
Break out my trusty web browser and go to Google and type in a search, and about 3 down from the top is a little free project called Zint (link here). Command line, generates barcodes, free, what more could you ask for? Download and install–has a nice little GUI, but I’m interested in the command line ability–navigate to the proper directory and give it a whirl. Ok, I did RTFM a little here.
WordPress is eating the preview something awful, but if you click on the link the picture loads fine and looks exactly like what I wanted. Now all I need to do is read the Excel spreadsheet, create a barcode and put it in the spreadsheet. How hard can that be?
How I hate ComObjects
ComObjects have been around for a long time, and their name implies they’re just like any other Powershell object. But, of course, they aren’t. They suck in every way possible and Office automation sucks even worse. While there’s plenty of documentation out there for doing simple processes simply expanding a cell and inserting a picture turned out to be pretty challenging. But after many test runs, and a lot of stubbornness I finally got something working. Here’s what it looked like:
$ZintPath = "C:\Program Files (x86)\Zint" $Excel = New-Object -ComObject Excel.Application $Excel.Visible = $True $Excel.Workbooks.Open("c:\utils\Barcodes.xls") | Out-Null
First define the to my Zint install, then create the Excel ComObject and open up the spreadsheet. I pipe that to Out-Null because I like to suppress output I don’t really need to see and the Office automation ComObjects are very chatty. You can also assign the statement to a variable to suppress the output if you want to.
ForEach ($Sheet in $Excel.Worksheets) { $Excel.Worksheets.Item($Sheet.Name).Activate() $Range = $Sheet.Range("F1") $Range.ColumnWidth = 35 $Sheet.Cells.RowHeight = 110 $NullCount = 0 $Row = 0 $ImageLocation = 10
In the test spreadsheet all of the barcodes were in several worksheets, so I decided to have the script loop through all of the worksheets. The Activate line isn’t strictly needed but I used it so I could see the sheet as it’s being worked on. $Sheet.Range is defining where I want to work in the worksheet. Then simply set some column width, row height (to fit our incoming picture). $NullCount is interesting because as I was writing this script it occurred to me that there’s no real end in Excel, so how do I know when I’m done? Just checking for a $null cell isn’t quite enough because there were blanks in the spreadsheet so I decided to use a counter called $NullCount. If I get to 3 $null cells in a row then I’ll consider the spreadsheet over–and, of course, there was one column of barcode data that had about 20 $null cells before it started, but I just cut and paste that column back up to the top and moved on! Potential blog post? Talking about having regular data? Mmm… sounds boring?!
OK, we have the Excel spreadsheet open and our column widened to our needs. Got some variables set on what row to start on ($Row) and where to place the image ($ImageLocation–nothing fancy here, I threw a number in there and then ran the script about 10 times until I got the image right where I wanted it). Let’s generate some barcodes!
While ($NullCount -le 3) { $Row ++ [string]$CellValue = $Sheet.Cells.Item($Row,1).Text If ([string]::IsNullOrEmpty($CellValue)) { $NullCount ++
This sets up a loop where our $NullCount is less than 3. Increment the row #, read the cell into a variable and test if it’s a null. If it is a null we increment our $NullCount and loop through again. If it’s NOT a null, we generate the barcode:
Else { $NullCount = 0 Set-Location $ZintPath .\zint.exe --data=$CellValue $Sheet.Shapes.AddPicture("$ZintPath\out.png",0,1,350,$ImageLocation,150,90) | Out-Null $ImageLocation = $ImageLocation + 110
Reset the $NullCount variable, change our current location to $ZintPath (why didn’t I just run $ZintPath\Zint.exe? Honestly, I just noticed this!), run Zint with our data, insert the picture into the spreadsheet and move our $ImageLocation down to the next cell. Worked like a million bucks! Except for one thing. $ImageLocation is a pixel count, and the size of the cell is a little bit less than 110 pixels–but not 109 pixels–so when I was scrolling down the spreadsheet I noticed the barcodes were drifting. And since the image isn’t really in the cell (though it looks like it) this was a problem. Obviously I was going to have to adjust $ImageLocation every now and then to keep the images lined up properly. But how to do it? I stared at this for a while, to be honest. I need to make a small adjustment about every 10 rows, which would be fairly easy to do by just keeping a counter and incrementing it as we go. But I felt this inelegant as I didn’t want to have to keep track of another variable all the time. Now what? I have the $Row counter all ready, I could test that for every 10th row, right? So any time $Row divided by 10 came out as a whole number make the adjustment!
If ($Row / 10 -is [int]) { $ImageLocation = $ImageLocation - 5 }
Since integers are always whole numbers I can test using “is” and check if the result of our division makes an integer then we’re on a 10th row. Adjust $ImageLocation up just a little. Had to do this check in the $true section of the NullCheck too because the script has to move down on null lines too.
Proof of Concept
Not the most elegant code I’ve ever written, I didn’t even close and save the document (just left it open and when I got my final run I just saved it using Excel), but it definitely proved that the work could be done using Powershell and with no cost upfront (beyond my time). Since it’s a script it can be redone an infinite number of times. At this point we broke out our barcode scanners and successfully scanned everything in.
Of course, as with most Proof of Concept projects, it’s not the final product by a long shot. Turns out our warehouse guy wants some labels to go on the sides of the shelves and others to go into little laminated pockets that snap onto our bins (thousands of bins) so two very different sizes and formats are needed. In the final code I ended up outputting to Word (and boy, was that fun) with two different scripts. But in the end I think we were able to output something very easily.
Now, it turns out another group wants a bunch of barcodes on a sheet or two of paper and they may end up changing those sheets periodically! So not only something ripe for automation but runnable by the user, oh my! Still don’t have a clear set of requirements on that project so not much more I can say at this point. If it turns into something interesting maybe I’ll write about it!
Rate this:
Share this:
Related
April 29, 2013 - Posted by Martin9700 | PowerShell | Barcodes, Excel, Word, Zint
11 Comments »
Leave a Reply Cancel reply
Blog Search
Blogroll
Links
Categories
- General (22)
- Humor (1)
- MS SQL (3)
- PowerShell (78)
- Powershell – Best Practices (6)
- Powershell – Getting Started (9)
- PowerShell – HTML Reporting (6)
- Powershell – Performance (12)
- Random Thoughts (8)
- Technical (17)
Top Posts & Pages
- Multithreading Powershell Scripts
- Generate Barcodes in Powershell
- Report All Snapshots in Your VMware Environment
- Using Powershell as a Telnet Client
- Getting Last Logon Information With PowerShell
- Discovery with Powershell - Script to Get Screenshots for VMs
- Getting Directory Information Fast
- Get File Counts and Size for All Extensions
Archives
- March 2021 (1)
- May 2019 (1)
- February 2019 (1)
- November 2016 (2)
- August 2016 (1)
- March 2016 (1)
- February 2016 (2)
- January 2016 (1)
- December 2015 (1)
- November 2015 (1)
- October 2015 (1)
- June 2015 (2)
- May 2015 (1)
- April 2015 (2)
- March 2015 (1)
- January 2015 (4)
- December 2014 (1)
- September 2014 (4)
- August 2014 (1)
- July 2014 (4)
- June 2014 (3)
- April 2014 (3)
- March 2014 (2)
- February 2014 (2)
- January 2014 (4)
- December 2013 (3)
- November 2013 (1)
- October 2013 (3)
- August 2013 (2)
- May 2013 (3)
- April 2013 (6)
- March 2013 (7)
- February 2013 (8)
- January 2013 (10)
- December 2012 (9)
- November 2012 (7)
- October 2012 (12)
- September 2012 (9)
- August 2012 (4)
- April 2011 (1)
- August 2010 (2)
- April 2010 (1)
- January 2010 (1)
- December 2009 (1)
- September 2009 (2)
- August 2009 (1)
- July 2009 (2)
Tag Cloud
ACL Active Directory Add-Member adsi Advanced Function alan rickman Background Jobs Best Practices ConvertTo-HTML Credentials csv Dfs dfs monitor dfs monitoring Discovery email employee directory Exchange Exchange Implicit Remoting Foreach ForEach-Object function Functions Get-ChildItem get-command get-help Get-Member GitHub Google Maps Google Visualizations Hashtable hbo html html report HTML Reports Import-SPWeb Invoke-SQLQuery Jobs JSON Meraki Modules monitoring mos def movie review Multi-thread multithread New User New User Automation object Pebble performance pipeline powergui PowerShell Powershell Get Started PowerShell Jobs PSObject Pushover regex Remoting Reporting RESTful ScriptProperty secure credentials Set-AlternatingRows Sharepoint spiceworks Spiceworld sql Strings Technical Veeam VMware WMI Write-Progress
Follow me on Twitter
My TweetsMe
Martin9700
Father, husband, IT Pro, scripter, blogger, cancer survivor. I tweet and blog about my IT passions, Powershell chief among them. Very active at Spiceworks.
Spiceworks
LinkedIn
RSS
-
Join 374 other subscribers
Thanks!
Would it be easier if you used a barcode font and put everything in an HTML table? That way you could just read in the Excel as a CSV file and not have to mess with COM objects at all.
There are barcode fonts?! NOW you tell me?!
http://www.barcodesinc.com/free-barcode-font/
On a more serious note, I still don’t have the clearest picture of how this project will ultimately play out so I was going with something that was the most portable. I did consider HTML using image files, but that required thousands of image files and the HTML, which isn’t too portable (read email). I never considered a barcode font, but that has an even worse problem in that if you don’t have the font installed it’s not going to work too well.
With Word, despite the great pain and suffering it imposed on me, I can save the final file and send it to the business unit and let THEM print the thing out, or do whatever they want to it.
I mentioned it in the post, but the second project (third?) is from another business unit and that may ultimately be HTML but again, the requirements are so vague right now I’m just not sure what the final outcome will be!
(forgot to mention) Pretty elegant solution you came up with.
Thanks!
Been reading over this one again, and realized that there’s more available info for working with Excel. Here’s an example that could help you in this project. So, you’ve opened an existing spreadsheet and activated one of the tabs as $Sheet. Then, $Sheet.UsedRange contains all the rows and columns that have something in them. Let’s call that $Used. $Used.Rows.Count is the number of rows in the sheet (which should help with the $nullcount issue) and $Used.Rows.Item(1) is the header row (Excel indexes begin with 1 instead of the normal PS 0).Likewise, $Used.Columns.Count is the number of columns (also indexed starting with 1).
Yes, so now I have to decide if I want to rework the script, or jab my eye with an ice pick… wait wait… don’t rush me….
Ha! Looking for coding a tool to generate barcodes from Powershell… and here I am 😀
You’re EVERYWHERE Martin!
Thanks for sharing this, it will be really useful, as barcode software are really a joke (licence price is a pain…) and my customer really needs it!
Right? And it all looks like it was designed in the 90’s. Glad I could help!