libs-imp-zint.lmt /size: 8885 b    last modification: 2023-12-21 09:44
1if not modules then modules = { } end modules ['libs-imp-ghostscript'] = {
2    version   = 1.001,
3    comment   = "companion to luat-lib.mkxl",
4    author    = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
5    copyright = "PRAGMA ADE / ConTeXt Development Team",
6    license   = "see context related readme files",
7}
8
9local libname = "zint"
10local libfile = "libzint" -- what on unix?
11-- local libfile = "/usr/local/lib/libzint.so"
12
13local zintlib = resolvers.libraries.validoptional(libname)
14
15if not zintlib then return end
16
17local function okay()
18    if resolvers.libraries.optionalloaded(libname,libfile) then
19        okay = function() return true end
20    else
21        okay = function() return false end
22    end
23    return okay()
24end
25
26local zint     = utilities.zint or { }
27utilities.zint = zint
28
29local zintlib_execute = zintlib.execute
30
31local next, type, unpack, rawget = next, type, unpack, rawget
32local lower, gsub = string.lower, string.gsub
33local formatters = string.formatters
34
35local mapping = {
36    ["code 11"]           =  1, ["pharma one-track"]       = 51, ["aztec code"]              =  92,
37    ["standard 2of5"]     =  2, ["pzn"]                    = 52, ["daft code"]               =  93,
38    ["interleaved 2of5"]  =  3, ["pharma two-track"]       = 53, ["micro qr code"]           =  97,
39    ["iata 2of5"]         =  4, ["pdf417"]                 = 55, ["hibc code 128"]           =  98,
40    ["data logic"]        =  6, ["pdf417 trunc"]           = 56, ["hibc code 39"]            =  99,
41    ["industrial 2of5"]   =  7, ["maxicode"]               = 57, ["hibc data matrix"]        = 102,
42    ["code 39"]           =  8, ["qr code"]                = 58, ["hibc qr code"]            = 104,
43    ["extended code 39"]  =  9, ["code 128-b"]             = 60, ["hibc pdf417"]             = 106,
44    ["ean"]               = 13, ["ap standard customer"]   = 63, ["hibc micropdf417"]        = 108,
45    ["ean + check"]       = 14, ["ap reply paid"]          = 66, ["hibc codablock-f"]        = 110,
46    ["gs1-128"]           = 16, ["ap routing"]             = 67, ["hibc aztec code"]         = 112,
47    ["codabar"]           = 18, ["ap redirection"]         = 68, ["dotcode"]                 = 115,
48    ["code 128"]          = 20, ["isbn"]                   = 69, ["han xin code"]            = 116,
49    ["leitcode"]          = 21, ["rm4scc"]                 = 70, ["rm mailmark"]             = 121,
50    ["identcode"]         = 22, ["data matrix"]            = 71, ["aztec runes"]             = 128,
51    ["code 16k"]          = 23, ["ean-14"]                 = 72, ["code 32"]                 = 129,
52    ["code 49"]           = 24, ["vin (north america)"]    = 73, ["comp ean"]                = 130,
53    ["code 93"]           = 25, ["codablock-f"]            = 74, ["comp gs1-128"]            = 131,
54    ["flattermarken"]     = 28, ["nve-18"]                 = 75, ["comp databar omni"]       = 132,
55    ["gs1 databar omni"]  = 29, ["japanese post"]          = 76, ["comp databar ltd"]        = 133,
56    ["gs1 databar ltd"]   = 30, ["korea post"]             = 77, ["comp databar expom"]      = 134,
57    ["gs1 databar expom"] = 31, ["gs1 databar stack"]      = 79, ["comp upc-a"]              = 135,
58    ["telepen alpha"]     = 32, ["gs1 databar stack omni"] = 80, ["comp upc-e"]              = 136,
59    ["upc-a"]             = 34, ["gs1 databar eso"]        = 81, ["comp databar stack"]      = 137,
60    ["upc-a + check"]     = 35, ["planet"]                 = 82, ["comp databar stack omni"] = 138,
61    ["upc-e"]             = 37, ["micropdf"]               = 84, ["comp databar eso"]        = 139,
62    ["upc-e + check"]     = 38, ["usps onecode"]           = 85, ["channel code"]            = 140,
63    ["postnet"]           = 40, ["uk plessey"]             = 86, ["code one"]                = 141,
64    ["msi plessey"]       = 47, ["telepen numeric"]        = 87, ["grid matrix"]             = 142,
65    ["fim"]               = 49, ["itf-14"]                 = 89, ["upnqr"]                   = 143,
66    ["logmars"]           = 50, ["kix code"]               = 90, ["rmqr"]                    = 145,
67}
68
69for k, v in table.sortedhash(mapping) do
70    mapping[gsub(lower(k),"[^a-z0-9]","")]= v
71end
72
73mapping.qr = mapping.qrcode
74
75table.setmetatableindex(mapping,function(t,k)
76    local s = gsub(lower(k),"[^a-z0-9]","")
77    local v = rawget(t,s) or false
78    t[k] = v
79    return v
80end)
81
82-- local options = {
83--  -- DATA_MODE      =   0 -- Binary
84--  -- UNICODE_MODE   =   1 -- UTF-8
85--  -- GS1_MODE       =   2 -- GS1
86--  -- GS1PARENS_MODE =  16 -- process parentheses as GS1 AI delimiters (instead of square brackets)
87--     square         = 100 -- DM_SQUARE : only consider square versions on automatic symbol size selection
88--  -- DMRE           = 101 -- DM_DMRE   : consider DMRE versions on automatic symbol size selection
89-- }
90
91local report  = logs.reporter("zint")
92local context = context
93local shown   = false
94local sqrt    = math.sqrt
95
96local f_rectangle = formatters["( unitsquare xyscaled (%N,-%N) shifted (%N,-%N) )"]
97local f_hexagon   = formatters["( fullhexagon scaled %N shifted (%N,-%N) )"]
98local f_circle    = formatters["( fullcircle scaled %N shifted (%N,-%N) )"]
99local f_string    = formatters['draw textext("%s") scaled (%N/10) shifted (%N,-%N);']
100
101local aliases = {
102    isbnx = "isbn",
103    ISBNX = "ISBN",
104}
105
106local function execute(specification)
107    if okay() then
108        local code   = specification.code
109        local text   = specification.text
110        local option = specification.option
111        --
112        if option then
113            option = gsub(option,"^%-+","")
114        end
115        specification.option = option
116        --
117        if code then
118            code = aliases[code] or code
119            --
120            if lower(code) == "isbn" then
121                specification.text = text and gsub(text,"[^%d]","") or ""
122            end
123            --
124            local id = mapping[code]
125            if id then
126                specification.code = id
127                local result, detail = zintlib_execute(specification)
128                if result then
129                    -- not that fast but if needed we can speed it up
130                    context.startMPcode()
131                    local rectangles = result.rectangles
132                    local hexagons   = result.hexagons
133                    local circles    = result.circles
134                    local strings    = result.strings
135                    if rectangles then
136                        local n = #rectangles
137                        for i=1,n do
138                            local r = rectangles[i]
139                            rectangles[i] = f_rectangle(r[3],r[4],r[1],r[2])
140                        end
141                        context("fill % && t && cycle;",rectangles)
142                    end
143                    if hexagons then
144                        local n = #hexagons
145                        local s = sqrt(2) / 2 -- can be finetuned if needed
146                        for i=1,n do
147                            local h = hexagons[i]
148                            hexagons[i] = f_hexagon(s*h[3],h[1],h[2])
149                        end
150                        context("fill % && t && cycle;",hexagons)
151                    end
152                    if circles then
153                        local n = #circles
154                        for i=1,n do
155                            local c = circles[i]
156                            circles[i] = f_circle(c[3],c[1],c[2])
157                        end
158                        context("eofill % && t && cycle;",circles)
159                    end
160                    if strings then
161                        -- We set the font at the encapsulating level.
162                        local n = #strings
163                        for i=1,n do
164                            local s = strings[i]
165                            strings[i] = f_string(s[4],s[3],s[1],s[2])
166                        end
167                        context("% t",strings)
168                    end
169                    context.stopMPcode()
170                else
171                    report("something went wrong: %s",detail or "unknown error")
172                end
173            else
174                report("unknown barcode alternative %a",code)
175                if not shown then
176                    report("")
177                    report("valid barcode alternatives:")
178                    report("")
179                    local list = table.sortedkeys(mapping)
180                    for i=1,#list do
181                        report("  %s", list[i])
182                    end
183                    report("")
184                    shown = true
185                end
186            end
187        end
188    end
189end
190
191optional.loaded.zint = { execute = execute }
192
193interfaces.implement {
194    name      = "zint",
195    actions   = execute,
196    arguments = {
197        {
198            { "code" },
199            { "text" },
200            { "option" },
201        }
202    }
203}
204