math-dir.lua /size: 5533 b    last modification: 2023-12-21 09:44
1if not modules then modules = { } end modules ['math-dir'] = {
2    version   = 1.001,
3    comment   = "companion to typo-dir.mkiv",
4    author    = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
5    copyright = "PRAGMA ADE / ConTeXt Development Team",
6    license   = "see context related readme files"
7}
8
9-- As I'm wrapping up the updated math support (for CTX/TUG 2013) I wondered about numbers in
10-- r2l math mode. Googling lead me to TUGboat, Volume 25 (2004), No. 2 where I see numbers
11-- running from left to right. Makes me wonder how far we should go. And as I was looking
12-- into bidi anyway, it's a nice distraction.
13--
14-- I first tried to hook something into noads but that gets pretty messy due to indirectness
15-- char noads. If needed, I'll do it that way. With regards to spacing: as we can assume that
16-- only numbers are involved we can safely swap them and the same is true for mirroring. But
17-- anyway, I'm not too happy with this solution so eventually I'll do something with noads (as
18-- an alternative method). Yet another heuristic approach.
19
20local nodes, node = nodes, node
21
22local trace_directions   = false  trackers.register("typesetters.directions.math", function(v) trace_directions = v end)
23
24local report_directions  = logs.reporter("typesetting","math directions")
25
26local nuts               = nodes.nuts
27local tonut              = nuts.tonut
28local tonode             = nuts.tonode
29
30local getnext            = nuts.getnext
31local getchar            = nuts.getchar
32local getid              = nuts.getid
33local getlist            = nuts.getlist
34local getattr            = nuts.getattr
35
36local setchar            = nuts.setchar
37local setlist            = nuts.setlist
38
39local insertnodebefore   = nuts.insertbefore
40local insertnodeafter    = nuts.insertafter
41
42local nodecodes          = nodes.nodecodes
43local enableaction       = nodes.tasks.enableaction
44
45local glyph_code         = nodecodes.glyph
46local hlist_code         = nodecodes.hlist
47local vlist_code         = nodecodes.vlist
48
49local nodepool           = nuts.pool
50
51local new_direction      = nodepool.direction
52
53local lefttoright_code   = nodes.dirvalues.lefttoright
54
55local chardirections     = characters.directions
56local charmirrors        = characters.mirrors
57local charclasses        = characters.textclasses
58
59local directions         = typesetters.directions or { }
60
61local a_mathbidi         = attributes.private('mathbidi')
62
63local function processmath(head)
64    local current = head
65    local start   = nil
66    local stop    = nil
67    local function capsulate()
68        head = insertnodebefore(head,start,new_direction(lefttoright_code))
69        insertnodeafter(head,stop,new_direction(lefttoright_code,true))
70        if trace_directions then
71            report_directions("reversed: %s",nodes.listtoutf(start,false,false,stop))
72        end
73        start = false
74        stop  = nil
75    end
76    while current do
77        local id = getid(current)
78        if id == glyph_code then
79            local char = getchar(current)
80            local cdir = chardirections[char]
81            if cdir == "en" or cdir == "an" then -- we could check for mathclass punctuation
82                if not start then
83                    start = current
84                end
85                stop = current
86            else
87                if not start then
88                    -- nothing
89                elseif start == stop then
90                    start = nil
91                else
92                    capsulate()
93                end
94                if cdir == "on" then
95                    local mirror = charmirrors[char]
96                    if mirror then
97                        local class = charclasses[char]
98                        if class == "open" or class == "close" then
99                            setchar(current,mirror)
100                            if trace_directions then
101                                report_directions("mirrored: %C to %C",char,mirror)
102                            end
103                        end
104                    end
105                end
106            end
107        elseif not start then
108            -- nothing
109            if id == hlist_code or id == vlist_code then
110                local list = processmath(getlist(current))
111                setlist(current,list)
112            end
113        elseif start == stop then
114            start = nil
115        else
116            capsulate(head,start,stop)
117            -- math can pack things into hlists .. we need to make sure we don't process
118            -- too often: needs checking
119            if id == hlist_code or id == vlist_code then
120                local list = processmath(getlist(current))
121                setlist(current,list)
122            end
123        end
124        current = getnext(current)
125    end
126    if not start then
127        -- nothing
128    elseif start == stop then
129        -- nothing
130    else
131        capsulate()
132    end
133    return head
134end
135
136local enabled = false
137
138function directions.processmath(head) -- style, penalties
139    if enabled then
140        local a = getattr(head,a_mathbidi)
141        if a and a > 0 then
142            return processmath(head)
143        end
144    end
145    return head
146end
147
148function directions.setmath(n)
149    if not enabled and n and n > 0 then
150        if trace_directions then
151            report_directions("enabling directions handler")
152        end
153        enableaction("math","typesetters.directions.processmath")
154        enabled = true
155    end
156end
157
158interfaces.implement {
159    name      = "setmathdirection",
160    actions   = directions.setmath,
161    arguments = "integer"
162}
163