如何使用 TikZ 或 MetaPost 生成 EAN 条形码?

如何使用 TikZ 或 MetaPost 生成 EAN 条形码?

我正在寻找使用 LuaLaTeX 生成 EAN 条形码的方法。我知道这个问题;解决方案需要 pstricks,它无法与 LuaLaTeX 轻松配合使用(我不想在机器上安装 GhostScript)。我知道这个code128包,但我需要 EAN。这肯定可以用 TikZ 实现吧?

编辑:由于我正在使用 LuaTeX,因此我对使用的解决方案同样感兴趣mplib

答案1

答案相当长,但这不需要任何外部软件包,如 tikz 或 MetaPost。如果你想要详细的解释,你应该阅读下一期拖船或者DTK(德语)。附加功能:如果没有给出校验和,它将计算校验和。

我首先展示一下生成的条形码的图片:

样本条形码

\documentclass[a4paper]{article}
\usepackage{luacode}
\begin{document}
\newcommand\barcodelua[1]{%
  \directlua{generate_barcode_lua("#1")}}

\begin{luacode*}

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
end

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
  else
    dp = "0mm"
  end
  return wd,dp
end

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

  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
    else
      sum = sum + tonumber(string.sub(str,i,i))
    end
  end
  local checksum = (10 - sum % 10) % 10
  return str .. tostring(checksum)
end

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))
  end

  -- 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)
    end
    pattern = pattern .. digits_str
    -- The middle two bars.
    if i==6 then pattern = pattern .. "10101" end
  end
  -- Append the right 111 pattern as above.
  return pattern .. "010"
end

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


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)))
    else
      nodelist = add_to_nodelist(nodelist,mkkern(wd * u))
    end
  end
  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)))
    end
    if i == 1 then
      nodelist = add_to_nodelist(nodelist,mkkern(5 * u))
    elseif i == 2 then
      nodelist = add_to_nodelist(nodelist,mkkern(4 * u))
    end
  end
 local barcode_bottom = node.hpack(nodelist)
 barcode_top = add_to_nodelist(barcode_top,barcode_bottom)
 local bc = node.vpack(barcode_top)

 node.write(bc)
end

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

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

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

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

\end{luacode*}
\barcodelua{4242002518169}
\end{document}

相关内容