1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
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
|