typo-dig.lmt /size: 5497 b    last modification: 2021-10-28 13:51
1if not modules then modules = { } end modules ['typo-dig'] = {
2    version   = 1.001,
3    optimize  = true,
4    comment   = "companion to typo-dig.mkiv",
5    author    = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
6    copyright = "PRAGMA ADE / ConTeXt Development Team",
7    license   = "see context related readme files"
8}
9
10-- we might consider doing this after the otf pass because now osf do not work
11-- out well in node mode.
12
13local next, type, tonumber = next, type, tonumber
14local format, insert = string.format, table.insert
15local round, div = math.round, math.div
16
17local trace_digits = false  trackers.register("typesetters.digits", function(v) trace_digits = v end)
18
19local report_digits = logs.reporter("typesetting","digits")
20
21local nodes, node = nodes, node
22
23local nuts               = nodes.nuts
24
25local getnext            = nuts.getnext
26local getprev            = nuts.getprev
27local getid              = nuts.getid
28local getwidth           = nuts.getwidth
29local isglyph            = nuts.isglyph
30local getattr            = nuts.getattr
31
32local setlink            = nuts.setlink
33local setnext            = nuts.setnext
34local setprev            = nuts.setprev
35
36local hpacknode          = nuts.hpack
37local insertnodebefore   = nuts.insertbefore
38local insertnodeafter    = nuts.insertafter
39local findattribute      = nuts.findattribute
40local unsetattributes    = nuts.unsetattributes
41
42local texsetattribute    = tex.setattribute
43local unsetvalue         = attributes.unsetvalue
44
45local nodecodes          = nodes.nodecodes
46local glyph_code         = nodecodes.glyph
47
48local nodepool           = nuts.pool
49local enableaction       = nodes.tasks.enableaction
50
51local new_glue           = nodepool.glue
52
53local fonthashes         = fonts.hashes
54local chardata           = fonthashes.characters
55
56local v_reset            = interfaces.variables.reset
57
58local charbase           = characters.data
59local getdigitwidth      = fonts.helpers.getdigitwidth
60
61typesetters              = typesetters or { }
62local typesetters        = typesetters
63
64typesetters.digits       = typesetters.digits or { }
65local digits             = typesetters.digits
66
67digits.actions           = { }
68local actions            = digits.actions
69
70local a_digits           = attributes.private("digits")
71
72-- at some point we can manipulate the glyph node so then i need
73-- to rewrite this then
74
75function nodes.aligned(head,start,stop,width,how)
76    if how == "flushright" or how == "middle" then
77        head, start = insertnodebefore(head,start,new_glue(0,65536,65536))
78    end
79    if how == "flushleft" or how == "middle" then
80        head, stop = insertnodeafter(head,stop,new_glue(0,65536,65536))
81    end
82    local prv = getprev(start)
83    local nxt = getnext(stop)
84    setprev(start)
85    setnext(stop)
86    local packed = hpacknode(start,width,"exactly") -- no directional mess here, just lr
87    if prv then
88        setlink(prv,packed)
89    end
90    if nxt then
91        setlink(packed,nxt)
92    end
93    if getprev(packed) then
94        return head, packed
95    else
96        return packed, packed
97    end
98end
99
100actions[1] = function(head,start,attr)
101    local char, font = isglyph(start)
102    local unic = chardata[font][char].unicode or char
103    if charbase[unic].category == "nd" then -- ignore unic tables
104        local oldwidth = getwidth(start)
105        local newwidth = getdigitwidth(font)
106        if newwidth ~= oldwidth then
107            if trace_digits then
108                report_digits("digit trigger %a, instance %a, char %C, unicode %U, delta %s",
109                    attr%100,div(attr,100),char,unic,newwidth-oldwidth)
110            end
111            head, start = nodes.aligned(head,start,start,newwidth,"middle")
112            return head, start
113        end
114    end
115    return head, start
116end
117
118function digits.handler(head)
119    local _, start = findattribute(head, a_digits)
120    if start then
121        local current = head
122        while current do
123            if getid(current) == glyph_code then
124                local attr = getattr(current,a_digits)
125                if attr and attr > 0 then
126                    local action = actions[attr%100] -- map back to low number
127                    if action then
128                        head, current = action(head,current,attr)
129                    elseif trace_digits then
130                        report_digits("unknown digit trigger %a",attr)
131                    end
132                    done = true
133                end
134            end
135            if current then
136                current = getnext(current)
137            end
138        end
139        if done then
140            unsetattributes(a_digits, head)
141        end
142    end
143    return head
144end
145
146local m, enabled = 0, false -- a trick to make neighbouring ranges work
147
148function digits.set(n) -- number or 'reset'
149    if n == v_reset then
150        n = unsetvalue
151    else
152        n = tonumber(n)
153        if n then
154            if not enabled then
155                enableaction("processors","typesetters.digits.handler")
156                if trace_digits then
157                    report_digits("enabling digit handler")
158                end
159                enabled = true
160            end
161            if m == 100 then
162                m = 1
163            else
164                m = m + 1
165            end
166            n = m * 100 + n
167        else
168            n = unsetvalue
169        end
170    end
171    texsetattribute(a_digits,n)
172end
173
174-- interface
175
176interfaces.implement {
177    name      = "setdigitsmanipulation",
178    actions   = digits.set,
179    arguments = "string"
180}
181