summaryrefslogtreecommitdiff
path: root/tex/context/base/mkiv/math-dir.lua
blob: f26324ed90c5c20f0af4381ee67662d4c6a55efc (plain)
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
if not modules then modules = { } end modules ['math-dir'] = {
    version   = 1.001,
    comment   = "companion to typo-dir.mkiv",
    author    = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
    copyright = "PRAGMA ADE / ConTeXt Development Team",
    license   = "see context related readme files"
}

-- As I'm wrapping up the updated math support (for CTX/TUG 2013) I wondered about numbers in
-- r2l math mode. Googling lead me to TUGboat, Volume 25 (2004), No. 2 where I see numbers
-- running from left to right. Makes me wonder how far we should go. And as I was looking
-- into bidi anyway, it's a nice distraction.
--
-- I first tried to hook something into noads but that gets pretty messy due to indirectness
-- char noads. If needed, I'll do it that way. With regards to spacing: as we can assume that
-- only numbers are involved we can safely swap them and the same is true for mirroring. But
-- anyway, I'm not too happy with this solution so eventually I'll do something with noads (as
-- an alternative method). Yet another heuristic approach.

local nodes, node = nodes, node

local trace_directions   = false  trackers.register("typesetters.directions.math", function(v) trace_directions = v end)

local report_directions  = logs.reporter("typesetting","math directions")

local nuts               = nodes.nuts
local tonut              = nuts.tonut
local tonode             = nuts.tonode

local getnext            = nuts.getnext
local getchar            = nuts.getchar
local getid              = nuts.getid
local getlist            = nuts.getlist
local getattr            = nuts.getattr

local setchar            = nuts.setchar
local setlist            = nuts.setlist

local insertnodebefore   = nuts.insertbefore
local insertnodeafter    = nuts.insertafter

local nodecodes          = nodes.nodecodes
local enableaction       = nodes.tasks.enableaction

local glyph_code         = nodecodes.glyph
local hlist_code         = nodecodes.hlist
local vlist_code         = nodecodes.vlist

local nodepool           = nuts.pool

local new_direction      = nodepool.direction

local lefttoright_code   = nodes.dirvalues.lefttoright

local chardirections     = characters.directions
local charmirrors        = characters.mirrors
local charclasses        = characters.textclasses

local directions         = typesetters.directions or { }

local a_mathbidi         = attributes.private('mathbidi')

local function processmath(head)
    local current = head
    local start   = nil
    local stop    = nil
    local function capsulate()
        head = insertnodebefore(head,start,new_direction(lefttoright_code))
        insertnodeafter(head,stop,new_direction(lefttoright_code,true))
        if trace_directions then
            report_directions("reversed: %s",nodes.listtoutf(start,false,false,stop))
        end
        start = false
        stop  = nil
    end
    while current do
        local id = getid(current)
        if id == glyph_code then
            local char = getchar(current)
            local cdir = chardirections[char]
            if cdir == "en" or cdir == "an" then -- we could check for mathclass punctuation
                if not start then
                    start = current
                end
                stop = current
            else
                if not start then
                    -- nothing
                elseif start == stop then
                    start = nil
                else
                    capsulate()
                end
                if cdir == "on" then
                    local mirror = charmirrors[char]
                    if mirror then
                        local class = charclasses[char]
                        if class == "open" or class == "close" then
                            setchar(current,mirror)
                            if trace_directions then
                                report_directions("mirrored: %C to %C",char,mirror)
                            end
                        end
                    end
                end
            end
        elseif not start then
            -- nothing
            if id == hlist_code or id == vlist_code then
                local list = processmath(getlist(current))
                setlist(current,list)
            end
        elseif start == stop then
            start = nil
        else
            capsulate(head,start,stop)
            -- math can pack things into hlists .. we need to make sure we don't process
            -- too often: needs checking
            if id == hlist_code or id == vlist_code then
                local list = processmath(getlist(current))
                setlist(current,list)
            end
        end
        current = getnext(current)
    end
    if not start then
        -- nothing
    elseif start == stop then
        -- nothing
    else
        capsulate()
    end
    return head
end

local enabled = false

function directions.processmath(head) -- style, penalties
    if enabled then
        local a = getattr(head,a_mathbidi)
        if a and a > 0 then
            return processmath(head)
        end
    end
end

function directions.setmath(n)
    if not enabled and n and n > 0 then
        if trace_directions then
            report_directions("enabling directions handler")
        end
        enableaction("math","typesetters.directions.processmath")
        enabled = true
    end
end

interfaces.implement {
    name      = "setmathdirection",
    actions   = directions.setmath,
    arguments = "integer"
}