15

I would like to generate a 2d barcode (PDF417 or QR codes) in an Excel cell using macros. Just wondering is there any free alternatives to paid libraries to do this?

I know certain tools can do the job but it is relatively expensive to us.

Jeroen
  • 60,696
  • 40
  • 206
  • 339
user2306468
  • 157
  • 1
  • 1
  • 5
  • Pure VBA solutions seem difficult to find (lot of remote API call are easier to find). Here is a recent pick: http://code.google.com/p/barcode-vba-macro-only/ (just tested!) – Sébastien Ferry Mar 24 '14 at 13:22
  • Check out this guy's website. He implemented the QR code algorithm for 21x21 matrixes simply by using excel formula. Probably you can find an easy way to implement it into you xls-sheet: http://blog.ambor.com/2013/03/create-qr-codes-in-excel-or-any.html –  Oct 21 '13 at 07:58
  • Go here for QR code in Excel (VBA) http://stackoverflow.com/questions/5446421/encode-algorithm-qr-code – Johan Schoeman Dec 13 '14 at 06:08

2 Answers2

21

The VBA module barcode-vba-macro-only (mentioned by Sébastien Ferry in the comments) is a pure VBA 1D/2D code generator created by Jiri Gabriel under MIT License in 2013.

The code isn't completely simple to understand, but many comments have been translated from Czech to English in the version linked above.

To use it in a worksheet, just copy or import barcody.bas into your VBA in a module. In a worksheet, put in the function like this:

=EncodeBarcode(CELL("SHEET"),CELL("ADDRESS"),A2,51,1,0,2)

