我正在寻找使用 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}