diff options
Diffstat (limited to 'Master/texmf-dist/scripts/barracuda/lib-barcode/brcd-i2of5.lua')
-rw-r--r-- | Master/texmf-dist/scripts/barracuda/lib-barcode/brcd-i2of5.lua | 373 |
1 files changed, 311 insertions, 62 deletions
diff --git a/Master/texmf-dist/scripts/barracuda/lib-barcode/brcd-i2of5.lua b/Master/texmf-dist/scripts/barracuda/lib-barcode/brcd-i2of5.lua index 8097b855f7b..2e1dcd4a020 100644 --- a/Master/texmf-dist/scripts/barracuda/lib-barcode/brcd-i2of5.lua +++ b/Master/texmf-dist/scripts/barracuda/lib-barcode/brcd-i2of5.lua @@ -1,14 +1,18 @@ -- Interleaved 2 of 5 (ITF) barcode generator -- --- Copyright (C) 2019 Roberto Giacomelli +-- Copyright (C) 2020 Roberto Giacomelli -- see LICENSE.txt file local ITF = { -- main container - _VERSION = "ITF v0.0.1", - _NAME = "ITF", + _VERSION = "i2of5 v0.0.1", + _NAME = "i2of5", _DESCRIPTION = "Interleaved 2 of 5 barcode encoder", } +ITF._id_variant = { + ITF14 = true, -- ITF 14 GS1 specification +} + ITF._start = 111 -- nnnn ITF._stop = 112 -- Wnn (integer must be in reverse order) @@ -26,6 +30,17 @@ ITF._pattern = { -- true -> narrow, false -> Wide } -- define parameters +ITF._par_order = { + "module", + "ratio", + "height", + "quietzone", + "check_digit_policy", + "check_digit_method", + "bearer_bars_enabled", + "bearer_bars_thickness", + "bearer_bars_layout", +} ITF._par_def = {} local pardef = ITF._par_def @@ -37,8 +52,7 @@ pardef.module = { 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 + fncheck = function (self, mod, _t_opt) --> boolean, err if mod >= self.default then return true, nil end return false, "[OutOfRange] too small lenght for X-dim" end, @@ -53,10 +67,9 @@ pardef.ratio = { 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 + fncheck = function (_self, ratio, t_opt) --> ok, err + local mils = 0.0254 * 186467 -- sp + local mod = t_opt.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..")" @@ -73,9 +86,8 @@ pardef.height = { 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 + fncheck = function (_self, h, _t_opt) --> boolean, err + local mils = 0.0254 * 186467 -- scaled point (sp) if h >= 250*mils then return true, nil end @@ -89,16 +101,14 @@ pardef.quietzone = { 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 + fncheck = function (self, qz, _t_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 + if qz < min then return false, "[OutOfRange] quietzone too small" end + return true, nil end, } @@ -110,8 +120,7 @@ pardef.check_digit_policy = { -- enumeration verify = true, -- check the last digit of the symbol as check digit none = true, -- do nothing }, - order = 5, - fncheck = function (self, e, _) --> boolean, err + fncheck = function (self, e, _t_opt) --> boolean, err if type(e) ~= "string" then return false, "[TypeError] not a string" end local keys = self.policy_enum if keys[e] == true then @@ -126,11 +135,10 @@ 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 + fncheck = function (self, e, _t_opt) --> boolean, err if type(e) ~= "string" then return false, "[TypeError] not a string" end local keys = self.method_enum if keys[e] == true then @@ -145,8 +153,7 @@ 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 + fncheck = function (_self, flag, _t_opt) --> boolean, err if type(flag) == "boolean" then return true, nil else @@ -159,26 +166,162 @@ 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 + fncheck = function (_self, thick, t_opt) --> boolean, err + local module = t_opt.module if thick >= 2*module then return true, nil end - return false, "[OutOfRange] thickness too small" + return false, "[OutOfRange] too small bearer bar thickness" end, } pardef.bearer_bars_layout = { -- enumeration - -- determine the algorithm for the check digit calculation + -- horizontal/frame bearer bars default = "hbar", isReserved = false, - order = 9, + method_enum = { + frame = true, -- a rectangle around the symbol + hbar = true, -- only top and bottom horizontal bars + }, + fncheck = function (self, e, _t_opt) --> 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, +} + +-- ITF14: variant based on GS1 specification, see +-- https://www.gs1.org/sites/default/files/docs/barcodes/GS1_General_Specifications.pdf +-- GS1 ITF14 parameters vary in range and preferred value on the specific +-- application. Default are chosen for better suite the majority of cases + +-- ITF14 variant parameters as family parameters alternative +ITF._par_def_ITF14 = {} +local itf14_pdef = ITF._par_def_ITF14 + +itf14_pdef.module = { -- module main parameter + -- Narrow element X-dimension is the width of the smallest element. + -- The module width must follow GS1 specification + default = 0.495 * 186467, -- 0.495 mm + unit = "sp", -- scaled point + isReserved = true, + fncheck = function (_self, mod, _t_opt) --> boolean, err + local mod_mm = mod * 186467 -- mm + if (mod_mm < 0.264) or (mod_mm > 0.660) then + return false, "[OutOfRange] X-dim is out of [0.264mm, 0.660mm]" + end + return true, nil + end, +} + +itf14_pdef.ratio = { + -- The "wide" element is a multiple of the "narrow" element that can + -- range between 2.25 and 3.0. + default = 2.5, + unit = "absolute-number", + isReserved = true, + fncheck = function (_self, ratio, _t_opt) --> boolean, err + if (ratio < 2.25) or (ratio > 3.0) then + return false, "[OutOfRange] wide-to-narrow ratio is out of [2.25, 3.0]" + end + return true, nil + end, +} + +itf14_pdef.height = { + -- The height should be at least 5.08 mm. Target value is 12.70 mm + -- except in retail pharmacy and general distribution or non-retail pharmacy + -- and general distribution + default = 12.70 * 186467, -- mm + unit = "sp", -- scaled point + isReserved = false, + fncheck = function (_self, h, _t_opt) --> boolean, err + if h < 5.08 * 186467 then + return false, "[OutOfRange] height is too small" + end + return true, nil + end, +} + +itf14_pdef.quietzone = { + -- Quiet zones must be at least 10 times the module width + default = 10, + unit = "absolute-number", -- scaled point + isReserved = false, + fncheck = function (_self, qz, _t_opt) --> boolean, err + if qz < 10 then + return false, "[OutOfRange] quietzone factor is too small (min 10x)" + else + return true, nil + end + end, +} + +itf14_pdef.check_digit_policy = { -- enumeration + default = "check_or_add", + isReserved = false, + policy_enum = { + check_or_add = true, + add = true, -- add a check digit to the symbol + verify = true, -- check the last digit of the symbol as check digit + }, + fncheck = function (self, e, _t_opt) --> 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 '"..e.."' not found" + end + end, +} + +-- itf14_pdef.check_digit_method = {} the same as the basic parameter + +itf14_pdef.bearer_bars_enabled = { -- boolean type + -- enable/disable Bearer bars around the barcode symbol + default = true, + isReserved = false, + fncheck = function (_self, flag, _t_opt) --> boolean, err + if type(flag) == "boolean" then + return true, nil + else + return false, "[TypeErr] not a boolean value" + end + end, +} + +-- from GS1 spec: +-- For printing methods that do not require printing plates, the bearer bar +-- SHALL be a minimum of twice the width of a narrow bar (dark bar) and need +-- only appear at the top and bottom of the symbol, butting directly against +-- the top and bottom of the symbol bars (dark bars). +pardef.bearer_bars_thickness = { -- dimension + default = 5 * 0.495 * 186467, -- 5 modules + unit = "sp", -- scaled point + isReserved = false, + fncheck = function (_self, thick, t_opt) --> boolean, err + local module = t_opt.module + if thick >= 2*module then + return true, nil + end + return false, "[OutOfRange] too small bearer bar thickness" + end, +} + +itf14_pdef.bearer_bars_layout = { -- enumeration + -- horizontal/frame bearer bars + default = "frame", + isReserved = false, method_enum = { frame = true, -- a rectangle around the symbol hbar = true, -- top and bottom horizontal bars }, - fncheck = function (self, e, _) --> boolean, err + 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 @@ -224,18 +367,15 @@ local function rshift(t) 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 +-- for general i2of5 symbols +local function check_mod10(data, last) + local s3 = 0; for i = last, 1, -2 do + s3 = s3 + data[i] end + local s1 = 0; for i = last - 1, 1, -2 do + s1 = s1 + data[i] + end + local sum = s1 + 3 * s3 local m = sum % 10 if m == 0 then return 0 else return 10 - m end end @@ -244,13 +384,60 @@ local function checkdigit(t, last, method) if method == "mod_10" then return check_mod10(t, last) else - assert(false, "[InternalErr] unknow method") - end + error("[InternalErr] unknow checksum method '"..method.."'") + end end +-- group char for readibility '(' or ')' or ' ' +local function itf14_check_char(enc, c, parse_state) --> elem, err + if type(c) ~= "string" or #c ~= 1 then + return nil, "[InternalErr] invalid char" + end + if parse_state.itf14_code == nil then parse_state.itf14_code = {} end + local code = parse_state.itf14_code + if parse_state.is_space == nil then parse_state.is_space = false end + if parse_state.is_popen == nil then parse_state.is_popen = false end + if parse_state.itf14_len == nil then parse_state.itf14_len = 0 end + local itf14_len = parse_state.itf14_len + -- parsing + if c == " " then + if itf14_len == 0 then -- ignore initial spaces + return nil, nil + end + parse_state.is_space = true + return nil, nil + elseif c == "(" then + if parse_state.is_popen then + return nil, "[Err] a parenthesis group is already open" + end + parse_state.is_popen = true + code[#code + 1] = c + return nil, nil + elseif c == ")" then + if not parse_state.is_popen then + return nil, "[Err] found a closing parenthesis without an opening one" + end + parse_state.is_popen = false + code[#code + 1] = c + return nil, nil + else -- c is at this point eventually a digit + local n = string.byte(c) - 48 + if n < 0 or n > 9 then + return nil, "[ArgErr] found a not digit or a not grouping char" + end + if parse_state.is_space then + code[#code + 1] = " " + parse_state.is_space = false + end + code[#code + 1] = c + itf14_len = itf14_len + 1 + parse_state.itf14_len = itf14_len + return n, nil + end +end -- configuration function -function ITF:config() --> ok, err +function ITF:_config() --> ok, err -- init Vbar objects local narrow = self.module local wide = narrow * self.ratio @@ -258,7 +445,6 @@ function ITF:config() --> ok, err 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 @@ -271,28 +457,66 @@ function ITF:config() --> ok, err vbar[n] = Vbar:from_two_tab(t1, t2, narrow, wide) end end + local variant = self._variant + if variant == "ITF14" then + self._check_char = itf14_check_char + end return true, nil end --- public functions +-- internal methods for constructors -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) +-- input code post processing for ITF14 variant +local function itf14_finalize(enc) --> ok, err + -- check digit action + local policy = enc.check_digit_policy + local slen = enc._code_len + local digits = enc._code_data + local is_add + if policy == "verify" then + if slen ~= 14 then + return nil, "[DataErr] incorrect input lenght of "..slen.. + " respect to 14 (checksum policy 'verify')" + end + is_add = false + elseif policy == "add" then + if slen ~= 13 then + return nil, "[DataErr] incorrect input lenght of "..slen.. + " respect to 13 (checksum policy 'add')" + end + is_add = true + elseif policy == "check_or_add" then + if slen == 14 then + is_add = false + elseif slen == 13 then + is_add = true + else + return nil, "[DataErr] incorrect input lenght of "..slen.. + " respect to the policy '"..policy.."'" + end + else + return false, "[InternalErr] incorrect policy enum value '"..policy.."'" + end + assert(type(is_add) == "boolean") + local cs = checkdigit(digits, 13, enc.check_digit_method) + if is_add then + digits[14] = cs + enc._code_len = 14 + else + if cs ~= digits[14] then + return false, "[DataErr] last digit is not equal to checksum" + end + end + return true, nil end --- internal methods for constructors - -function ITF:_finalize() --> ok, err +-- input code post processing for basic i2of5 +local function basic_finalize(enc) --> ok, err -- check digit action - local policy = self.check_digit_policy - local slen = self._code_len + local policy = enc.check_digit_policy + local slen = enc._code_len local is_even = (slen % 2 == 0) - local digits = self._code_data + local digits = enc._code_data if policy == "none" then if not is_even then rshift(digits) -- add a heading zero for padding @@ -303,24 +527,46 @@ function ITF:_finalize() --> ok, err rshift(digits) -- add a heading zero for padding slen = slen + 1 end - local c = checkdigit(digits, slen, self.check_digit_method) + local c = checkdigit(digits, slen, enc.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) + local c = checkdigit(digits, slen - 1, enc.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 + enc._code_len = slen return true, nil end +-- input code post processing +function ITF:_finalize() --> ok, err + local var = self._variant + if var then + assert(var == "ITF14") + return itf14_finalize(self) + else + return basic_finalize(self) + end +end + +-- public functions + +function ITF:get_checkdigit(n, method) --> checksum, err + 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 math.floor(n) ~= n then return nil, "[ArgErr] found a not integer number" end + method = method or self.check_digit_method + local last, t = n_to_arr(n) + return checkdigit(t, last, method) +end + -- drawing function -- tx, ty is an optional translator vector function ITF:append_ga(canvas, tx, ty) --> canvas @@ -352,6 +598,9 @@ function ITF:append_ga(canvas, tx, ty) --> canvas -- bounding box setting local x1 = xpos + (2 + ratio)*xdim local qz = self.quietzone + if self._variant then + qz = qz * xdim + end local b1x, b1y = x0 - qz, y0 local b2x, b2y = x1 + qz, y1 if self.bearer_bars_enabled then |