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
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
|
if not modules then modules = { } end modules ['typo-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"
}
-- When we started with this, there were some issues in luatex so we needed to take care of
-- intereferences. Some has been improved but we stil might end up with each node having a
-- dir property. Now, the biggest problem is that there is an official bidi algorithm but
-- some searching on the web shows that there are many confusing aspects and therefore
-- proposals circulate about (sometimes imcompatible ?) improvements. In the end it all boils
-- down to the lack of willingness to tag an input source. Of course tagging of each number
-- and fenced strip is somewhat over the top, but now it has to be captured in logic. Texies
-- normally have no problem with tagging but we need to handle any input. So, what we have
-- done here (over the years) is starting from what we expect to see happen, especially with
-- respect to punctation, numbers and fences. Eventually alternative algorithms will be provides
-- so that users can choose (the reason why suggestion sfor improvements circulate on the web
-- is that it is non trivial to predict the expected behaviour so one hopes that the ditor
-- and the rest of the machinery match somehow. Anyway, the fun of tex is that it has no hard
-- coded behavior. And ... we also want to have more debugging and extras and ... so we want
-- a flexible approach. In the end we will have:
--
-- = full tagging (mechanism turned off)
-- = half tagging (the current implementation)
-- = unicode version x interpretation (several depending on the evolution)
local next, type = next, type
local format, insert, sub, find, match = string.format, table.insert, string.sub, string.find, string.match
local utfchar = utf.char
local formatters = string.formatters
local nodes, node = nodes, node
local trace_textdirections = false trackers.register("typesetters.directions.text", function(v) trace_textdirections = v end)
local trace_mathdirections = false trackers.register("typesetters.directions.math", function(v) trace_mathdirections = v end)
local trace_directions = false trackers.register("typesetters.directions", function(v) trace_textdirections = v trace_mathdirections = v end)
local report_textdirections = logs.reporter("typesetting","text directions")
local report_mathdirections = logs.reporter("typesetting","math directions")
local traverse_id = node.traverse_id
local insert_node_before = node.insert_before
local insert_node_after = node.insert_after
local remove_node = nodes.remove
local end_of_math = nodes.end_of_math
local texsetattribute = tex.setattribute
local texsetcount = tex.setcount
local unsetvalue = attributes.unsetvalue
local hasbit = number.hasbit
local nodecodes = nodes.nodecodes
local whatcodes = nodes.whatcodes
local mathcodes = nodes.mathcodes
local tasks = nodes.tasks
local tracers = nodes.tracers
local setcolor = tracers.colors.set
local resetcolor = tracers.colors.reset
local glyph_code = nodecodes.glyph
local whatsit_code = nodecodes.whatsit
local math_code = nodecodes.math
local penalty_code = nodecodes.penalty
local kern_code = nodecodes.kern
local glue_code = nodecodes.glue
local hlist_code = nodecodes.hlist
local vlist_code = nodecodes.vlist
local localpar_code = whatcodes.localpar
local dir_code = whatcodes.dir
local nodepool = nodes.pool
local new_textdir = nodepool.textdir
local fonthashes = fonts.hashes
local fontdata = fonthashes.identifiers
local fontchar = fonthashes.characters
local chardirections = characters.directions
local charmirrors = characters.mirrors
local charclasses = characters.textclasses
local directions = typesetters.directions or { }
typesetters.directions = directions
local a_state = attributes.private('state')
local a_directions = attributes.private('directions')
local a_mathbidi = attributes.private('mathbidi')
local strip = false
local s_isol = fonts.analyzers.states.isol
local variables = interfaces.variables
local v_global = variables["global"]
local v_local = variables["local"]
local v_on = variables.on
local v_yes = variables.yes
local m_enabled = 2^6 -- 64
local m_global = 2^7
local m_fences = 2^8
local handlers = { }
local methods = { }
local lastmethod = 0
local function installhandler(name,handler)
local method = methods[name]
if not method then
lastmethod = lastmethod + 1
method = lastmethod
methods[name] = method
end
handlers[method] = handler
return method
end
directions.handlers = handlers
directions.installhandler = installhandler
local function tomode(specification)
local scope = specification.scope
local mode
if scope == v_global or scope == v_on then
mode = m_enabled + m_global
elseif scope == v_local then
mode = m_enabled
else
return 0
end
local method = methods[specification.method]
if method then
mode = mode + method
else
return 0
end
if specification.fences == v_yes then
mode = mode + m_fences
end
return mode
end
local function getglobal(a)
return a and a > 0 and hasbit(a,m_global)
end
local function getfences(a)
return a and a > 0 and hasbit(a,m_fences)
end
local function getmethod(a)
return a and a > 0 and a % m_enabled or 0
end
directions.tomode = tomode
directions.getglobal = getglobal
directions.getfences = getfences
directions.getmethod = getmethod
directions.installhandler = installhandler
-- beware: in dha we have character properties and in dua|b we have direction properties
function directions.setcolor(current,direction,reversed,mirror)
if mirror then
setcolor(current,"bidi:mirrored")
elseif direction == "l" then
if reversed then
setcolor(current,"bidi:left:reversed")
else
setcolor(current,"bidi:left:original")
end
elseif direction == "r" then
if reversed then
setcolor(current,"bidi:right:reversed")
else
setcolor(current,"bidi:right:original")
end
else
resetcolor(current)
end
end
function commands.getbidimode(specification)
context(tomode(specification)) -- hash at tex end
end
local enabled = false
local starttiming = statistics.starttiming
local stoptiming = statistics.stoptiming
function directions.process(namespace,attribute,head) -- for the moment nodes and not nuts
if not head.next then
return head, false
end
local attr = head[a_directions]
if not attr or attr == 0 then
return head, false
end
local method = getmethod(attr)
local handler = handlers[method]
if not handler then
return head, false
end
starttiming(directions)
local head, done = handler(namespace,attribute,head)
stoptiming(directions)
return head, done
end
statistics.register("text directions", function()
if enabled then
return statistics.elapsedseconds(directions)
end
end)
-- function directions.enable()
-- tasks.enableaction("processors","directions.handler")
-- end
function directions.set(n) -- todo: names and numbers
if not enabled then
if trace_textdirections then
report_textdirections("enabling directions handler")
end
tasks.enableaction("processors","typesetters.directions.handler")
enabled = true
end
if not n or n == 0 then
n = unsetvalue
-- maybe tracing
end
texsetattribute(a_directions,n)
end
commands.setdirection = directions.set
directions.handler = nodes.installattributehandler {
name = "directions",
namespace = directions,
processor = directions.process,
}
|