Skip to content

Instantly share code, notes, and snippets.

@pgundlach
Created March 14, 2012 10:25
Show Gist options
  • Save pgundlach/2035612 to your computer and use it in GitHub Desktop.
Save pgundlach/2035612 to your computer and use it in GitHub Desktop.
Strichcodes mit LuaTeX
module(...,package.seeall)
local add_checksum_if_necessary, mkpattern, split_number, calculate_unit, pattern_to_wd_dp
function generate_barcode(str)
-- Wenn nur 12 Ziffern übergeben werden, fügen wir die 13 hinzu
str = add_checksum_if_necessary(str)
-- Die kleinste Lücke / Strich ist ein siebtel der Breite einer Ziffer und damit
-- abhängig von der Schriftart
local u = calculate_unit()
-- Wir fangen mit der hbox für die Striche an:
tex.sprint([[\newbox\barcodebox\setbox\barcodebox\hbox{%]])
-- Das Muster (pattern) ist eine Zeichenkette von Ziffern, die die Breite eines Striches bzw.
-- einer Lücke repräsentieren. 0 ist eine spezielle Markierung, die einen längeren Strich
-- der Breite 1 bezeichnet. Die Breiten werden mit 1/7 der Ziffernbreite multipliziert, da
-- die Summe der Striche / Lücken für eine Ziffer insgesamt 7 Einheiten breit sind.
-- Ein Beispiel-Muster fängt an mit
-- 80103211112312132113231132111010132...
local pattern = mkpattern(str)
-- Für jeden Eintrag im Muster erzeugen wir eine Lücke oder einen Balken mit der Breite,
-- die im Eintrag angegeben ist. Eine Tiefe > 0 wird für die Balken am Rand und in der Mitte
-- des Strichcodes benutzt. Es gibt keine technische Notwendigkeit dafür, aber es hat
-- sich eingebürgert den Strichcode so darzustellen.
local wd,dp -- Breite und Tiefe eines Strichs
for i=1,string.len(pattern) do
wd,dp = pattern_to_wd_dp(pattern,i)
-- Die geraden Einträge sind die Länge eines Strichs (vrule), die ungerade der Lücken (kern).
if i % 2 == 0 then
tex.sprint(
string.format([[\vrule width %dsp height 2cm depth %s]],wd * u,dp))
else
tex.sprint(string.format([[\kern %dsp]],wd * u))
end
end
-- Wir haben nun die hbox mit den Strichen und erzeugen nun die hbox mit den Ziffern.
tex.sprint(
[[}\vbox{\hsize\wd\barcodebox\box\barcodebox\kern -1.7mm\hbox{%]]
)
-- Die Ziffern Unterhalb des Strichcodes werden in drei Teile getrennt. Eine vor dem ersten Strich,
-- die erste Hälfte links und die zweite Hälfte rechts von dem mittleren Strich.
local first,second,third = split_number(str)
tex.sprint(
string.format(
[[%s\kern %dsp %s\kern %dsp%s}}]],first, 5 * u, second, 4 * u, third ))
end
function calculate_unit()
-- Die relativen Breiten der Striche und Lücken in einer Ziffer summieren sich zu 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)
-- Die Ziffern 0-9 werden durch diese Striche/Lücken dargestellt. 3211 beispielsweise
-- bedeutet: Lücke von 3 Einheiten, Strich von 2 Einheiten Breite und eine Lücke und ein
-- Strich von jeweils einer Einheit.
local digits_t = {"3211","2221","2122","1411","1132","1231","1114","1312","1213","3112"}
-- Die erste Ziffer wird durch die Darstellung der nächsten sechs Ziffern kodiert. Ein Eintrag
-- von 1 bedeutet, dass die erzeugten Striche / Lücken umgekehrt werden müssen.
local mirror_t = {"------","--1-11","--11-1","--111-","-1--11", "-11--1","-111--","-1-1-1","-1-11-","-11-1-"}
-- Die Zeichenkette aus Ziffern wird in ein Feld konvertiert.
local number = {}
for i=1,string.len(str) do
number[i] = tonumber(string.sub(str,i,i))
end
-- Die erste Ziffer bestimmt, wie die nächsten sechs dargestellt werden.
local prefix = table.remove(number,1)
local mirror_str = mirror_t[prefix + 1]
-- Die Variable pattern wird das erzeugte Muster speichern. Es wird mit der Lücke für die erste
-- Ziffer angefangen, anschließend kommt die linke Begrenzung 111 bzw. hier als 010 für die
-- Kodierung der längeren Striche angegeben.
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
-- Die mittleren beiden Striche
if i==6 then pattern = pattern .. "10101" end
end
-- Das Muster 111 rechts anfügen.
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
-- Die geraden Einträge sind die Linien, die ungeraden die Lücken
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
-- barcode_top wird die vbox aus der ersten Lösung
local barcode_top = node.hpack(nodelist)
barcode_top = add_to_nodelist(barcode_top,mkkern(tex.sp("-1.7mm")))
-- Die folgende Liste enthält die dargestellten Ziffern
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 hat nun drei Einträge: die hbox mit den Linien und Kerns, das Kern
-- mit der Größe -1.7mm und die hbox mit den Ziffern unterhalb der Striche
barcode_top = add_to_nodelist(barcode_top,barcode_bottom)
local bc = node.vpack(barcode_top)
-- node.write() schreibt die vbox in die PDF Datei
node.write(bc)
end
function add_to_nodelist( head,entry )
if head then
-- Den Eintrag 'entry' an die Nodeliste anhängen und die prev/next Zeiger anpassen.
local tail = node.tail(head)
tail.next = entry
entry.prev = tail
else
-- Keine Nodeliste vorhanden, also wird einfach der Eintrag zur Nodeliste
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
\ProvidesPackage{ltxbarcode}
\directlua{require("ltxbarcode")}
\newcommand\barcode[1]{%
\directlua{ltxbarcode.generate_barcode("\luatexluaescapestring{#1}")}}
\newcommand\barcodelua[1]{%
\directlua{ltxbarcode.generate_barcode_lua("#1")}}
\documentclass{article}
\usepackage{ltxbarcode}
\begin{document}
\barcode{424200251816}
% oder, mit Prüfziffer:
\barcode{4242002518169}
\end{document}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment