diff options
Diffstat (limited to 'macros/luatex/generic/barracuda/src/lib-barcode/brcd-upc.lua')
-rw-r--r-- | macros/luatex/generic/barracuda/src/lib-barcode/brcd-upc.lua | 251 |
1 files changed, 251 insertions, 0 deletions
diff --git a/macros/luatex/generic/barracuda/src/lib-barcode/brcd-upc.lua b/macros/luatex/generic/barracuda/src/lib-barcode/brcd-upc.lua new file mode 100644 index 0000000000..99bfd4349e --- /dev/null +++ b/macros/luatex/generic/barracuda/src/lib-barcode/brcd-upc.lua @@ -0,0 +1,251 @@ +-- Universal Product Code +-- UPC family barcode generator +-- +-- Copyright (C) 2019-2022 Roberto Giacomelli +-- see LICENSE.txt file + +local UPC = { + _VERSION = "upc v0.0.2", + _NAME = "upc", + _DESCRIPTION = "UPC barcode encoder", +} + +UPC._pattern = {[0] = 3211, 2221, 2122, 1411, 1132, 1231, 1114, 1312, 1213, 3112,} +UPC._sepbar = 111 + +UPC._id_variant = { + A = true, -- UPC-A +} + +-- common family parameters +UPC._par_order = { + "mod", + "height", + "bars_depth_factor", + "quietzone_factor", + "text_enabled", + "text_ygap_factor", + "text_xgap_factor", +} +UPC._par_def = {} +local pardef = UPC._par_def + +-- from Wikipedia https://en.wikipedia.org/wiki/Universal_Product_Code +-- The x-dimension for the UPC-A at the nominal size is 0.33 mm (0.013"). +-- UPC-A can be reduced or magnified anywhere from 80% to 200%. +-- standard module is 0.33 mm but it may 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, + 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, +} + +-- Nominal symbol height for UPC-A is 25.9 mm (1.02") +pardef.height = { + default = 25.9 * 186467, + unit = "sp", + isReserved = false, + fncheck = function (self, h, _enc) --> boolean, err + if h > 0 then + return true, nil + else + return false, "[OutOfRange] non positive value for height" + end + end, +} + +-- The bars forming the S (start), M (middle), and E (end) guard patterns, are +-- extended downwards by 5 times x-dimension, with a resulting nominal symbol +-- height of 27.55 mm (1.08") +pardef.bars_depth_factor = { + default = 5, + unit = "absolute-number", + isReserved = false, + 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, +} + +-- A quiet zone, with a width of at least 9 times the x-dimension, must be +-- present on each side of the scannable area of the UPC-A barcode. +pardef.quietzone_factor = { + default = 9, + unit = "absolute-number", + isReserved = false, + 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, +} + +-- enable/disable human readble text +pardef.text_enabled = { -- boolean type + default = true, + isReserved = false, + 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, +} + +-- vertical gap from text and symbol baseline +pardef.text_ygap_factor = { + default = 1.0, + unit = "absolute-number", + isReserved = false, + 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, +} + +-- horizontal gap from text and symbol bars +pardef.text_xgap_factor = { + default = 0.75, + unit = "absolute-number", + isReserved = false, + 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, +} + +-- utility function + +-- the checksum of UPC-A +-- 'data' is an array of digits +local function checkdigit_A(data) --> checksum, ok + local s = 0 + for i = 1, 11, 2 do + s = s + data[i] + end + s = 3*s + for i = 2, 10, 2 do + s = s + data[i] + end + local res = s % 10 + if res > 0 then + res = 10 - res + end + return res, data[12] == res +end + +-- internal methods for Barcode costructors + +-- config function called at the moment of encoder construction +function UPC:_config() --> ok, err + local variant = self._variant + if not variant then + return false, "[Err] variant is mandatory for the UPC family" + end + local Vbar = self._libgeo.Vbar -- Vbar class + local Vbar_archive = self._libgeo.Archive -- Archive class + local Codeset = Vbar_archive:new() + self._upca_codeset = Codeset + local mod = self.mod + local sepbar = self._sepbar + Codeset:insert(Vbar:from_int(sepbar, mod), "sepbar") + local symb = self._pattern + for i = 0, 9 do + Codeset:insert(Vbar:from_int(symb[i], mod, false), 0, i) + Codeset:insert(Vbar:from_int(symb[i], mod, true), 1, i) + end + return true, nil +end + +-- function called every time an input UPC-A code has been completely parsed +function UPC:_finalize() --> ok, err + local data = self._code_data + local symb_len = #data + if symb_len ~= 12 then + return false, "[ArgErr] not a 12-digit long array" + end + local ck, ok = checkdigit_A(data) + if not ok then -- is the last digit ok? + return false, + "[Err] incorrect last digit "..data[12]..", the checksum is "..ck + end + return true, nil +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 UPC:_append_ga(canvas, tx, ty) --> bbox + local Codeset = self._upca_codeset + local code = self._code_data + local mod = self.mod + local sepbar = assert(Codeset:get("sepbar")) + local queue_0 = sepbar + assert(Codeset:get(0, code[1])) + + 36*mod + sepbar + 36*mod + + assert(Codeset:get(1, code[12])) + sepbar + local queue_1 = 10*mod + for i = 2, 6 do + queue_1 = queue_1 + assert(Codeset:get(0, code[i])) + end + queue_1 = queue_1 + 5*mod + for i = 7, 11 do + queue_1 = queue_1 + assert(Codeset:get(1, code[i])) + end + local bars_depth = mod * self.bars_depth_factor + local w, h = 95*mod, self.height + bars_depth + local x0 = tx - self.ax * w + local y0 = ty - self.ay * h + local x1 = x0 + w + local y1 = y0 + h + local ys = y0 + bars_depth + -- draw the symbol + assert(canvas:encode_disable_bbox()) + assert(canvas:encode_vbar_queue(queue_0, x0, y0, y1)) + assert(canvas:encode_vbar_queue(queue_1, x0, ys, y1)) + -- bounding box set up + local qz = self.quietzone_factor * mod + assert(canvas:encode_set_bbox(x0 - qz, y0, x1 + qz, y1)) + assert(canvas:encode_enable_bbox()) + if self.text_enabled then -- human readable text + local Text = self._libgeo.Text + local txt_1 = Text:from_digit_array(code, 1, 1) + local txt_2 = Text:from_digit_array(code, 2, 6) + local txt_3 = Text:from_digit_array(code, 7, 11) + local txt_4 = Text:from_digit_array(code, 12, 12) + local y_bl = ys - self.text_ygap_factor * mod + assert(canvas:encode_Text(txt_1, x0 - qz, y_bl, 0, 1)) + local mx = self.text_xgap_factor + local x2_1 = x0 + (10+mx)*mod + local x2_2 = x0 + (46-mx)*mod + assert(canvas:encode_Text_xwidth(txt_2, x2_1, x2_2, y_bl, 1)) + local x3_1 = x0 + (49+mx)*mod + local x3_2 = x0 + (85-mx)*mod + assert(canvas:encode_Text_xwidth(txt_3, x3_1, x3_2, y_bl, 1)) + assert(canvas:encode_Text(txt_4, x1 + qz, y_bl, 1, 1)) + end + return {x0, y0, x1, y1, qz, nil, qz, nil,} +end + +return UPC |