typo-dig.lua /size: 5151 b    last modification: 2021-10-28 13:50
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 takeattr           = nuts.takeattr
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
39
40local texsetattribute    = tex.setattribute
41local unsetvalue         = attributes.unsetvalue
42
43local nodecodes          = nodes.nodecodes
44local glyph_code         = nodecodes.glyph
45
46local nodepool           = nuts.pool
47local enableaction       = nodes.tasks.enableaction
48
49local new_glue           = nodepool.glue
50
51local fonthashes         = fonts.hashes
52local chardata           = fonthashes.characters
53
54local v_reset            = interfaces.variables.reset
55
56local charbase           = characters.data
57local getdigitwidth      = fonts.helpers.getdigitwidth
58
59typesetters              = typesetters or { }
60local typesetters        = typesetters
61
62typesetters.digits       = typesetters.digits or { }
63local digits             = typesetters.digits
64
65digits.actions           = { }
66local actions            = digits.actions
67
68local a_digits           = attributes.private("digits")
69
70-- at some point we can manipulate the glyph node so then i need
71-- to rewrite this then
72
73function nodes.aligned(head,start,stop,width,how)
74    if how == "flushright" or how == "middle" then
75        head, start = insertnodebefore(head,start,new_glue(0,65536,65536))
76    end
77    if how == "flushleft" or how == "middle" then
78        head, stop = insertnodeafter(head,stop,new_glue(0,65536,65536))
79    end
80    local prv = getprev(start)
81    local nxt = getnext(stop)
82    setprev(start)
83    setnext(stop)
84    local packed = hpacknode(start,width,"exactly") -- no directional mess here, just lr
85    if prv then
86        setlink(prv,packed)
87    end
88    if nxt then
89        setlink(packed,nxt)
90    end
91    if getprev(packed) then
92        return head, packed
93    else
94        return packed, packed
95    end
96end
97
98actions[1] = function(head,start,attr)
99    local char, font = isglyph(start)
100    local unic = chardata[font][char].unicode or char
101    if charbase[unic].category == "nd" then -- ignore unic tables
102        local oldwidth = getwidth(start)
103        local newwidth = getdigitwidth(font)
104        if newwidth ~= oldwidth then
105            if trace_digits then
106                report_digits("digit trigger %a, instance %a, char %C, unicode %U, delta %s",
107                    attr%100,div(attr,100),char,unic,newwidth-oldwidth)
108            end
109            head, start = nodes.aligned(head,start,start,newwidth,"middle")
110            return head, start
111        end
112    end
113    return head, start
114end
115
116function digits.handler(head)
117    local current = head
118    while current do
119        if getid(current) == glyph_code then
120            local attr = takeattr(current,a_digits)
121            if attr and attr > 0 then
122                local action = actions[attr%100] -- map back to low number
123                if action then
124                    head, current = action(head,current,attr)
125                elseif trace_digits then
126                    report_digits("unknown digit trigger %a",attr)
127                end
128            end
129        end
130        if current then
131            current = getnext(current)
132        end
133    end
134    return head
135end
136
137local m, enabled = 0, false -- a trick to make neighbouring ranges work
138
139function digits.set(n) -- number or 'reset'
140    if n == v_reset then
141        n = unsetvalue
142    else
143        n = tonumber(n)
144        if n then
145            if not enabled then
146                enableaction("processors","typesetters.digits.handler")
147                if trace_digits then
148                    report_digits("enabling digit handler")
149                end
150                enabled = true
151            end
152            if m == 100 then
153                m = 1
154            else
155                m = m + 1
156            end
157            n = m * 100 + n
158        else
159            n = unsetvalue
160        end
161    end
162    texsetattribute(a_digits,n)
163end
164
165-- interface
166
167interfaces.implement {
168    name      = "setdigitsmanipulation",
169    actions   = digits.set,
170    arguments = "string"
171}
172