The usage is as follows:

  1. Leave the CELL("SHEET) and CELL("ADDRESS") as they are since it's just giving reference to the worksheet and cell address you have the formula
    • A2 is the cell that you have your string to be encoded. In my case it's cell A2 You can pass "Text" with quotes to do the same. Having the cell makes it more dynamic
    • 51 is the option for QR Code. Other options are 1=EAN8/13/UPCA/UPCE, 2=two of five interleaved, 3=Code39, 50=Data Matrix, 51=QRCode
      • 1 is for graphical mode. The barcode is drawn on a Shape object. 0 for font mode. I assume you need to have the font type installed. Not as useful.
      • 0 is the parameter for the particular barcode type. For QR_Code, 0=Low Error Correction, 1=Medium Error Correction, 2=Quartile error correction, 3=high error correction.
      • 2 only applies to 1D codes. It's the buffer zones. I'm not certain what it does exactly but probably something to do with the 1D bar spaces?

I added wrapper functions to make it a pure VBA function call rather than using it as a formula in a worksheet:

Public Sub RenderQRCode(workSheetName As String, cellLocation As String, textValue As String)
   Dim s_param As String
   Dim s_encoded As String
   Dim xSheet As Worksheet
   Dim QRShapeName As String
   Dim QRLabelName As String

   s_param = "mode=Q"
   s_encoded = qr_gen(textValue, s_param)
   Call DrawQRCode(s_encoded, workSheetName, cellLocation)

   Set xSheet = Worksheets(workSheetName)
   QRShapeName = "BC" & "$" & Left(cellLocation, 1) _
       & "$" & Right(cellLocation, Len(cellLocation) - 1) & "#GR"

   QRLabelName = QRShapeName & "_Label"

   With xSheet.Shapes(QRShapeName)
       .Width = 30
       .Height = 30
   End With

   On Error Resume Next
   If Not (xSheet.Shapes(QRLabelName) Is Nothing) Then
       xSheet.Shapes(QRLabelName).Delete
   End If

   xSheet.Shapes.AddTextbox(msoTextOrientationHorizontal, _
       xSheet.Shapes(QRShapeName).Left+35, _
       xSheet.Shapes(QRShapeName).Top, _                          
       Len(textValue) * 6, 30) _
       .Name = QRLabelName


   With xSheet.Shapes(QRLabelName)
       .Line.Visible = msoFalse
       .TextFrame2.TextRange.Font.Name = "Arial"
       .TextFrame2.TextRange.Font.Size = 9
       .TextFrame.Characters.Text = textValue
       .TextFrame2.VerticalAnchor = msoAnchorMiddle
   End With
End Sub

Sub DrawQRCode(xBC As String, workSheetName As String, rangeName As String, Optional xNam As String)
 Dim xShape As Shape, xBkgr As Shape
 Dim xSheet As Worksheet
 Dim xRange As Range, xCell As Range
 Dim xAddr As String
 Dim xPosOldX As Double, xPosOldY As Double
 Dim xSizeOldW As Double, xSizeOldH As Double
 Dim x, y, m, dm, a As Double
 Dim b%, n%, w%, p$, s$, h%, g%

Set xSheet = Worksheets(workSheetName)
Set xRange = Worksheets(workSheetName).Range(rangeName)
xAddr = xRange.Address
xPosOldX = xRange.Left
xPosOldY = xRange.Top

 xSizeOldW = 0
 xSizeOldH = 0
 s = "BC" & xAddr & "#GR"
 x = 0#
 y = 0#
 m = 2.5
 dm = m * 2#
 a = 0#
 p = Trim(xBC)
 b = Len(p)
 For n = 1 To b
   w = AscL(Mid(p, n, 1)) Mod 256
   If (w >= 97 And w <= 112) Then
     a = a + dm
   ElseIf w = 10 Or n = b Then
     If x < a Then x = a
     y = y + dm
     a = 0#
   End If
 Next n
 If x <= 0# Then Exit Sub
 On Error Resume Next
 Set xShape = xSheet.Shapes(s)
 On Error GoTo 0
 If Not (xShape Is Nothing) Then
   xPosOldX = xShape.Left
   xPosOldY = xShape.Top
   xSizeOldW = xShape.Width
   xSizeOldH = xShape.Height
   xShape.Delete
 End If
 On Error Resume Next
 xSheet.Shapes("BC" & xAddr & "#BK").Delete
 On Error GoTo 0
 Set xBkgr = xSheet.Shapes.AddShape(msoShapeRectangle, 0, 0, x, y)
 xBkgr.Line.Visible = msoFalse
 xBkgr.Line.Weight = 0#
 xBkgr.Line.ForeColor.RGB = RGB(255, 255, 255)
 xBkgr.Fill.Solid
 xBkgr.Fill.ForeColor.RGB = RGB(255, 255, 255)
 xBkgr.Name = "BC" & xAddr & "#BK"
 Set xShape = Nothing
 x = 0#
 y = 0#
 g = 0
 For n = 1 To b
   w = AscL(Mid(p, n, 1)) Mod 256
   If w = 10 Then
     y = y + dm
     x = 0#
   ElseIf (w >= 97 And w <= 112) Then
     w = w - 97
     With xSheet.Shapes
     Select Case w
       Case 1: Set xShape = .AddShape(msoShapeRectangle, x, y, m, m): GoSub fmtxshape
       Case 2: Set xShape = .AddShape(msoShapeRectangle, x + m, y, m, m): GoSub fmtxshape
       Case 3: Set xShape = .AddShape(msoShapeRectangle, x, y, dm, m): GoSub fmtxshape
       Case 4: Set xShape = .AddShape(msoShapeRectangle, x, y + m, m, m): GoSub fmtxshape
       Case 5: Set xShape = .AddShape(msoShapeRectangle, x, y, m, dm): GoSub fmtxshape
       Case 6: Set xShape = .AddShape(msoShapeRectangle, x + m, y, m, m): GoSub fmtxshape
               Set xShape = .AddShape(msoShapeRectangle, x, y + m, m, m): GoSub fmtxshape
       Case 7: Set xShape = .AddShape(msoShapeRectangle, x, y, dm, m): GoSub fmtxshape
               Set xShape = .AddShape(msoShapeRectangle, x, y + m, m, m): GoSub fmtxshape
       Case 8: Set xShape = .AddShape(msoShapeRectangle, x + m, y + m, m, m): GoSub fmtxshape
       Case 9: Set xShape = .AddShape(msoShapeRectangle, x, y, m, m): GoSub fmtxshape
               Set xShape = .AddShape(msoShapeRectangle, x + m, y + m, m, m): GoSub fmtxshape
       Case 10: Set xShape = .AddShape(msoShapeRectangle, x + m, y, m, dm): GoSub fmtxshape
       Case 11: Set xShape = .AddShape(msoShapeRectangle, x, y, dm, m): GoSub fmtxshape
                Set xShape = .AddShape(msoShapeRectangle, x + m, y + m, m, m): GoSub fmtxshape
       Case 12: Set xShape = .AddShape(msoShapeRectangle, x, y + m, dm, m): GoSub fmtxshape
       Case 13: Set xShape = .AddShape(msoShapeRectangle, x, y, m, m): GoSub fmtxshape
                Set xShape = .AddShape(msoShapeRectangle, x, y + m, dm, m): GoSub fmtxshape
       Case 14: Set xShape = .AddShape(msoShapeRectangle, x + m, y, m, m): GoSub fmtxshape
                Set xShape = .AddShape(msoShapeRectangle, x, y + m, dm, m): GoSub fmtxshape
       Case 15: Set xShape = .AddShape(msoShapeRectangle, x, y, dm, dm): GoSub fmtxshape
     End Select
     End With
     x = x + dm
   End If
 Next n
 On Error Resume Next
 Set xShape = xSheet.Shapes(s)
 On Error GoTo 0
 If Not (xShape Is Nothing) Then
   xShape.Left = xPosOldX
   xShape.Top = xPosOldY
   If xSizeOldW > 0 Then
     xShape.Width = xSizeOldW
     xShape.Height = xSizeOldH
   End If
 Else
   If Not (xBkgr Is Nothing) Then xBkgr.Delete
 End If
 Exit Sub
fmtxshape:
  xShape.Line.Visible = msoFalse
  xShape.Line.Weight = 0#
  xShape.Fill.Solid
  xShape.Fill.ForeColor.RGB = RGB(0, 0, 0)
  g = g + 1
  xShape.Name = "BC" & xAddr & "#BR" & g
  If g = 1 Then
    xSheet.Shapes.Range(Array(xBkgr.Name, xShape.Name)).Group.Name = s
  Else
    xSheet.Shapes.Range(Array(s, xShape.Name)).Group.Name = s
  End If
  Return

End Sub

With this wrapper, you can now simply call to render QRCode by calling this in VBA:

Call RenderQRCode("Sheet1", "A13", "QR Value")

Just input the worksheet name, cell location, and the QR_value. The QR shape will get drawn at the location you specified.

You can play around with this section of the code to change the size of the QR

With xSheet.Shapes(QRShapeName)
       .Width = 30  'change your size
       .Height = 30  'change your size
   End With
Jonas Heidelberg
  • 4,984
  • 1
  • 27
  • 41
Patratacus
  • 1,651
  • 1
  • 16
  • 19
  • Somehow the QR code content stutters, as if the "for loop" counter that goes through my input to generate the code got reset somewhere halfway through the message, duplicating a few words from the middle of my message :-/. Has anyone else seen such an issue with the Google code linked above? – Jonas Heidelberg Dec 05 '16 at 19:33
  • I still have this issue - I added it as a new question: http://stackoverflow.com/questions/41404226/why-does-this-vba-generated-qr-code-stutter – Jonas Heidelberg Dec 31 '16 at 00:03
  • 1
    I fixed the stuttering now (at least for all edge cases I came across), and put the improved code on GitHub. See updated links in the answer. – Jonas Heidelberg Jan 22 '17 at 23:15
  • Nice job! And thanks for sharing the code to the community! – Patratacus Jan 24 '17 at 18:21
  • Nice job. But it is very slow. I think it might be better if the composite shape is converted into an image and (optionally cached externally in folders also). – Sri Nithya Sharabheshwarananda Feb 10 '17 at 17:03
  • 1
    Is working great for text, but if you want to use numbers only, does not work. Any idea how, or what to change to work also for numbers only? – Lucian Bumb Jun 13 '17 at 09:42
  • Update: this guy (who made the module: https://github.com/JonasHeidelberg/barcode-vba-macro-only/blob/master/barcody.bas) updated the code with a buil-in wraper base on Mr. @patratacus 's idea. Look for "Test_RenderQRCode" sub for detail – Duc Anh Nguyen Sep 27 '17 at 01:16
  • 1
    I got error in `Sub bc_2D(ShIx As Integer, xAddr As String, xBC As String)` on `Dim xPos As New com.sun.star.awt.Point` which reference I should point to? – Dmitrij Holkin Oct 10 '18 at 11:48
11

I know this is quite an old and well-established post (though the very good existing answer has not been accepted yet), but I would like to share an alternative that I prepared for a similar post in StackOverflow in Portuguese using the free online API from QR Code Generator.

The code is the following:

Sub GenQRCode(ByVal data As String, ByVal color As String, ByVal bgcolor As String, ByVal size As Integer)
On Error Resume Next

    For i = 1 To ActiveSheet.Pictures.Count
        If ActiveSheet.Pictures(i).Name = "QRCode" Then
            ActiveSheet.Pictures(i).Delete
            Exit For
        End If
    Next i

    sURL = "https://api.qrserver.com/v1/create-qr-code/?" + "size=" + Trim(Str(size)) + "x" + Trim(Str(size)) + "&color=" + color + "&bgcolor=" + bgcolor + "&data=" + data
    Debug.Print sURL

    Set pic = ActiveSheet.Pictures.Insert(sURL + sParameters)
    Set cell = Range("D9")

    With pic
        .Name = "QRCode"
        .Left = cell.Left
        .Top = cell.Top
    End With

End Sub

It gets the job done by simply (re)creating an image from the URL built from the parameters in the cells. Naturally, the user must be connected to the Internet.

For example (the worksheet, with contents in Brazilian Portuguese, can be downloaded from 4Shared):

enter image description here

Community
  • 1
  • 1
Luiz Vieira
  • 570
  • 11
  • 35
  • 1
    Thanks for your post! Really appreciated! I managed to get your code using the API to work. I'm developing a system that uses 200+ qr codes in one sheet so Patratacus solution slowed the system up majorly so I tried yours and it seems to work much better. Only challenge being - It works on my PC but not on my clients Mac. The problem being calling the sURL. It seems one needs to use Mac Shell but I'm having difficulty implementing it. Any ideas? Should I rather re post this as a new question or answer rather that a comment? Thanks in advance. – Tristan Mar 27 '17 at 11:07
  • Hi there @Tristan. You're welcome. :) I am not a Mac user, so I am afraid I can not help you with that. Nonetheless, I suspect that the OS might be preventing Excel from issuing the HTTP request. Have you tried with a different URL (one that simply responds with a fixed image)? You should check something in that direction. Posting a new question might be useful, but you need more details on your problem, specially to avoid having it suspended as out of scope or not reproducible. Good luck! :) – Luiz Vieira Mar 27 '17 at 13:47
  • Hi @Luiz, On Mac we have got the api to return the same string as what is being returned by your "sURL + sParameters" command inside the Pictures.Insert code. We got this by using Macs shell script "curl --get -d". This seems to return the images raw data? And now it seems that Macs Picture.Insert can't read the raw data and only an image path. So we are trying to find a way around this. Either find a way for Macs Picture.Insert to read raw data or get the data returned by the api to save as a file and then open that with the pictures.insert. Maybe Ill start a new question.Thanks again! – Tristan Mar 27 '17 at 17:37
  • 1
    Hi @Tristan. I think you are missing the point of the code. In MS Excel, you *don't need* to previously download the file to then pass the raw data to `Picture.Insert`. It works directly on an URL (and MS Excel downloads it automatically). Well, at least in the version for Windows... :) – Luiz Vieira Mar 28 '17 at 02:51
  • 1
    Hi @Luiz, Yes, with MS Excel on PC pictue.Insert works fine directly on the URL as in your code but unfortunately it don't do this on Mac. With Mac Picture.Insert only works buy giving it the full path of the file. So for using Mac one has to save the file first and then open that using Picture.Insert. I think we have worked out a solution. I have posted a new question and will post an answer shortly after testing this solution a little more. http://stackoverflow.com/questions/43054725/generating-qr-code-in-mac-vba Thanks for you help again! all the best. – Tristan Mar 28 '17 at 17:25
  • @Tristan Ah, ok. Good to know you have solved it. :) Cheers – Luiz Vieira Mar 28 '17 at 17:27