TeX - LaTeX Stack Exchange is a question and answer site for users of TeX, LaTeX, ConTeXt, and related typesetting systems. Join them; it only takes a minute:

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

I'm looking for way to generate EAN barcodes with LuaLaTeX. I'm aware of this question; the solution to that needs pstricks, which won't work easily with LuaLaTeX (I don't want to install GhostScript on the machines). And I know the code128 package, but I need EAN. Surely this is possible with TikZ?

EDIT: Since I'm using LuaTeX, I'm equally interested in a solution using mplib.

share|improve this question
Would this help? – morbusg Apr 4 '12 at 12:28
@morbusg: Yes. My android barcode scanner likes it. – Martin Schröder Apr 4 '12 at 12:42
up vote 13 down vote accepted

A rather long answer, but this does not need any external packages such as tikz or MetaPost. If you want a detailed explanation, you should get the next issue of the TUGBoat or DTK (German). A bonus feature: it will calculate the checksum if not given.

I'll show a picture of the resulting barcode first:

sample barcode



local add_checksum_if_necessary, mkpattern, split_number, calculate_unit, pattern_to_wd_dp

function calculate_unit()
  -- The relative widths of a digit represented by the barcode add up to 7.
  local currentfont = font.fonts[font.current()]
  local digit_zero = currentfont.characters[48]
  return digit_zero.width / 7

function pattern_to_wd_dp( pattern,pos )
  local wd,dp
  wd = tonumber(string.sub(pattern,pos,pos))
  if wd == 0 then
    dp = "2mm"
    wd = 1
    dp = "0mm"
  return wd,dp

function add_checksum_if_necessary( str )
  if string.len(str) == 13 then
    return str

  local sum = 0
  local len = string.len(str)
  for i=len,1,-1 do
    if (len - i ) % 2 == 0 then
      sum = sum + tonumber(string.sub(str,i,i)) * 3
      sum = sum + tonumber(string.sub(str,i,i))
  local checksum = (10 - sum % 10) % 10
  return str .. tostring(checksum)

function mkpattern( str )
  -- These are the digits represented by the bars.
  local digits_t = {"3211","2221","2122","1411","1132","1231","1114","1312","1213","3112"}

  -- The first digit is encoded by the appearance of the
  -- next six digits. A value of 1 means that the
  -- generated gaps/bars are to be inverted.
  local mirror_t = {"------","--1-11","--11-1","--111-","-1--11","-11--1","-111--","-1-1-1","-1-11-","-11-1-"}

  -- Convert the digit string into an array.
  local number = {}
  for i=1,string.len(str) do
    number[i] = tonumber(string.sub(str,i,i))

  -- The first digit in a barcode determines how the next six digit patterns are displayed.
  local prefix = table.remove(number,1)
  local mirror_str = mirror_t[prefix + 1]

  local pattern = "8010"
  local digits_str

  for i=1,#number do
    digits_str = digits_t[number[i] + 1]
    if string.sub(mirror_str,i,i) == "1" then
      digits_str = string.reverse(digits_str)
    pattern = pattern .. digits_str
    -- The middle two bars.
    if i==6 then pattern = pattern .. "10101" end
  -- Append the right 111 pattern as above.
  return pattern .. "010"

function split_number( str )
  return string.match(str,"(%d)(%d%d%d%d%d%d)(%d%d%d%d%d%d)")

local add_to_nodelist, mkrule, mkkern, mkglyph
function generate_barcode_lua( str )
  str = add_checksum_if_necessary(str)

  local u = calculate_unit()
  local nodelist

  local pattern = mkpattern(str)
  local wd,dp
  for i=1,string.len(pattern) do
    wd,dp = pattern_to_wd_dp(pattern,i)
    if i % 2 == 0 then
      nodelist = add_to_nodelist(nodelist,mkrule(wd * u,tex.sp("2cm"),tex.sp(dp)))
      nodelist = add_to_nodelist(nodelist,mkkern(wd * u))
  local barcode_top = node.hpack(nodelist)
  barcode_top = add_to_nodelist(barcode_top,mkkern(tex.sp("-1.7mm")))

  -- The following list holds the displayed digits
  nodelist = nil
  for i,v in ipairs({split_number(str)}) do
    for j=1,string.len(v) do
      nodelist = add_to_nodelist(nodelist,mkglyph(string.sub(v,j,j)))
    if i == 1 then
      nodelist = add_to_nodelist(nodelist,mkkern(5 * u))
    elseif i == 2 then
      nodelist = add_to_nodelist(nodelist,mkkern(4 * u))
 local barcode_bottom = node.hpack(nodelist)
 barcode_top = add_to_nodelist(barcode_top,barcode_bottom)
 local bc = node.vpack(barcode_top)


function add_to_nodelist( head,entry )
  if head then
    local tail = node.tail(head)
    tail.next = entry
    entry.prev = tail
    head = entry
  return head

function mkrule( wd,ht,dp )
  local r = node.new("rule")
  r.width = wd
  r.height = ht
  r.depth = dp
  return r

function mkkern( wd )
  local k = node.new("kern")
  k.kern = wd
  return k

function mkglyph( char )
  local g = node.new("glyph")
  g.char = string.byte(char)
  g.font = font.current()
  g.lang = tex.language
  return g

share|improve this answer

Your Answer


By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.