summaryrefslogtreecommitdiff
path: root/macros/luatex/generic/barracuda/lib/lib-barcode
diff options
context:
space:
mode:
Diffstat (limited to 'macros/luatex/generic/barracuda/lib/lib-barcode')
-rw-r--r--macros/luatex/generic/barracuda/lib/lib-barcode/barcode.lua544
-rw-r--r--macros/luatex/generic/barracuda/lib/lib-barcode/code128.lua341
-rw-r--r--macros/luatex/generic/barracuda/lib/lib-barcode/code39.lua381
-rw-r--r--macros/luatex/generic/barracuda/lib/lib-barcode/ean.lua790
-rw-r--r--macros/luatex/generic/barracuda/lib/lib-barcode/i2of5.lua378
5 files changed, 2434 insertions, 0 deletions
diff --git a/macros/luatex/generic/barracuda/lib/lib-barcode/barcode.lua b/macros/luatex/generic/barracuda/lib/lib-barcode/barcode.lua
new file mode 100644
index 0000000000..ed6204528a
--- /dev/null
+++ b/macros/luatex/generic/barracuda/lib/lib-barcode/barcode.lua
@@ -0,0 +1,544 @@
+
+-- Barcode abstract class
+-- Copyright (C) 2019 Roberto Giacomelli
+-- Please see LICENSE.TXT for any legal information about present software
+
+local Barcode = {
+ _VERSION = "Barcode v0.0.5",
+ _NAME = "Barcode",
+ _DESCRIPTION = "Barcode abstract class",
+}
+Barcode.__index = Barcode
+
+-- barcode_type/submodule name
+Barcode._available_enc = {-- keys must be lowercase
+ code39 = "lib-barcode.code39",
+ code128 = "lib-barcode.code128",
+ ean = "lib-barcode.ean",
+ i2of5 = "lib-barcode.i2of5", -- Interleaved 2 of 5
+}
+Barcode._builder_instances = {} -- encoder builder instances repository
+
+-- common parameters to all the barcode objects
+Barcode._super_par_def = {}
+local pardef = Barcode._super_par_def
+
+-- set an Anchor point (ax, ay) relatively to the barcode bounding box
+-- without considering any text object
+-- ax = 0, ay = 0 is the lower left corner of the symbol
+-- ax = 1, ay = 1 is the upper right corner of the symbol
+Barcode.ax = 0.0
+pardef.ax = {
+ default = 0.0,
+ unit = "sp", -- scaled point
+ isReserved = false,
+ order = 1,
+ fncheck = function (self, ax, _) --> boolean, err
+ if ax >= 0.0 and ax <= 1.0 then return true, nil end
+ return false, "[OutOfRange] 'ax' out of [0, 1] interval"
+ end,
+}
+Barcode.ay = 0.0
+pardef.ay = {
+ default = 0.0,
+ unit = "sp", -- scaled point
+ isReserved = false,
+ order = 2,
+ fncheck = function (self, ay, _) --> boolean, err
+ if ay >= 0.0 and ay <= 1.0 then return true, nil end
+ return false, "[OutOfRange] 'ay' out of [0, 1] interval"
+ end,
+}
+
+-- Barcode.bbox_to_quietzone -- under assessment
+
+-- Barcode methods
+
+-- stateless iterator troughtout the ordered parameters collection
+local function p_iter(state, i)
+ i = i + 1
+ local t = state[i]
+ if t then
+ return i, t
+ end
+end
+
+-- main iterator on parameter definitions
+function Barcode:param_ord_iter()
+ local state = {}
+ -- append family parameter
+ local p2_family = self._par_def -- base family parameters
+ local fam_len = 0
+ if p2_family then
+ for pname, pdef in pairs(p2_family) do
+ state[pdef.order] = {
+ pname = pname,
+ pdef = pdef,
+ isSuper = false,
+ }
+ fam_len = fam_len + 1
+ end
+ assert(fam_len == #state)
+ end
+ -- append the variant parameters
+ local var_len = 0
+ local var = self._variant
+ if var then -- specific variant parameters
+ local p2_variant = assert(self._par_def_variant[var])
+ for pname, pdef in pairs(p2_variant) do
+ if state[pname] then
+ error("[InternalErr] overriding paramenter '"..pname.."'")
+ end
+ state[pdef.order + fam_len] = {
+ pname = pname,
+ pdef = pdef,
+ isSuper = false,
+ }
+ var_len = var_len + 1
+ end
+ assert(fam_len + var_len == #state)
+ end
+ -- append the super class parameter to the iterator state
+ local p1 = self._super_par_def
+ local super_len = 0
+ for pname, pdef in pairs(p1) do
+ if state[pname] then
+ error("[InternalError] overriding paramenter name '"..pname.."'")
+ end
+ state[fam_len + var_len + pdef.order] = {
+ pname = pname,
+ pdef = pdef,
+ isSuper = true,
+ }
+ super_len = super_len + 1
+ end
+ assert(super_len + fam_len + var_len == #state)
+ return p_iter, state, 0
+end
+
+-- encoder costructor
+-- Symbology can be a family with many variants. This is represented by the
+-- first argument 'bc_type' formatted as <family>-<variant>.
+-- in more details:
+-- <family id><dash char><variant id> if there are variants
+-- <encoder id> if not
+-- i.e. when 'bc_type' is the string "ean-13", "ean" is the barcode family and
+-- "13" is its variant name.
+-- For whose barcodes that do not have variants, 'bc_type' is simply the endoder id
+-- such as "code128".
+-- 'id_enc' is an optional identifier useful to retrive an encoder reference later
+-- 'opt' is an optional table with the user-defined parameters setting up encoders
+--
+function Barcode:new_encoder(bc_type, id_enc, opt) --> object, err
+ -- argument checking
+ if type(bc_type) ~= "string" then
+ return nil, "[ArgErr] 'bc_type' is not a string"
+ end
+ local pdash = string.find(bc_type, "-")
+ local family, variant
+ if pdash then
+ family = string.sub(bc_type, 1, pdash - 1)
+ variant = string.sub(bc_type, pdash + 1)
+ else
+ family = bc_type
+ end
+ local av_enc = self._available_enc
+ if not av_enc[family] then -- is the barcode type a real module?
+ return nil, "[Err] barcode type '"..family.."' not found"
+ end
+ if id_enc == nil then
+ id_enc = (variant or "") .. "_noname"
+ elseif type(id_enc) ~= "string" then
+ return nil, "[ArgErr] provided 'id_enc' is not a string"
+ end
+ if type(opt) == "table" or opt == nil then
+ opt = opt or {}
+ else
+ return nil, "[ArgErr] provided 'opt' is wrong"
+ end
+ local tenc = self._builder_instances
+ local builder;
+ if tenc[family] then -- is the encoder builder already loaded?
+ builder = tenc[family]
+ else -- loading the encoder builder
+ local mod_path = av_enc[family]
+ builder = require(mod_path)
+ tenc[family] = assert(builder, "[InternalErr] module not found!")
+ builder._enc_instances = {}
+ end
+ if builder._enc_instances[id_enc] then
+ return nil, "[Err] 'id_enc' already present"
+ end
+ local enc = {} -- the new encoder
+ enc.__index = enc
+ enc._variant = variant
+ setmetatable(enc, {
+ __index = function(_, k)
+ if builder[k] ~= nil then
+ return builder[k]
+ end
+ return self[k]
+ end
+ })
+ builder._enc_instances[id_enc] = enc
+ -- param defition
+ for _, tpar in enc:param_ord_iter() do
+ local pname = tpar.pname
+ local pdef = tpar.pdef
+ local isSuper = tpar.isSuper
+ local val = opt[pname] -- param = val
+ if val ~= nil then
+ local ok, err = pdef:fncheck(val, enc)
+ if ok then
+ enc[pname] = val
+ else -- error!
+ return nil, err
+ end
+ else
+ -- load the default value of <pname>
+ local def_val; if pdef.fndefault then
+ def_val = pdef:fndefault(enc)
+ else
+ def_val = pdef.default
+ end
+ if not isSuper then
+ enc[pname] = def_val
+ end
+ end
+ end
+ if enc.config then -- this must be called after the parameter definition
+ enc:config(variant)
+ end
+ return enc, nil
+end
+
+-- retrive encoder object already created
+-- 'name' is optional in case you didn't assign one to the encoder
+function Barcode:enc_by_name(bc_type, name) --> <encoder object>, <err>
+ if type(bc_type) ~= "string" then
+ return nil, "[ArgErr] 'bc_type' must be a string"
+ end
+ local pdash = string.find(bc_type, "-")
+ local family, variant
+ if pdash then
+ family = string.sub(bc_type, 1, pdash - 1)
+ variant = string.sub(bc_type, pdash + 1)
+ else
+ family = bc_type
+ end
+ local av_enc = self._available_enc
+ if not av_enc[family] then -- is the barcode type a real module?
+ return nil, "[Err] barcode type '"..family.."' not found"
+ end
+ local builder = self._builder_instances[family]
+ if builder == nil then
+ return nil, "[Err] enc builder '"..family.."' not loaded, use 'new_encoder()' method"
+ end
+ if name == nil then
+ name = (variant or "") .. "_noname"
+ elseif type(name) ~= "string" then
+ return nil, "[ArgErr] 'name' must be a string"
+ end
+ local repo = builder._enc_instances
+ local enc = repo[name]
+ if enc == nil then
+ return nil, "[Err] encoder '"..name.."' not found"
+ else
+ return enc, nil
+ end
+end
+
+-- base constructors common to all the encoders
+-- for numeric only simbology
+function Barcode:_check_char(c) --> elem, err
+ if type(c) ~= "string" or #c ~= 1 then
+ return nil, "[InternalErr] invalid char"
+ end
+ local n = string.byte(c) - 48
+ if n < 0 or n > 9 then
+ return nil, "[ArgErr] found a not digit char"
+ end
+ return n, nil
+end
+function Barcode:_check_digit(n) --> elem, err
+ if type(n) ~= "number" then
+ return nil, "[InternalErr] not a number"
+ end
+ if n < 0 or n > 9 then
+ return nil, "[InternalErr] not a digit"
+ end
+ return n, nil
+end
+
+-- not empty string --> Barcode object
+function Barcode:from_string(symb, opt) --> object, err
+ assert(self._check_char, "[InternalErr] undefined _check_char() method")
+ if type(symb) ~= "string" then
+ return nil, "[ArgErr] 'symb' is not a string"
+ end
+ if #symb == 0 then
+ return nil, "[ArgErr] 'symb' is an empty string"
+ end
+ local chars = {}
+ local len = 0
+ for c in string.gmatch(symb, ".") do
+ local elem, err = self:_check_char(c)
+ if err then
+ return nil, err
+ else
+ chars[#chars+1] = elem
+ len = len + 1
+ end
+ end
+ -- build the barcode object
+ local o = {
+ _code_data = chars, -- array of chars
+ _code_len = len, -- symbol lenght
+ }
+ setmetatable(o, self)
+ if opt ~= nil then
+ if type(opt) ~= "table" then
+ return nil, "[ArgErr] 'opt' is not a table"
+ else
+ local ok, err = o:set_param(opt)
+ if not ok then
+ return nil, err
+ end
+ end
+ end
+ if o._finalize then
+ local ok, e = o:_finalize()
+ if not ok then return nil, e end
+ end
+ return o, nil
+end
+
+-- positive integer --> Barcode object
+function Barcode:from_uint(n, opt) --> object, err
+ assert(self._check_digit, "[InternalErr] undefined _check_digit() method")
+ if type(n) ~= "number" then return nil, "[ArgErr] 'n' is not a number" end
+ if n < 0 then return nil, "[ArgErr] 'n' must be a positive integer" end
+ if n - math.floor(n) ~= 0 then
+ return nil, "[ArgErr] 'n' is not an integer"
+ end
+ if opt ~= nil and type(opt) ~= "table" then
+ return nil, "[ArgErr] 'opt' is not a table"
+ end
+ local digits = {}
+ local i = 0
+ if n == 0 then
+ local elem, err = self:_check_digit(0)
+ if err then
+ return nil, err
+ end
+ digits[1] = elem
+ i = 1
+ else
+ while n > 0 do
+ local d = n % 10
+ i = i + 1
+ local elem, err = self:_check_digit(d)
+ if err then
+ return nil, err
+ end
+ digits[i] = elem
+ n = (n - d) / 10
+ end
+ for k = 1, i/2 do -- reverse the array
+ local d = digits[k]
+ local h = i - k + 1
+ digits[k] = digits[h]
+ digits[h] = d
+ end
+ end
+ -- build the barcode object
+ local o = {
+ _code_data = digits, -- array of digits
+ _code_len = i, -- symbol lenght
+ }
+ setmetatable(o, self)
+ if opt ~= nil then
+ if type(opt) ~= "table" then
+ return nil, "[ArgErr] 'opt' is not a table"
+ else
+ local ok, err = o:set_param(opt)
+ if not ok then
+ return nil, err
+ end
+ end
+ end
+ if o._finalize then
+ local ok, e = o:_finalize()
+ if not ok then return nil, e end
+ end
+ return o, nil
+end
+
+-- check a parameter set
+-- this method check also reserved parameter
+-- argments: {k=v, ...}, "default" | "current"
+-- if ref is "default" parameters are checked with default values
+-- if ref is "current" parameters are checked with current values
+function Barcode:check_param(opt, ref) --> boolean, check report
+ if type(opt) ~= "table" then
+ return nil, "[ArgErr] opt is not a table"
+ end
+ if ref == nil then
+ ref = "current"
+ else
+ if type(ref) ~= "string" then
+ return nil, "[ArgErr] ref is not a string"
+ end
+ if (ref ~= "current") or (ref ~= "default") then
+ return nil, "[ArgErr] ref can only be 'default' or 'current'"
+ end
+ end
+ -- checking process
+ local cktab = {}
+ local isOk = true
+ local err_rpt -- nil if no error
+ for _, tpar in self:param_ord_iter() do
+ local pname = tpar.pname
+ local pdef = tpar.pdef
+ -- load the default value of <pname>
+ local def_val; if pdef.fndefault then
+ def_val = pdef:fndefault(cktab)
+ else
+ def_val = pdef.default
+ end
+ local val = opt[pname]
+ if val ~= nil then
+ local ok, err = pdef:fncheck(val, cktab)
+ if ok then
+ cktab[pname] = val
+ else -- error!
+ isOk = false
+ if err_rpt == nil then err_rpt = {} end
+ err_rpt[#err_rpt + 1] = {
+ param = pname,
+ checked_val = val,
+ default_val = def_val,
+ isOk = ok,
+ err = err,
+ }
+ end
+ end
+ local v
+ if ref == "current" then
+ v = self[pname]
+ else
+ v = def_val
+ end
+ cktab[pname] = v
+ end
+ return isOk, err_rpt
+end
+
+-- restore to the default values all the parameter
+-- (reserved parameters are unmodified so no need to restore it)
+-- this need further investigation about the conseguence of a restore
+-- that reset the parameter but "locally"
+-- so this method must be considered experimental
+function Barcode:restore_param() --> self :FIXME:
+ for _, par in ipairs(self._par_id) do
+ local pdef = self[par.."_def"]
+ if not pdef.isReserved then
+ self[par] = pdef.default
+ end
+ end
+ return self
+end
+
+-- create a table with the information of the current barcode encoder
+function Barcode:info() --> table
+ local info = {
+ name = self._NAME,
+ version = self._VERSION,
+ description = self._DESCRIPTION,
+ param = {},
+ }
+ local tpar = info.param
+ for _, pardef in self:param_ord_iter() do
+ local id = pardef.pname
+ local pdef = pardef.pdef
+ tpar[#tpar + 1] = {
+ name = id,
+ descr = nil, -- TODO:
+ value = self[id],
+ isReserved = pdef.isReserved,
+ unit = pdef.unit,
+ }
+ end
+ return info
+end
+
+-- make accessible by name parameter values
+-- id: parameter identifier
+function Barcode:get_param(id) --> value, err
+ if type(id) ~= "string" then
+ return nil, "[ArgErr] 'id' must be a string"
+ end
+ local pardef = self._par_def
+ if not pardef[id] then
+ return nil, "[Err] Parameter '"..id.."' doesn't exist"
+ end
+ local res = assert(self[id], "[InternalErr] parameter value unreachable")
+ return res, nil
+end
+
+-- set a barcode parameter only if it is not reserved
+-- arguments:
+-- :set_param{key = value, key = value, ...}
+-- :set_param(key, value)
+function Barcode:set_param(arg1, arg2) --> boolean, err
+ -- processing arguments
+ local targ
+ local isPair = true
+ if type(arg1) == "table" then
+ if arg2 ~= nil then
+ return false, "[ArgErr] Further arguments not allowed"
+ end
+ targ = arg1
+ isPair = false
+ elseif type(arg1) == "string" then -- key/value
+ if arg2 == nil then
+ return false, "[ArgErr] 'value' as the second argument expected"
+ end
+ targ = {}
+ targ[arg1] = arg2
+ else
+ return false, "[ArgErr] param name must be a string"
+ end
+ -- preparing to the checking process
+ local cktab = {}
+ local ckparam = {}
+ -- checking process
+ for _, tpar in self:param_ord_iter() do
+ local pname = tpar.pname
+ local pdef = tpar.pdef
+ local val = targ[pname] -- par = val
+ if val ~= nil then
+ if pdef.isReserved then
+ return false, "[Err] parameter '" .. pname ..
+ "' is reserved, create another encoder"
+ end
+ local ok, err = pdef:fncheck(val, cktab)
+ if ok then
+ cktab[pname] = val
+ ckparam[pname] = val
+ else -- error!
+ return false, err
+ end
+ else -- no val in user option
+ cktab[pname] = self[pname]
+ end
+ end
+ for p, v in pairs(ckparam) do
+ self[p] = v
+ end
+ return true, nil
+end
+
+return Barcode
+
+--
diff --git a/macros/luatex/generic/barracuda/lib/lib-barcode/code128.lua b/macros/luatex/generic/barracuda/lib/lib-barcode/code128.lua
new file mode 100644
index 0000000000..317b3de8f8
--- /dev/null
+++ b/macros/luatex/generic/barracuda/lib/lib-barcode/code128.lua
@@ -0,0 +1,341 @@
+-- Code128 barcode generator module
+-- Copyright (C) 2019 Roberto Giacomelli
+--
+-- All dimension must be in scaled point (sp)
+-- every fields that starts with an undercore sign are intended as private
+
+local Code128 = {
+ _VERSION = "code128 v0.0.5",
+ _NAME = "Code128",
+ _DESCRIPTION = "Code128 barcode encoder",
+}
+
+Code128._int_def_bar = {-- code bar definitions
+ [0] = 212222, 222122, 222221, 121223, 121322, 131222, 122213, 122312,
+ 132212, 221213, 221312, 231212, 112232, 122132, 122231, 113222, 123122,
+ 123221, 223211, 221132, 221231, 213212, 223112, 312131, 311222, 321122,
+ 321221, 312212, 322112, 322211, 212123, 212321, 232121, 111323, 131123,
+ 131321, 112313, 132113, 132311, 211313, 231113, 231311, 112133, 112331,
+ 132131, 113123, 113321, 133121, 313121, 211331, 231131, 213113, 213311,
+ 213131, 311123, 311321, 331121, 312113, 312311, 332111, 314111, 221411,
+ 431111, 111224, 111422, 121124, 121421, 141122, 141221, 112214, 112412,
+ 122114, 122411, 142112, 142211, 241211, 221114, 413111, 241112, 134111,
+ 111242, 121142, 121241, 114212, 124112, 124211, 411212, 421112, 421211,
+ 212141, 214121, 412121, 111143, 111341, 131141, 114113, 114311, 411113,
+ 411311, 113141, 114131, 311141, 411131, 211412, 211214, 211232,
+ 2331112, -- this is the stop char at index 106
+}
+
+Code128._codeset = {
+ A = 103, -- Start char for Codeset A
+ B = 104, -- Start char for Codeset B
+ C = 105, -- Start char for Codeset C
+ stopChar = 106, -- Stop char
+ shift = 98, -- A to B or B to A
+}
+
+Code128._switch = { -- codes for switching from a codeset to another one
+ [103] = {[104] = 100, [105] = 99}, -- from A to B or C
+ [104] = {[103] = 101, [105] = 99}, -- from B to A or C
+ [105] = {[103] = 101, [104] = 100}, -- from C to A or B
+}
+
+-- parameters definition
+Code128._par_def = {}
+local pardef = Code128._par_def
+
+-- module main parameter
+pardef.xdim = {
+ default = 0.21 * 186467, -- X dimension
+ unit = "sp", -- scaled point
+ isReserved = true,
+ order = 1, -- the one first to be modified
+ fncheck = function (self, x, _) --> boolean, err
+ if x >= self.default then
+ return true, nil
+ else
+ return false, "[OutOfRange] too small value for xdim"
+ end
+ end,
+}
+
+pardef.ydim = {
+ default = 10 * 186467, -- Y dimension
+ unit = "sp",
+ isReserved = false,
+ order = 2,
+ fncheck = function (self, y, tpar) --> boolean, err
+ local xdim = tpar.xdim
+ if y >= 10*xdim then
+ return true, nil
+ else
+ return false, "[OutOfRange] too small value for ydim"
+ end
+ end,
+}
+
+pardef.quietzone_factor = {
+ default = 10,
+ unit = "absolute-number",
+ isReserved = false,
+ order = 3,
+ fncheck = function (self, z, _) --> boolean, err
+ if z >= 10 then
+ return true, nil
+ else
+ return false, "[OutOfRange] too small value for quietzone_factor"
+ end
+ end,
+}
+
+-- create vbar objects
+function Code128:config() --> ok, err
+ -- build Vbar object for the start/stop symbol
+ local mod = self.xdim
+ local sc = self._codeset.stopChar -- build the stop char
+ local n = self._int_def_bar[sc]
+ local Vbar = self._libgeo.Vbar -- Vbar class
+ self._vbar = {}
+ local b = self._vbar
+ b[sc] = Vbar:from_int(n, mod, true)
+ return true, nil
+end
+
+-- utility functions
+
+-- the number of consecutive digits from the index 'i' in the code array
+local function count_digits_from(arr, i)
+ local start = i
+ local dim = #arr
+ while i <= dim and (arr[i] > 47 and arr[i] < 58) do
+ i = i + 1
+ end
+ return i - start
+end
+
+-- evaluate the check digit of the data representation
+local function check_digit(code)
+ local sum = code[1] -- this is the start character
+ for i = 2, #code do
+ sum = sum + code[i]*(i-1)
+ end
+ return sum % 103
+end
+
+-- return a pair of boolean the first one is true
+-- if a control char and a lower case char occurs in the data
+-- and the second one is true if the control char occurs before
+-- the lower case char
+local function ctrl_or_lowercase(pos, data) --> boolean, boolean|nil
+ local len = #data
+ local ctrl_occur, lower_occur = false, false
+ for i = pos, len do
+ local c = data[i]
+ if (not ctrl_occur) and (c >= 0 and c < 32) then
+ -- [0,31] control chars
+ if lower_occur then
+ return true, false -- lowercase char < ctrl char
+ else
+ ctrl_occur = true
+ end
+ elseif (not lower_occur) and (c > 95 and c < 128) then
+ -- [96, 127] lower case chars
+ if ctrl_occur then
+ return true, true -- ctrl char < lowercase char
+ else
+ lower_occur = true
+ end
+ end
+ end
+ return false -- no such data
+end
+
+-- encode the provided char against a codeset
+-- in the future this function will consider FN data
+local function encode_char(t, codesetAorB, char_code, codesetA)
+ local code
+ if codesetAorB == codesetA then -- codesetA
+ if char_code < 32 then
+ code = char_code + 64
+ elseif char_code > 31 and char_code < 96 then
+ code = char_code - 32
+ else
+ error("Not implemented or wrong code")
+ end
+ else -- codesetB
+ if char_code > 31 and char_code < 128 then
+ code = char_code - 32
+ else
+ error("Not implemented or wrong code")
+ end
+ end
+ t[#t + 1] = code
+end
+
+-- encode the message in a sequence of Code128 symbol minimizing its lenght
+local function encode128(arr, codeset, switch) --> data, err :TODO:
+ local res = {} -- the result array (the check character will be appended)
+ -- find the Start Character A, B, or C
+ local cur_codeset
+ local ndigit = count_digits_from(arr, 1)
+ local len = #arr
+ --local no_ctrl_lower_char
+ if (ndigit == 2 and len == 2) or ndigit > 3 then -- start char code C
+ cur_codeset = codeset.C
+ else
+ local ok, ctrl_first = ctrl_or_lowercase(1, arr)
+ if ok and ctrl_first then
+ cur_codeset = codeset.A
+ else
+ cur_codeset = codeset.B
+ end
+ end
+ res[#res + 1] = cur_codeset
+ local pos = 1 -- symbol's index to encode
+ while pos <= len do
+ if cur_codeset == codeset.C then
+ if arr[pos] < 48 or arr[pos] > 57 then -- not numeric char
+ local ok, ctrl_first = ctrl_or_lowercase(pos, arr)
+ if ok and ctrl_first then
+ cur_codeset = codeset.A
+ else
+ cur_codeset = codeset.B
+ end
+ res[#res + 1] = switch[codeset.C][cur_codeset]
+ else
+ local imax = pos + 2*math.floor(ndigit/2) - 1
+ for idx = pos, imax, 2 do
+ res[#res + 1] = (arr[idx] - 48)*10 + arr[idx+1] - 48
+ end
+ pos = pos + imax
+ ndigit = ndigit - imax
+ if ndigit == 1 then
+ -- cur_codeset setup
+ local ok, ctrl_first = ctrl_or_lowercase(pos + 1, arr)
+ if ok and ctrl_first then
+ cur_codeset = codeset.A
+ else
+ cur_codeset = codeset.B
+ end
+ res[#res + 1] = switch[codeset.C][cur_codeset]
+ end
+ end
+ else --- current codeset is A or B
+ if ndigit > 3 then
+ if ndigit % 2 > 1 then -- odd number of digits
+ encode_char(res, cur_codeset, arr[pos], codeset.A)
+ pos = pos + 1
+ ndigit = ndigit - 1
+ end
+ res[#res + 1] = switch[cur_codeset][codeset.C]
+ cur_codeset = codeset.C
+ elseif (cur_codeset == codeset.B) and
+ (arr[pos] >= 0 and arr[pos] < 32) then -- ops a control char
+ local ok, ctrl_first = ctrl_or_lowercase(pos + 1, arr)
+ if ok and (not ctrl_first) then -- shift to codeset A
+ res[#res + 1] = codeset.shift
+ encode_char(res, codeset.A, arr[pos], codeset.A)
+ pos = pos + 1
+ ndigit = count_digits_from(pos, arr)
+ else -- switch to code set A
+ res[#res + 1] = switch[cur_codeset][codeset.A]
+ cur_codeset = codeset.A
+ end
+ elseif (cur_codeset == codeset.A) and
+ (arr[pos] > 95 and arr[pos] < 128) then -- ops a lower case char
+ local ok, ctrl_first = ctrl_or_lowercase(pos+1, arr)
+ if ok and ctrl_first then -- shift to codeset B
+ res[#res + 1] = codeset.shift
+ encode_char(res, codeset.B, arr[pos], codeset.A)
+ pos = pos + 1
+ ndigit = count_digits_from(arr, pos)
+ else -- switch to code set B
+ res[#res + 1] = switch[cur_codeset][codeset.B]
+ cur_codeset = codeset.B
+ end
+ else
+ -- insert char
+ encode_char(res, cur_codeset, arr[pos], codeset.A)
+ pos = pos + 1
+ ndigit = count_digits_from(arr, pos)
+ end
+ end
+ end
+ res[#res + 1] = check_digit(res)
+ res[#res + 1] = codeset.stopChar
+ return res
+end
+
+-- Code 128 internal functions used by Barcode costructors
+
+function Code128:_check_char(c) --> elem, err
+ if type(c) ~= "string" or #c ~= 1 then
+ return nil, "[InternalErr] invalid char"
+ end
+ local b = string.byte(c)
+ if b > 127 then
+ local fmt = "[unimplemented] the '%d' is an ASCII extented char"
+ return nil, string.format(fmt, c)
+ end
+ return b, nil
+end
+
+function Code128:_check_digit(n) --> elem, err
+ if type(n) ~= "number" then
+ return nil, "[InternalErr] invalid digit"
+ end
+ return n + 48, nil
+end
+
+function Code128:_finalize() --> ok, err
+ local chr = assert(self._code_data, "[InternalErr] '_code_data' field is nil")
+ local data, err = encode128(chr, self._codeset, self._switch)
+ if err then return false, err end
+ -- load dynamically required Vbar objects
+ local vbar = self._vbar
+ local oVbar = self._libgeo.Vbar
+ for _, c in ipairs(data) do
+ if not vbar[c] then
+ local n = self._int_def_bar[c]
+ local mod = self.xdim
+ vbar[c] = oVbar:from_int(n, mod, true)
+ end
+ end
+ self._enc_data = data
+ return true, nil
+end
+
+-- Drawing into the provided channel the geometrical barcode data
+-- tx, ty is the optional translator vector
+-- the function return the canvas reference to allow call chaining
+function Code128:append_ga(canvas, tx, ty) --> canvas
+ local xdim, h = self.xdim, self.ydim
+ local sw = 11*xdim -- the width of a symbol
+ local data = self._enc_data
+ local w = #data * sw + 2 * xdim -- total symbol width
+ local ax, ay = self.ax, self.ay
+ local x0 = (tx or 0) - ax * w
+ local y0 = (ty or 0) - ay * h
+ local x1 = x0 + w
+ local y1 = y0 + h
+ local xpos = x0
+ -- drawing the symbol
+ local err
+ err = canvas:start_bbox_group()
+ assert(not err, err)
+ for _, c in ipairs(data) do
+ local vb = self._vbar[c]
+ err = canvas:encode_Vbar(vb, xpos, y0, y1)
+ assert(not err, err)
+ xpos = xpos + sw
+ end
+ -- bounding box setting
+ local qz = self.quietzone_factor * xdim
+ -- { xmin, ymin, xmax, ymax }
+ err = canvas:stop_bbox_group(x0 - qz, y0, x1 + qz, y1)
+ assert(not err, err)
+ return canvas
+end
+
+return Code128
+-- \ No newline at end of file
diff --git a/macros/luatex/generic/barracuda/lib/lib-barcode/code39.lua b/macros/luatex/generic/barracuda/lib/lib-barcode/code39.lua
new file mode 100644
index 0000000000..a639619c63
--- /dev/null
+++ b/macros/luatex/generic/barracuda/lib/lib-barcode/code39.lua
@@ -0,0 +1,381 @@
+-- Code39 barcode encoder implementation
+-- Copyright (C) 2019 Roberto Giacomelli
+--
+-- All dimensions must be in scaled point (sp)
+-- every fields that starts with an undercore sign are intended as private
+
+local Code39 = {
+ _VERSION = "code39 v0.0.5",
+ _NAME = "Code39",
+ _DESCRIPTION = "Code39 barcode encoder",
+}
+
+Code39._symb_def = {-- symbol definition
+ ["0"] = 112122111, ["1"] = 211112112, ["2"] = 211112211,
+ ["3"] = 111112212, ["4"] = 211122111, ["5"] = 111122112,
+ ["6"] = 111122211, ["7"] = 212112111, ["8"] = 112112112,
+ ["9"] = 112112211, ["A"] = 211211112, ["B"] = 211211211,
+ ["C"] = 111211212, ["D"] = 211221111, ["E"] = 111221112,
+ ["F"] = 111221211, ["G"] = 212211111, ["H"] = 112211112,
+ ["I"] = 112211211, ["J"] = 112221111, ["K"] = 221111112,
+ ["L"] = 221111211, ["M"] = 121111212, ["N"] = 221121111,
+ ["O"] = 121121112, ["P"] = 121121211, ["Q"] = 222111111,
+ ["R"] = 122111112, ["S"] = 122111211, ["T"] = 122121111,
+ ["U"] = 211111122, ["V"] = 211111221, ["W"] = 111111222,
+ ["X"] = 211121121, ["Y"] = 111121122, ["Z"] = 111121221,
+ ["-"] = 212111121, ["."] = 112111122, [" "] = 112111221,
+ ["$"] = 111212121, ["/"] = 121112121, ["+"] = 121211121,
+ ["%"] = 121212111,
+}
+Code39._star_def = 112121121 -- '*' start/stop character
+
+-- parameters definition
+Code39._par_def = {}
+local pardef = Code39._par_def
+
+-- module main parameter
+pardef.module = {
+ -- Narrow element X-dimension is the width of the smallest element in a
+ -- barcode symbol.
+ -- The X-dimension impacts scan-ability. Within the allowed range, it is
+ -- recommended to use the largest possible X-dimension that is consistent
+ -- with label or form design.
+ -- The module width (width of narrow element) should be at least 7.5 mils
+ -- or 0.1905mm (a mil is 1/1000 inch).
+ default = 7.5 * 0.0254 * 186467, -- 7.5 mils (sp) unit misure,
+ unit = "sp", -- scaled point
+ isReserved = true,
+ order = 1, -- the one first to be modified
+ fncheck = function (self, mod, _) --> boolean, err
+ if mod >= self.default then return true, nil end
+ return false, "[OutOfRange] too small value for module"
+ end,
+}
+
+pardef.ratio = {
+ -- The "wide" element is a multiple of the "narrow" element and this
+ -- multiple must remain the same throughout the symbol. This multiple can
+ -- range between 2.0 and 3.0. Preferred value is 3.0.
+ -- The multiple for the wide element should be between 2.0 and 3.0 if the
+ -- narrow element is greater than 20 mils. If the narrow element is less
+ -- than 20 mils (0.508mm), the multiple can only range between 2.0 and 2.2.
+ default = 2.0, -- the minimum
+ unit = "absolute-number",
+ isReserved = true,
+ order = 2,
+ fncheck = function (self, ratio, tparcheck) --> boolean, err
+ local mils = 0.0254 * 186467
+ local mod = tparcheck.module
+ local maxr; if mod < 20*mils then maxr = 2.2 else maxr = 3.0 end
+ if ratio < 2.0 then
+ return false, "[OutOfRange] too small ratio (min 2.0)"
+ end
+ if ratio > maxr then
+ return false, "[OutOfRange] too big ratio (max "..maxr..")"
+ end
+ return true, nil
+ end,
+}
+
+pardef.quietzone = {
+ -- It is recommended to use the largest possible quiet zone, that is
+ -- consistent with label or form design.
+ -- Quiet zones must be at least 10 times the module width or 0.10 inches,
+ -- whichever is larger. Default value (100 mils)
+ default = 2.54 * 186467, -- 0.1 inches equal to 100*mils
+ unit = "sp", -- scaled point
+ isReserved = false,
+ order = 3,
+ fncheck = function (self, qz, tparcheck) --> boolean, err
+ local mils = 0.0254 * 186467
+ local mod = tparcheck.module
+ local min = math.max(10*mod, 100*mils)
+ if qz >= min then
+ return true, nil
+ end
+ return false, "[OutOfRange] quietzone too small"
+ end,
+ fndefault = function(self, tck) --> default value respect to a set of param
+ local mod = tck.module
+ return math.max(10*mod, self.default)
+ end,
+}
+
+pardef.interspace = { -- Intercharacter gap
+ -- The intercharacter gap width (igw) is 5.3 times the module width (mw) if
+ -- mw is less than 10 mils. If mw is 10 mils or greater, the value for igw
+ -- is 3mw or 53 mils, whichever is greater. However, for quality printers,
+ -- igw often equals mw.
+ default = 7.5 * 0.0254 * 186467, -- 1 module, for quality printer
+ unit = "sp", -- scaled point
+ isReserved = false,
+ order = 4,
+ fncheck = function (self, igw, tparcheck) --> boolean, err
+ local mod = tparcheck.module
+ if igw >= mod then return true, nil end
+ return false, "[OutOfRange] interspace too small"
+ end,
+ fndefault = function(self, tck) --> default value respect to a set of param
+ return tck.module
+ end,
+}
+
+pardef.height = {
+ -- To enhance readability, it is recommended that the barcode be designed
+ -- to be as tall as possible, taking into consideration the aspects of label
+ -- and forms design.
+ -- The height should be at least 0.15 times the barcode's length or 0.25 inch.
+ default = 8 * 186467, -- 8 mm -- TODO: better assessment for symbol length
+ unit = "sp", -- scaled point
+ isReserved = false,
+ order = 5,
+ fncheck = function (self, h, _) --> boolean, err
+ local mils = 0.0254 * 186467
+ if h >= 250*mils then return true, nil end
+ return false, "[OutOfRange] height too small"
+ end,
+}
+
+-- text yes or not
+pardef.text_enabled = { -- boolean type
+ -- enable/disable a text label upon the barcode symbol
+ default = true,
+ isReserved = false,
+ order = 6,
+ fncheck = function (self, flag, _) --> boolean, err
+ if type(flag) == "boolean" then
+ return true, nil
+ else
+ return false, "[TypeErr] not a boolean value"
+ end
+ end,
+}
+
+-- define the text label vertical position
+pardef.text_vpos = { -- enumeration
+ default = "bottom",
+ isReserved = false,
+ order = 7,
+ policy_enum = {
+ top = true, -- place text at the top of symbol
+ bottom = true, -- place text under the symbol
+ },
+ fncheck = function (self, e, _) --> boolean, err
+ if type(e) ~= "string" then return false, "[TypeError] not a string" end
+ local keys = self.policy_enum
+ if keys[e] == true then
+ return true, nil
+ else
+ return false, "[Err] enumeration value '"..e.."' not found"
+ end
+ end,
+}
+
+-- define the text label horizontal position
+pardef.text_hpos = { -- enumeration
+ default = "left",
+ isReserved = false,
+ order = 8,
+ policy_enum = {
+ left = true,
+ center = true,
+ right = true,
+ spaced = true,
+ },
+ fncheck = function (self, e, _) --> boolean, err
+ if type(e) ~= "string" then return false, "[TypeError] not a string" end
+ local keys = self.policy_enum
+ if keys[e] == true then
+ return true, nil
+ else
+ return false, "[Err] enumeration value '"..e.."' not found"
+ end
+ end,
+}
+
+-- vertical dimension between symbol and text
+pardef.text_gap = {
+ default = 2.2 * 65536, -- 2.2 pt
+ isReserved = false,
+ order = 9,
+ unit = "em", --> TODO: please put this under further analisys asap
+ fncheck = function(self, g, _) --> boolean, err
+ if type(g) == "number" then
+ if g > 0 then
+ return true, nil
+ else
+ return false, "[OptErr] negative number"
+ end
+ else
+ return false, "[TypeErr] not valid type for gap opt"
+ end
+ end,
+}
+
+-- star character appearing
+pardef.text_star = {
+ default = false,
+ isReserved = false,
+ order = 10,
+ fncheck = function(self, flag, _) --> boolean, err
+ if type(flag) == "boolean" then
+ return true, nil
+ else
+ return false, "[TypeErr] not a boolean value"
+ end
+ end,
+}
+
+-- configuration function
+function Code39:config() --> ok, err
+ -- build Vbar object for the start/stop symbol
+ local mod, ratio = self.module, self.ratio
+ local n_star = self._star_def
+ local Vbar = self._libgeo.Vbar -- Vbar class
+ self._vbar = {['*'] = Vbar:from_int_revpair(n_star, mod, mod*ratio)}
+ return true, nil
+end
+
+-- overriding Barcode method
+function Code39:_check_char(c) --> elem, err
+ if type(c) ~= "string" or #c ~= 1 then
+ return nil, "[InternalErr] invalid char"
+ end
+ local symb_def = self._symb_def
+ local n = symb_def[c]
+ if not n then
+ local fmt = "[ArgErr] '%s' is not a valid Code 39 symbol"
+ return nil, string.format(fmt, c)
+ end
+ return c, nil
+end
+
+-- overriding Barcode method
+function Code39:_check_digit(n) --> elem, err
+ if type(n) ~= "number" then
+ return nil, "[InteranlErr] not a number"
+ end
+ if n < 0 or n > 9 then
+ return nil, "[InternalErr] not a digit"
+ end
+ return tostring(n), nil
+end
+
+function Code39:_finalize() --> ok, err
+ local v = assert(self._code_data, "[InternalErr] '_code_data' field is nil")
+ local vbar = self._vbar
+ local g_Vbar = self._libgeo.Vbar
+ local mod, ratio = self.module, self.ratio
+ local symb_def = self._symb_def
+ for _, c in ipairs(v) do
+ if not vbar[c] then
+ local n1 = symb_def[c]
+ vbar[c] = g_Vbar:from_int_revpair(n1, mod, mod*ratio)
+ end
+ end
+ return true, nil
+end
+
+-- tx, ty is an optional translator vector
+function Code39:append_ga(canvas, tx, ty) --> canvas
+ local code = self._code_data
+ local ns = self._code_len -- number of chars inside the symbol
+ local mod = self.module
+ local ratio = self.ratio
+ local interspace = self.interspace
+ local h = self.height
+ local xs = mod*(6 + 3*ratio)
+ local xgap = xs + interspace
+ local w = xgap*(ns + 1) + xs -- (ns + 2)*xgap - interspace
+ local ax, ay = self.ax, self.ay
+ local x0 = (tx or 0) - ax * w
+ local y0 = (ty or 0) - ay * h
+ local x1 = x0 + w
+ local y1 = y0 + h
+ local xpos = x0
+ local err
+ err = canvas:start_bbox_group()
+ assert(not err, err)
+ local vbar = self._vbar
+ -- start/stop symbol
+ local term_vbar = vbar['*']
+ -- draw start symbol
+ err = canvas:encode_Vbar(term_vbar, xpos, y0, y1)
+ assert(not err, err)
+ for _, c in ipairs(code) do -- draw code symbols
+ xpos = xpos + xgap
+ local vb = vbar[c]
+ err = canvas:encode_Vbar(vb, xpos, y0, y1)
+ assert(not err, err)
+ end
+ -- draw stop symbol
+ err = canvas:encode_Vbar(term_vbar, xpos + xgap, y0, y1)
+ assert(not err, err)
+ -- bounding box setting
+ local qz = self.quietzone
+ err = canvas:stop_bbox_group(x0 - qz, y0, x1 + qz, y1)
+ assert(not err, err)
+ -- check height as the minimum of 15% of length
+ -- TODO: message could warn the user
+ -- if 0.15 * w > h then
+ -- message("The height of the barcode is to small")
+ -- end
+ if self.text_enabled then -- human readable text
+ local chars; if self.text_star then
+ chars = {"*"}
+ for _, c in ipairs(code) do
+ chars[#chars + 1] = c
+ end
+ chars[#chars + 1] = "*"
+ else
+ chars = {}
+ for _, c in ipairs(code) do
+ chars[#chars + 1] = c
+ end
+ end
+ local Text = self._libgeo.Text
+ local txt = Text:from_chars(chars)
+ -- setup text position
+ local txt_vpos = self.text_vpos
+ local txt_gap = self.text_gap
+ local ypos, tay; if txt_vpos == "top" then -- vertical setup
+ ypos = y1 + txt_gap
+ tay = 0.0
+ elseif txt_vpos == "bottom" then
+ ypos = y0 - txt_gap
+ tay = 1.0
+ else
+ error("[IntenalErr] text vertical placement option is wrong")
+ end
+ local txt_hpos = self.text_hpos
+ if txt_hpos == "spaced" then -- horizontal setup
+ local xaxis = x0
+ if not self.text_star then
+ xaxis = xaxis + xgap
+ end
+ xaxis = xaxis + xs/2
+ err = canvas:encode_Text_xspaced(txt, xaxis, xgap, ypos, tay)
+ assert(not err, err)
+ else
+ local xpos, tax
+ if txt_hpos == "left" then
+ xpos = x0
+ tax = 0.0
+ elseif txt_hpos == "center" then
+ xpos = (x1 + x0)/2
+ tax = 0.5
+ elseif txt_hpos == "right" then
+ xpos = x1
+ tax = 1.0
+ else
+ error("[InternalErr] wrong option for text_pos")
+ end
+ err = canvas:encode_Text(txt, xpos, ypos, tax, tay)
+ assert(not err, err)
+ end
+ end
+ return canvas
+end
+
+return Code39
+-- \ No newline at end of file
diff --git a/macros/luatex/generic/barracuda/lib/lib-barcode/ean.lua b/macros/luatex/generic/barracuda/lib/lib-barcode/ean.lua
new file mode 100644
index 0000000000..6893550a5c
--- /dev/null
+++ b/macros/luatex/generic/barracuda/lib/lib-barcode/ean.lua
@@ -0,0 +1,790 @@
+-- EAN family barcode generator
+--
+-- Copyright (C) 2019 Roberto Giacomelli
+-- see LICENSE.txt file
+--
+-- variant identifiers of the EAN family barcodes:
+-- "13" EAN13
+-- "8" EAN8
+-- "5" EAN5 add-on
+-- "2" EAN2 add-on
+-- "13+5" EAN13 with EAN5 add-on
+-- "13+2" EAN13 with EAN2 add-on
+-- "8+5" EAN8 with EAN5 add-on
+-- "8+2" EAN8 with EAN2 add-on
+
+local EAN = {
+ _VERSION = "ean v0.0.5",
+ _NAME = "ean",
+ _DESCRIPTION = "EAN barcode encoder",
+}
+
+EAN._codeset_seq = {-- 1 -> A, 2 -> B, 3 -> C
+[0]={1, 1, 1, 1, 1, 1, 3, 3, 3, 3, 3, 3},
+ {1, 1, 2, 1, 2, 2, 3, 3, 3, 3, 3, 3},
+ {1, 1, 2, 2, 1, 2, 3, 3, 3, 3, 3, 3},
+ {1, 1, 2, 2, 2, 1, 3, 3, 3, 3, 3, 3},
+ {1, 2, 1, 1, 2, 2, 3, 3, 3, 3, 3, 3},
+ {1, 2, 2, 1, 1, 2, 3, 3, 3, 3, 3, 3},
+ {1, 2, 2, 2, 1, 1, 3, 3, 3, 3, 3, 3},
+ {1, 2, 1, 2, 1, 2, 3, 3, 3, 3, 3, 3},
+ {1, 2, 1, 2, 2, 1, 3, 3, 3, 3, 3, 3},
+ {1, 2, 2, 1, 2, 1, 3, 3, 3, 3, 3, 3},
+}
+
+EAN._codeset14_8 = 1
+EAN._codeset58_8 = 3
+
+EAN._codeset_5 = { -- check digit => structure
+[0]={2, 2, 1, 1, 1}, -- 0 GGLLL
+ {2, 1, 2, 1, 1}, -- 1 GLGLL
+ {2, 1, 1, 2, 1}, -- 2 GLLGL
+ {2, 1, 1, 1, 2}, -- 3 GLLLG
+ {1, 2, 2, 1, 1}, -- 4 LGGLL
+ {1, 1, 2, 2, 1}, -- 5 LLGGL
+ {1, 1, 1, 2, 2}, -- 6 LLLGG
+ {1, 2, 1, 2, 1}, -- 7 LGLGL
+ {1, 2, 1, 1, 2}, -- 8 LGLLG
+ {1, 1, 2, 1, 2}, -- 9 LLGLG
+}
+
+EAN._symbol = { -- codesets A, B, and C
+ [1] = {[0] = 3211, 2221, 2122, 1411, 1132, 1231, 1114, 1312, 1213, 3112,},
+ [2] = {[0] = 1123, 1222, 2212, 1141, 2311, 1321, 4111, 2131, 3121, 2113,},
+ [3] = {[0] = 3211, 2221, 2122, 1411, 1132, 1231, 1114, 1312, 1213, 3112,},
+}
+
+EAN._is_first_bar = {false, false, true}
+EAN._start = {111, true}
+EAN._stop = {11111, false}
+
+-- family parameters
+
+EAN._par_def = {}; local pardef = EAN._par_def
+
+-- standard module is 0.33 mm but it can vary from 0.264 to 0.66mm
+pardef.mod = {
+ default = 0.33 * 186467, -- (mm to sp) X dimension (original value 0.33)
+ unit = "sp", -- scaled point
+ isReserved = true,
+ order = 1, -- the one first to be modified
+ fncheck = function (self, x, _) --> boolean, err
+ local mm = 186467
+ local min, max = 0.264 * mm, 0.660 * mm
+ if x < min then
+ return false, "[OutOfRange] too small value for mod"
+ elseif x > max then
+ return false, "[OutOfRange] too big value for mod"
+ end
+ return true, nil
+ end,
+}
+
+pardef.height = {
+ default = 22.85 * 186467, -- 22.85 mm
+ unit = "sp",
+ isReserved = false,
+ order = 2,
+ fncheck = function (self, h, _) --> boolean, err
+ if h > 0 then
+ return true, nil
+ else
+ return false, "[OutOfRange] non positive value for height"
+ end
+ end,
+}
+
+pardef.quietzone_left_factor = {
+ default = 11,
+ unit = "absolute-number",
+ isReserved = false,
+ order = 3,
+ fncheck = function (self, qzf, _) --> boolean, err
+ if qzf > 0 then
+ return true, nil
+ else
+ return false, "[OutOfRange] non positive value for quietzone_left_factor"
+ end
+ end,
+}
+
+pardef.quietzone_right_factor = {
+ default = 7,
+ unit = "absolute-number",
+ isReserved = false,
+ order = 4,
+ fncheck = function (self, qzf, _) --> boolean, err
+ if qzf > 0 then
+ return true, nil
+ else
+ return false, "[OutOfRange] non positive value for quietzone_right_factor"
+ end
+ end,
+}
+
+pardef.bars_depth_factor = {
+ default = 5,
+ unit = "absolute-number",
+ isReserved = false,
+ order = 5,
+ fncheck = function (self, b, _) --> boolean, err
+ if b >= 0 then
+ return true, nil
+ else
+ return false, "[OutOfRange] non positive value for bars_depth_factor"
+ end
+ end,
+}
+
+-- enable/disable a text label upon the barcode symbol
+pardef.text_enabled = { -- boolean type
+ default = true,
+ isReserved = false,
+ order = 6,
+ fncheck = function (self, flag, _) --> boolean, err
+ if type(flag) == "boolean" then
+ return true, nil
+ else
+ return false, "[TypeErr] not a boolean value for text_enabled"
+ end
+ end,
+}
+
+pardef.text_ygap_factor = {
+ default = 1.0,
+ unit = "absolute-number",
+ isReserved = false,
+ order = 7,
+ fncheck = function (self, t, _) --> boolean, err
+ if t >= 0 then
+ return true, nil
+ else
+ return false, "[OutOfRange] non positive value for text_ygap_factor"
+ end
+ end,
+}
+
+pardef.text_xgap_factor = {
+ default = 0.75,
+ unit = "absolute-number",
+ isReserved = false,
+ order = 8,
+ fncheck = function (self, t, _) --> boolean, err
+ if t >= 0 then
+ return true, nil
+ else
+ return false, "[OutOfRange] non positive value for text_xgap_factor"
+ end
+ end,
+}
+
+-- variant parameters
+
+EAN._par_def_variant = {
+ ["13"] = {}, -- EAN13
+ ["8"] = {}, -- EAN8
+ ["5"] = {}, -- add-on EAN5
+ ["2"] = {}, -- add-on EAN2
+ ["13+5"] = {}, -- EAN13 with EAN5 add-on
+ ["13+2"] = {}, -- EAN13 with EAN2 add-on
+ ["8+5"] = {}, -- EAN8 with EAN5 add-on
+ ["8+2"] = {}, -- EAN8 with EAN2 add-on
+}
+
+-- ean13_pardef.text_ISBN parameter -- TODO:
+
+-- EAN 13/8 + add-on parameters
+local addon_xgap_factor = {-- distance between symbol
+ default = 10,
+ unit = "absolute-number",
+ isReserved = false,
+ order = 1,
+ fncheck = function (self, t, _) --> boolean, err
+ if t >= 7 then
+ return true, nil
+ else
+ return false, "[OutOfRange] not positive value for xgap_factor"
+ end
+ end,
+}
+EAN._par_def_variant["13+5"].addon_xgap_factor = addon_xgap_factor
+EAN._par_def_variant["13+2"].addon_xgap_factor = addon_xgap_factor
+EAN._par_def_variant["8+5"].addon_xgap_factor = addon_xgap_factor
+EAN._par_def_variant["8+2"].addon_xgap_factor = addon_xgap_factor
+
+-- configuration functions
+
+-- utility for generic configuration of full symbol, add-on included
+-- n1 length of main symbol, 8 or 13
+-- n2 length of add-on symbol, 2 or 5
+local function config_full(ean, Vbar, mod, n1, n2)
+ local i1 = tostring(n1)
+ local fn_1 = assert(ean._config_variant[i1])
+ fn_1(ean, Vbar, mod)
+ local i2 = tostring(n2)
+ local fn_2 = assert(ean._config_variant[i2])
+ fn_2(ean, Vbar, mod)
+ ean._main_len = n1
+ ean._addon_len = n2
+ ean._is_last_checksum = true
+end
+
+EAN._config_variant = {
+ ["13"] = function (ean13, Vbar, mod)
+ ean13._main_len = 13
+ ean13._is_last_checksum = true
+ local start = ean13._start
+ local stop = ean13._stop
+ ean13._13_start_stop_vbar = Vbar:from_int(start[1], mod, start[2])
+ ean13._13_ctrl_center_vbar = Vbar:from_int(stop[1], mod, stop[2])
+ ean13._13_codeset_vbar = {}
+ local tvbar = ean13._13_codeset_vbar
+ for i_cs, codetab in ipairs(ean13._symbol) do
+ tvbar[i_cs] = {}
+ local tv = tvbar[i_cs]
+ local isbar = ean13._is_first_bar[i_cs]
+ for i = 0, 9 do
+ tv[i] = Vbar:from_int(codetab[i], mod, isbar)
+ end
+ end
+ end,
+ ["8"] = function (ean8, Vbar, mod)
+ ean8._main_len = 8
+ ean8._is_last_checksum = true
+ local start = ean8._start
+ local stop = ean8._stop
+ ean8._8_start_stop_vbar = Vbar:from_int(start[1], mod, start[2])
+ ean8._8_ctrl_center_vbar = Vbar:from_int(stop[1], mod, stop[2])
+ ean8._8_codeset_vbar = {}
+ local tvbar = ean8._8_codeset_vbar
+ for k = 1, 3, 2 do -- only codeset A and C (k == 1, 3)
+ tvbar[k] = {}
+ local codetab = ean8._symbol[k]
+ local isbar = ean8._is_first_bar[k]
+ local tv = tvbar[k]
+ for i = 0, 9 do
+ tv[i] = Vbar:from_int(codetab[i], mod, isbar)
+ end
+ end
+ end,
+ ["5"] = function (ean5, Vbar, mod) -- add-on EAN5
+ ean5._main_len = 5
+ ean5._is_last_checksum = false
+ ean5._5_start_vbar = Vbar:from_int(112, mod, true)
+ ean5._5_sep_vbar = Vbar:from_int(11, mod, false)
+ ean5._5_codeset_vbar = {}
+ local tvbar = ean5._5_codeset_vbar
+ local symbols = ean5._symbol
+ for c = 1, 2 do
+ tvbar[c] = {}
+ local tcs = tvbar[c]
+ local isbar = false
+ local sb = symbols[c]
+ for i = 0, 9 do
+ tcs[i] = Vbar:from_int(sb[i], mod, false)
+ end
+ end
+ end,
+ ["2"] = function (ean2, Vbar, mod) -- add-on EAN2
+ ean2._main_len = 2
+ ean2._is_last_checksum = false
+ ean2._2_start_vbar = Vbar:from_int(112, mod, true)
+ ean2._2_sep_vbar = Vbar:from_int(11, mod, false)
+ ean2._2_codeset_vbar = {}
+ local tvbar = ean2._2_codeset_vbar
+ local symbols = ean2._symbol
+ for c = 1, 2 do
+ tvbar[c] = {}
+ local tcs = tvbar[c]
+ local isbar = false
+ local sb = symbols[c]
+ for i = 0, 9 do
+ tcs[i] = Vbar:from_int(sb[i], mod, false)
+ end
+ end
+ end,
+ ["13+5"] = function (ean, Vbar, mod) -- EAN13 with EAN5 add-on
+ config_full(ean, Vbar, mod, 13, 5)
+ end,
+ ["13+2"] = function(ean, Vbar, mod) -- EAN13 with EAN2 add-on
+ config_full(ean, Vbar, mod, 13, 2)
+ end,
+ ["8+5"] = function(ean, Vbar, mod) -- EAN8 with EAN5 add-on
+ config_full(ean, Vbar, mod, 8, 5)
+ end,
+ ["8+2"] = function(ean, Vbar, mod) -- EAN8 with EAN2 add-on
+ config_full(ean, Vbar, mod, 8, 2)
+ end,
+}
+
+-- config function
+-- create all the possible VBar object
+function EAN:config(variant) --> ok, err
+ if not type(variant) == "string" then
+ return false, "[ArgErr] incorrect type for 'variant', string expected"
+ end
+ local fnconfig = self._config_variant[variant]
+ if not fnconfig then
+ return false, "[Err] EAN variant '".. variant .."' not found"
+ end
+ local Vbar = self._libgeo.Vbar -- Vbar class
+ local mod = self.mod
+ fnconfig(self, Vbar, mod)
+ return true, nil
+end
+
+-- utility function
+
+-- the checksum of EAN8 or EAN13 code
+-- 'data' is an array of digits
+local function checksum_8_13(data, stop_index)
+ local s1 = 0; for i = 2, stop_index, 2 do
+ s1 = s1 + data[i]
+ end
+ local s2 = 0; for i = 1, stop_index, 2 do
+ s2 = s2 + data[i]
+ end
+ local sum; if stop_index % 2 == 0 then
+ sum = 3 * s1 + s2
+ else
+ sum = s1 + 3 * s2
+ end
+ return (10 - (sum % 10)) % 10
+end
+
+-- return the checksum digit of an EAN 5 or EAN 2 add-on
+-- this digits will not be part of the code
+-- 'data' is an array of digits
+-- i is the index where the code starts
+-- len is the length of the code
+local function checksum_5_2(data, i, len) --> checksum digit or nil
+ if len == 5 then -- EAN 5 add-on
+ local c1 = data[i] + data[i + 2] + data[i + 4]
+ local c2 = data[i + 1] + data[i + 3]
+ local ck = 3 * c1 + 9 * c2
+ return ck % 10
+ elseif len == 2 then -- EAN 2 add-on
+ local ck = 10 * data[i] + data[i + 1]
+ return ck % 4
+ end
+end
+
+-- public methods
+
+-- return the checksum digit of the argument or an error
+-- respect to the encoder variant EAN8 or EAN13
+-- 'n' can be an integer, a string of digits or an array of digits
+function EAN:checksum(n) --> n, err
+ local arr;
+ if type(n) == "number" then
+ if n <= 0 then
+ return nil, "[ArgErr] number must be a positive integer"
+ end
+ if n - math.floor(n) > 0 then
+ return nil, "[ArgErr] 'n' is not an integer"
+ end
+ arr = {}
+ local i = 0
+ while n > 0 do
+ i = i + 1
+ arr[i] = n % 10
+ n = (n - arr[i]) / 10
+ end
+ -- array reversing
+ local len = #arr + 1
+ for i = 1, #arr/2 do
+ local dt = arr[i]
+ arr[i] = arr[len - i]
+ arr[len - i] = dt
+ end
+ elseif type(n) == "table" then
+ if not #n > 0 then return nil, "[ArgErr] empty array" end
+ for _, d in ipairs(n) do
+ if type(d) ~= "number" then
+ return nil, "[ArgErr] array 'n' contains a not digit element"
+ end
+ if d < 0 or d > 9 then
+ return nil, "[ArgErr] array contains a not digit number"
+ end
+ end
+ arr = n
+ elseif type(n) == "string" then
+ arr = {}
+ for c in string.gmatch(n, ".") do
+ local d = tonumber(c)
+ if (not d) or d > 9 then
+ return nil, "[ArgErr] 's' contains a not digit char"
+ end
+ arr[#arr + 1] = d
+ end
+ else
+ return nil, "[ArgErr] not a number, a string or an array of digits"
+ end
+ local i = #arr
+ if i == 7 or i == 8 then
+ return checksum_8_13(arr, 7)
+ elseif i == 12 or i == 13 then
+ return checksum_8_13(arr, 12)
+ else
+ return nil, "[Err] unsuitable data length for EAN8 or EAN13 checksum"
+ end
+end
+
+function EAN:get_code() --> string
+ local var = self._variant
+ local code = self.code13 -- TODO:
+ return table.concat(code)
+end
+
+-- internal methods for Barcode costructors
+
+function EAN:_finalize() --> ok, err
+ local l1 = self._main_len
+ local l2 = self._addon_len
+ local ok_len = l1 + (l2 or 0)
+ local symb_len = self._code_len
+ if symb_len ~= ok_len then
+ return false, "[ArgErr] not a "..ok_len.."-digits long array"
+ end
+ if self._is_last_checksum then -- is the last digit ok?
+ local data = self._code_data
+ local ck = checksum_8_13(data, l1 - 1)
+ if ck ~= data[l1] then
+ return false, "[Err] wrong checksum digit"
+ end
+ end
+ return true, nil
+end
+
+-- drawing functions
+
+EAN._append_ga_variant = {}
+local fn_append_ga_variant = EAN._append_ga_variant
+
+-- draw EAN13 symbol
+fn_append_ga_variant["13"] = function (ean, canvas, tx, ty, ax, ay)
+ local code = ean._code_data
+ local mod = ean.mod
+ local bars_depth = mod * ean.bars_depth_factor
+ local w, h = 95*mod, ean.height + bars_depth
+ local x0 = (tx or 0) - ax * w
+ local y0 = (ty or 0) - ay * h
+ local x1 = x0 + w
+ local y1 = y0 + h
+ local xpos = x0 -- current insertion x-coord
+ local ys = y0 + bars_depth
+ local s_width = 7*mod
+ local code_seq = ean._codeset_seq[code[1]]
+ -- draw the start symbol
+ local err
+ err = canvas:start_bbox_group(); assert(not err, err)
+ local be = ean._13_start_stop_vbar
+ err = canvas:encode_Vbar(be, xpos, y0, y1); assert(not err, err)
+ xpos = xpos + 3*mod
+ -- draw the first 6 numbers
+ for i = 2, 7 do
+ local codeset = code_seq[i-1]
+ local n = code[i]
+ local vbar = ean._13_codeset_vbar[codeset][n]
+ err = canvas:encode_Vbar(vbar, xpos, ys, y1); assert(not err, err)
+ xpos = xpos + s_width
+ end
+ -- draw the control symbol
+ local ctrl = ean._13_ctrl_center_vbar
+ err = canvas:encode_Vbar(ctrl, xpos, y0, y1); assert(not err, err)
+ xpos = xpos + 5*mod
+ -- draw the last 6 numbers
+ for i = 8, 13 do
+ local codeset = code_seq[i-1]
+ local n = code[i]
+ local vbar = ean._13_codeset_vbar[codeset][n]
+ err = canvas:encode_Vbar(vbar, xpos, ys, y1); assert(not err, err)
+ xpos = xpos + s_width
+ end
+ -- draw the stop char
+ err = canvas:encode_Vbar(be, xpos, y0, y1); assert(not err, err)
+ -- bounding box set up
+ local qzl = ean.quietzone_left_factor * mod
+ local qzr = ean.quietzone_right_factor * mod
+ local err = canvas:stop_bbox_group(x0 - qzl, y0, x1 + qzr, y1)
+ assert(not err, err)
+ if ean.text_enabled then -- human readable text
+ local Text = ean._libgeo.Text
+ local txt_1 = Text:from_digit_array(code, 1, 1)
+ local txt_2 = Text:from_digit_array(code, 2, 7)
+ local txt_3 = Text:from_digit_array(code, 8, 13)
+ local y_bl = ys - ean.text_ygap_factor * mod
+ local mx = ean.text_xgap_factor
+ local err
+ err = canvas:encode_Text(txt_1, x0 - qzl, y_bl, 0, 1)
+ assert(not err, err)
+ local x2_1 = x0 + (3+mx)*mod
+ local x2_2 = x0 + (46-mx)*mod
+ err = canvas:encode_Text_xwidth(txt_2, x2_1, x2_2, y_bl, 1)
+ assert(not err, err)
+ local x3_1 = x0 + (49+mx)*mod
+ local x3_2 = x0 + (92-mx)*mod
+ err = canvas:encode_Text_xwidth(txt_3, x3_1, x3_2, y_bl, 1)
+ assert(not err, err)
+ end
+end
+
+-- draw EAN8 symbol
+fn_append_ga_variant["8"] = function (ean, canvas, tx, ty, ax, ay)
+ local code = ean._code_data
+ local mod = ean.mod
+ local bars_depth = mod * ean.bars_depth_factor
+ local w, h = 67*mod, ean.height + bars_depth
+ local x0 = (tx or 0) - ax * w
+ local y0 = (ty or 0) - ay * h
+ local x1 = x0 + w
+ local y1 = y0 + h
+ local xpos = x0 -- current insertion x-coord
+ local ys = y0 + bars_depth
+ local s_width = 7*mod
+ -- draw the start symbol
+ local err
+ err = canvas:start_bbox_group(); assert(not err, err)
+ local be = ean._8_start_stop_vbar
+ err = canvas:encode_Vbar(be, xpos, y0, y1); assert(not err, err)
+ xpos = xpos + 3*mod
+ -- draw the first 4 numbers
+ local t_vbar = ean._8_codeset_vbar
+ local cs14 = ean._codeset14_8
+ for i = 1, 4 do
+ local n = code[i]
+ local vbar = t_vbar[cs14][n]
+ err = canvas:encode_Vbar(vbar, xpos, ys, y1); assert(not err, err)
+ xpos = xpos + s_width
+ end
+ -- draw the control symbol
+ local ctrl = ean._8_ctrl_center_vbar
+ err = canvas:encode_Vbar(ctrl, xpos, y0, y1); assert(not err, err)
+ xpos = xpos + 5*mod
+ -- draw the product code
+ local cs58 = ean._codeset58_8
+ for i = 5, 8 do
+ local n = code[i]
+ local vbar = t_vbar[cs58][n]
+ err = canvas:encode_Vbar(vbar, xpos, ys, y1); assert(not err, err)
+ xpos = xpos + s_width
+ end
+ -- draw the stop char
+ err = canvas:encode_Vbar(be, xpos, y0, y1); assert(not err, err)
+ -- bounding box set up
+ local qzl = ean.quietzone_left_factor * mod
+ local qzr = ean.quietzone_right_factor * mod
+ local err = canvas:stop_bbox_group(x0 - qzl, y0, x1 + qzr, y1)
+ assert(not err, err)
+ if ean.text_enabled then -- human readable text
+ local Text = ean._libgeo.Text
+ local t_1 = Text:from_digit_array(code, 1, 4)
+ local t_2 = Text:from_digit_array(code, 5, 8)
+ local y_bl = ys - ean.text_ygap_factor * mod
+ local mx = ean.text_xgap_factor
+ local x1_1 = x0 + ( 3 + mx)*mod
+ local x1_2 = x0 + (32 - mx)*mod
+ err = canvas:encode_Text_xwidth(t_1, x1_1, x1_2, y_bl, 1)
+ assert(not err, err)
+ local x2_1 = x0 + (35+mx)*mod
+ local x2_2 = x0 + (64-mx)*mod
+ err = canvas:encode_Text_xwidth(t_2, x2_1, x2_2, y_bl, 1)
+ assert(not err, err)
+ end
+end
+
+-- draw EAN5 add-on symbol
+fn_append_ga_variant["5"] = function (ean, canvas, tx, ty, ax, ay, h)
+ local code = ean._code_data
+ local l1 = ean._main_len
+ local i1; if l1 == 5 then
+ i1 = 1
+ else
+ i1 = l1 + 1
+ end
+ local i2 = i1 + 4
+ local mod = ean.mod
+ local w = 47*mod
+ h = h or ean.height
+ local x0 = (tx or 0) - ax * w
+ local y0 = (ty or 0) - ay * h
+ local x1 = x0 + w
+ local y1 = y0 + h
+ local xpos = x0 -- current insertion x-coord
+ local sym_w = 7*mod
+ local sep_w = 2*mod
+ -- draw the start symbol
+ local err
+ err = canvas:start_bbox_group(); assert(not err, err)
+ local start = ean._5_start_vbar
+ err = canvas:encode_Vbar(start, xpos, y0, y1); assert(not err, err)
+ xpos = xpos + 4*mod
+ local ck = checksum_5_2(code, i1, 5)
+ local codeset = ean._codeset_5[ck]
+ local sep = ean._5_sep_vbar
+ local t_vbar = ean._5_codeset_vbar
+ -- draw the five digits
+ local k = 0
+ for i = i1, i2 do
+ k = k + 1
+ local cs = codeset[k] -- 1 or 2
+ local d = code[i]
+ local vbar = t_vbar[cs][d]
+ err = canvas:encode_Vbar(vbar, xpos, y0, y1); assert(not err, err)
+ xpos = xpos + sym_w
+ if k < 5 then
+ err = canvas:encode_Vbar(sep, xpos, y0, y1); assert(not err, err)
+ xpos = xpos + sep_w
+ end
+ end
+ -- bounding box set up
+ local qzl = ean.quietzone_left_factor * mod
+ local qzr = ean.quietzone_right_factor * mod
+ local err = canvas:stop_bbox_group(x0 - qzl, y0, x1 + qzr, y1)
+ assert(not err, err)
+ if ean.text_enabled then -- human readable text
+ local Text = ean._libgeo.Text
+ local txt = Text:from_digit_array(code, i1, i2)
+ local y_bl = y1 + ean.text_ygap_factor * mod
+ local x1_1 = x0 + 3*mod
+ local x1_2 = x1 - 3*mod
+ err = canvas:encode_Text_xwidth(txt, x1_1, x1_2, y_bl, 0)
+ assert(not err, err)
+ end
+end
+
+-- draw EAN2 symbol
+fn_append_ga_variant["2"] = function (ean, canvas, tx, ty, ax, ay, h)
+ local code = ean._code_data
+ local l1 = ean._main_len
+ local i1; if l1 == 2 then
+ i1 = 1
+ else
+ i1 = l1 + 1
+ end
+ local mod = ean.mod
+ local w = 20*mod
+ h = h or ean.height
+ local x0 = (tx or 0.0) - ax * w
+ local y0 = (ty or 0.0) - ay * h
+ local x1 = x0 + w
+ local y1 = y0 + h
+ local xpos = x0 -- current insertion x-coord
+ local sym_w = 7*mod
+ local sep_w = 2*mod
+ -- draw the start symbol
+ local err
+ err = canvas:start_bbox_group(); assert(not err, err)
+ local start = ean._2_start_vbar
+ err = canvas:encode_Vbar(start, xpos, y0, y1); assert(not err, err)
+ xpos = xpos + 4*mod
+ local r = checksum_5_2(code, i1, 2)
+ local s1, s2
+ if r == 0 then -- LL scheme
+ s1, s2 = 1, 1
+ elseif r == 1 then -- LG scheme
+ s1, s2 = 1, 2
+ elseif r == 2 then -- GL scheme
+ s1, s2 = 2, 1
+ else -- r == 3 -- GG scheme
+ s1, s2 = 2, 2
+ end
+ local t_vbar = ean._2_codeset_vbar
+ local d1 = code[i1] -- render the first digit
+ local vb1 = t_vbar[s1][d1]
+ err = canvas:encode_Vbar(vb1, xpos, y0, y1); assert(not err, err)
+ xpos = xpos + sym_w
+ local sep = ean._2_sep_vbar
+ err = canvas:encode_Vbar(sep, xpos, y0, y1); assert(not err, err)
+ xpos = xpos + sep_w
+ local d2 = code[i1 + 1] -- render the second digit
+ local vb2 = t_vbar[s2][d2]
+ err = canvas:encode_Vbar(vb2, xpos, y0, y1); assert(not err, err)
+ -- bounding box set up
+ local qzl = ean.quietzone_left_factor * mod
+ local qzr = ean.quietzone_right_factor * mod
+ local err = canvas:stop_bbox_group(x0 - qzl, y0, x1 + qzr, y1)
+ assert(not err, err)
+ if ean.text_enabled then -- human readable text
+ local Text = ean._libgeo.Text
+ local txt = Text:from_digit_array(code, i1, i1 + 1)
+ local y_bl = y1 + ean.text_ygap_factor * mod
+ local x1_1 = x0 + 3*mod
+ local x1_2 = x1 - 3*mod
+ err = canvas:encode_Text_xwidth(txt, x1_1, x1_2, y_bl, 0)
+ assert(not err, err)
+ end
+end
+
+fn_append_ga_variant["13+5"] = function (ean, canvas, tx, ty, ax, ay)
+ local mod = ean.mod
+ local bars_depth = mod * ean.bars_depth_factor
+ local h = ean.height + bars_depth
+ local w = (142 + ean.addon_xgap_factor) * mod
+ local x0 = (tx or 0) - ax * w
+ local y0 = (ty or 0) - ay * h
+ local x1 = x0 + w
+ local fn_ga = ean._append_ga_variant
+ local fn_1 = fn_ga["13"]
+ local fn_2 = fn_ga["5"]
+ fn_1(ean, canvas, x0, y0, 0, 0)
+ fn_2(ean, canvas, x1, y0, 1, 0, 0.85 * h)
+end
+
+fn_append_ga_variant["13+2"] = function (ean, canvas, tx, ty, ax, ay)
+ local mod = ean.mod
+ local bars_depth = mod * ean.bars_depth_factor
+ local h = ean.height + bars_depth
+ local w = (115 + ean.addon_xgap_factor) * mod
+ local x0 = (tx or 0) - ax * w
+ local y0 = (ty or 0) - ay * h
+ local x1 = x0 + w
+ local fn_ga = ean._append_ga_variant
+ local fn_1 = fn_ga["13"]
+ local fn_2 = fn_ga["2"]
+ fn_1(ean, canvas, x0, y0, 0, 0)
+ fn_2(ean, canvas, x1, y0, 1, 0, 0.85 * h)
+end
+
+fn_append_ga_variant["8+5"] = function (ean, canvas, tx, ty, ax, ay)
+ local mod = ean.mod
+ local bars_depth = mod * ean.bars_depth_factor
+ local h = ean.height + bars_depth
+ local w = (114 + ean.addon_xgap_factor) * mod
+ local x0 = (tx or 0) - ax * w
+ local y0 = (ty or 0) - ay * h
+ local x1 = x0 + w
+ local fn_ga = ean._append_ga_variant
+ local fn_1 = fn_ga["8"]
+ local fn_2 = fn_ga["5"]
+ fn_1(ean, canvas, x0, y0, 0, 0)
+ fn_2(ean, canvas, x1, y0, 1, 0, 0.85 * h)
+end
+
+fn_append_ga_variant["8+2"] = function (ean, canvas, tx, ty, ax, ay)
+ local mod = ean.mod
+ local bars_depth = mod * ean.bars_depth_factor
+ local h = ean.height + bars_depth
+ local w = (87 + ean.addon_xgap_factor) * mod
+ local x0 = (tx or 0) - ax * w
+ local y0 = (ty or 0) - ay * h
+ local x1 = x0 + w
+ local fn_ga = ean._append_ga_variant
+ local fn_1 = fn_ga["8"]
+ local fn_2 = fn_ga["2"]
+ fn_1(ean, canvas, x0, y0, 0, 0)
+ fn_2(ean, canvas, x1, y0, 1, 0, 0.85 * h)
+end
+
+-- Drawing into the provided channel geometrical data
+-- tx, ty is the optional translation vector
+-- the function return the canvas reference to allow call chaining
+function EAN:append_ga(canvas, tx, ty) --> canvas
+ local var = self._variant
+ local fn_append_ga = assert(self._append_ga_variant[var])
+ local ax, ay = self.ax, self.ay
+ fn_append_ga(self, canvas, tx, ty, ax, ay)
+ return canvas
+end
+
+return EAN
diff --git a/macros/luatex/generic/barracuda/lib/lib-barcode/i2of5.lua b/macros/luatex/generic/barracuda/lib/lib-barcode/i2of5.lua
new file mode 100644
index 0000000000..8097b855f7
--- /dev/null
+++ b/macros/luatex/generic/barracuda/lib/lib-barcode/i2of5.lua
@@ -0,0 +1,378 @@
+-- Interleaved 2 of 5 (ITF) barcode generator
+--
+-- Copyright (C) 2019 Roberto Giacomelli
+-- see LICENSE.txt file
+
+local ITF = { -- main container
+ _VERSION = "ITF v0.0.1",
+ _NAME = "ITF",
+ _DESCRIPTION = "Interleaved 2 of 5 barcode encoder",
+}
+
+ITF._start = 111 -- nnnn
+ITF._stop = 112 -- Wnn (integer must be in reverse order)
+
+ITF._pattern = { -- true -> narrow, false -> Wide
+ [0] = {true, true, false, false, true},
+ [1] = {false, true, true, true, false},
+ [2] = {true, false, true, true, false},
+ [3] = {false, false, true, true, true},
+ [4] = {true, true, false, true, false},
+ [5] = {false, true, false, true, true},
+ [6] = {true, false, false, true, true},
+ [7] = {true, true, true, false, false},
+ [8] = {false, true, true, false, true},
+ [9] = {true, false, true, false, true},
+}
+
+-- define parameters
+ITF._par_def = {}
+local pardef = ITF._par_def
+
+-- module main parameter
+pardef.module = {
+ -- Narrow element X-dimension is the width of the smallest element.
+ -- The module width (width of narrow element) should be at least 7.5 mils
+ -- that is exactly 0.1905mm (1 mil is 1/1000 inch).
+ default = 7.5 * 0.0254 * 186467, -- 7.5 mils (sp) unit misure,
+ unit = "sp", -- scaled point
+ isReserved = true,
+ order = 1, -- the one first to be modified
+ fncheck = function (self, mod, _) --> boolean, err
+ if mod >= self.default then return true, nil end
+ return false, "[OutOfRange] too small lenght for X-dim"
+ end,
+}
+
+pardef.ratio = {
+ -- The "wide" element is a multiple of the "narrow" element that can
+ -- range between 2.0 and 3.0. Preferred value is 3.0.
+ -- The multiple for the wide element should be between 2.0 and 3.0 if the
+ -- narrow element is greater than 20 mils. If the narrow element is less
+ -- than 20 mils (0.508mm), the ratio must exceed 2.2.
+ default = 3.0,
+ unit = "absolute-number",
+ isReserved = true,
+ order = 2,
+ fncheck = function (_, ratio, tpardef) --> boolean, err
+ local mils = 0.0254 * 186467
+ local mod = tpardef.module
+ local minr; if mod < 20*mils then minr = 2.2 else minr = 2.0 end
+ if ratio < minr then
+ return false, "[OutOfRange] too small ratio (the min is "..minr..")"
+ end
+ if ratio > 3.0 then
+ return false, "[OutOfRange] too big ratio (the max is 3.0)"
+ end
+ return true, nil
+ end,
+}
+
+pardef.height = {
+ -- The height should be at least 0.15 times the barcode's length or 0.25 inch.
+ default = 15 * 186467, -- 15mm -- TODO: better assessment for symbol length
+ unit = "sp", -- scaled point
+ isReserved = false,
+ order = 3,
+ fncheck = function (_self, h, _opt) --> boolean, err
+ local mils = 0.0254 * 186467
+ if h >= 250*mils then
+ return true, nil
+ end
+ return false, "[OutOfRange] height too small"
+ end,
+}
+
+pardef.quietzone = {
+ -- Quiet zones must be at least 10 times the module width or 0.25 inches,
+ -- whichever is larger
+ default = 250 * 0.0254 * 186467, -- 0.25 inch (250 mils)
+ unit = "sp", -- scaled point
+ isReserved = false,
+ order = 4,
+ fncheck = function (self, qz, _opt) --> boolean, err
+ local mils = 0.0254 * 186467
+ local mod = self.module
+ local min = math.max(10*mod, 250*mils)
+ if qz >= min then
+ return true, nil
+ else
+ return false, "[OutOfRange] quietzone too small"
+ end
+ end,
+}
+
+pardef.check_digit_policy = { -- enumeration
+ default = "none",
+ isReserved = false,
+ policy_enum = {
+ add = true, -- add a check digit to the symbol
+ verify = true, -- check the last digit of the symbol as check digit
+ none = true, -- do nothing
+ },
+ order = 5,
+ fncheck = function (self, e, _) --> boolean, err
+ if type(e) ~= "string" then return false, "[TypeError] not a string" end
+ local keys = self.policy_enum
+ if keys[e] == true then
+ return true, nil
+ else
+ return false, "[Err] enum value not found"
+ end
+ end,
+}
+
+pardef.check_digit_method = { -- enumeration
+ -- determine the algorithm for the check digit calculation
+ default = "mod_10",
+ isReserved = false,
+ order = 6,
+ method_enum = {
+ mod_10 = true, -- MOD 10 check digits method
+ },
+ fncheck = function (self, e, _) --> boolean, err
+ if type(e) ~= "string" then return false, "[TypeError] not a string" end
+ local keys = self.method_enum
+ if keys[e] == true then
+ return true, nil
+ else
+ return false, "[Err] enum value not found"
+ end
+ end,
+}
+
+pardef.bearer_bars_enabled = { -- boolean type
+ -- enable/disable Bearer bars around the barcode symbol
+ default = false,
+ isReserved = false,
+ order = 7,
+ fncheck = function (_, flag, _) --> boolean, err
+ if type(flag) == "boolean" then
+ return true, nil
+ else
+ return false, "[TypeErr] not a boolean value"
+ end
+ end,
+}
+
+pardef.bearer_bars_thickness = { -- dimension
+ default = 37.5 * 0.0254 * 186467, -- 5 modules
+ unit = "sp", -- scaled point
+ isReserved = false,
+ order = 8,
+ fncheck = function (_self, thick, tpardef) --> boolean, err
+ local module = tpardef.module
+ if thick >= 2*module then
+ return true, nil
+ end
+ return false, "[OutOfRange] thickness too small"
+ end,
+}
+
+pardef.bearer_bars_layout = { -- enumeration
+ -- determine the algorithm for the check digit calculation
+ default = "hbar",
+ isReserved = false,
+ order = 9,
+ method_enum = {
+ frame = true, -- a rectangle around the symbol
+ hbar = true, -- top and bottom horizontal bars
+ },
+ fncheck = function (self, e, _) --> boolean, err
+ if type(e) ~= "string" then return false, "[TypeError] not a string" end
+ local keys = self.method_enum
+ if keys[e] == true then
+ return true, nil
+ else
+ return false, "[Err] enum value not found"
+ end
+ end,
+}
+
+-- auxiliary functions
+
+-- separate a non negative integer in its digits {d_n-1, ..., d_1, d_0}
+local function n_to_arr(n) --> len, digits
+ local digits = {}
+ local slen
+ if n == 0 then
+ digits[#digits + 1] = 0
+ slen = 1
+ else
+ slen = 0
+ while n > 0 do
+ local d = n % 10
+ digits[#digits + 1] = d
+ n = (n - d) / 10
+ slen = slen + 1
+ end
+ end
+ for k = 1, slen/2 do -- array reversing
+ local h = slen - k + 1
+ local d = digits[k]
+ digits[k] = digits[h]
+ digits[h] = d
+ end
+ return slen, digits
+end
+
+local function rshift(t)
+ local tlen = #t
+ for i = tlen, 1, -1 do
+ t[i + 1] = t[i]
+ end
+ t[1] = 0
+end
+
+
+local function check_mod10(t, last)
+ local sum = 0
+ local w = true
+ for i = last, 1, -1 do
+ if w then
+ sum = sum + 3*t[i]
+ else
+ sum = sum + t[i]
+ end
+ w = not w
+ end
+ local m = sum % 10
+ if m == 0 then return 0 else return 10 - m end
+end
+
+local function checkdigit(t, last, method)
+ if method == "mod_10" then
+ return check_mod10(t, last)
+ else
+ assert(false, "[InternalErr] unknow method")
+ end
+end
+
+
+-- configuration function
+function ITF:config() --> ok, err
+ -- init Vbar objects
+ local narrow = self.module
+ local wide = narrow * self.ratio
+ -- start symbol
+ local Vbar = self._libgeo.Vbar -- Vbar class
+ self._vbar_start = Vbar:from_int_revpair(self._start, narrow, wide)
+ self._vbar_stop = Vbar:from_int_revpair(self._stop, narrow, wide)
+
+ -- build every possible pair of digits from 00 to 99
+ self._vbar_data = {}
+ local vbar = self._vbar_data
+ local pattern = self._pattern
+ for dec = 0, 9 do
+ for unit = 0, 9 do
+ local t1 = pattern[dec]
+ local t2 = pattern[unit]
+ local n = dec*10 + unit
+ vbar[n] = Vbar:from_two_tab(t1, t2, narrow, wide)
+ end
+ end
+ return true, nil
+end
+
+-- public functions
+
+function ITF:get_checkdigit(n, method)
+ if type(n) ~= "number" then return nil, "[ArgErr] 'n' is not a number" end
+ if n < 0 then return nil, "[ArgErr] found a negative number" end
+ if n - math.floor(n) ~= 0 then return nil, "[ArgErr] found a float number" end
+ method = method or self.check_digit_method
+ local last, t = n_to_arr(n)
+ return checkdigit(t, last, method)
+end
+
+-- internal methods for constructors
+
+function ITF:_finalize() --> ok, err
+ -- check digit action
+ local policy = self.check_digit_policy
+ local slen = self._code_len
+ local is_even = (slen % 2 == 0)
+ local digits = self._code_data
+ if policy == "none" then
+ if not is_even then
+ rshift(digits) -- add a heading zero for padding
+ slen = slen + 1
+ end
+ elseif policy == "add" then
+ if is_even then
+ rshift(digits) -- add a heading zero for padding
+ slen = slen + 1
+ end
+ local c = checkdigit(digits, slen, self.check_digit_method)
+ digits[#digits + 1] = c
+ elseif policy == "verify" then
+ if not is_even then
+ rshift(digits)
+ slen = slen + 1
+ end
+ local c = checkdigit(digits, slen - 1, self.check_digit_method)
+ if c ~= digits[slen] then
+ return false, "[DataErr] wrong check digit"
+ end
+ else
+ return false, "[InternalError] wrong enum value"
+ end
+ self._code_len = slen
+ return true, nil
+end
+
+-- drawing function
+-- tx, ty is an optional translator vector
+function ITF:append_ga(canvas, tx, ty) --> canvas
+ local err = canvas:start_bbox_group(); assert(not err, err)
+ -- draw the start symbol
+ local xdim = self.module
+ local ratio = self.ratio
+ local symb_len = 2 * xdim * (3 + 2*ratio)
+ local x0 = tx or 0
+ local xpos = x0
+ local y0 = ty or 0
+ local y1 = y0 + self.height
+ local start = self._vbar_start
+ local err
+ err = canvas:encode_Vbar(start, xpos, y0, y1); assert(not err, err)
+ xpos = xpos + 4 * xdim
+ -- draw the code symbol
+ local digits = self._code_data
+ local vbars = self._vbar_data
+ for i = 1, #digits, 2 do
+ local index = 10 * digits[i] + digits[i+1]
+ local b = vbars[index]
+ err = canvas:encode_Vbar(b, xpos, y0, y1); assert(not err, err)
+ xpos = xpos + symb_len
+ end
+ -- draw the stop symbol
+ local stop = self._vbar_stop
+ err = canvas:encode_Vbar(stop, xpos, y0, y1); assert(not err, err)
+ -- bounding box setting
+ local x1 = xpos + (2 + ratio)*xdim
+ local qz = self.quietzone
+ local b1x, b1y = x0 - qz, y0
+ local b2x, b2y = x1 + qz, y1
+ if self.bearer_bars_enabled then
+ local w = self.bearer_bars_thickness
+ err = canvas:encode_linethick(w); assert(not err, err)
+ b1y, b2y = b1y - w, b2y + w
+ local layout = self.bearer_bars_layout
+ if layout == "hbar" then
+ err = canvas:encode_hline(b1x, b2x, y0 - w/2); assert(not err, err)
+ err = canvas:encode_hline(b1x, b2x, y1 + w/2); assert(not err, err)
+ elseif layout == "frame" then
+ err = canvas:encode_rectangle(b1x - w/2, y0 - w/2, b2x + w/2, y1 + w/2)
+ assert(not err, err)
+ b1x, b2x = b1x - w, b2x + w
+ else
+ error("[IntenalErr] bearer bars layout option is wrong")
+ end
+ end
+ local err = canvas:stop_bbox_group(b1x, b1y, b2x, b2y)
+ assert(not err, err)
+ return canvas
+end
+
+return ITF