1if not modules then modules = { } end modules ['typo-dha'] = {
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
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
42local nodes, node = nodes, node
43
44local trace_directions = false trackers.register("typesetters.directions", function(v) trace_directions = v end)
45
46local report_directions = logs.reporter("typesetting","text directions")
47
48local nuts = nodes.nuts
49
50local getnext = nuts.getnext
51local getprev = nuts.getprev
52local getchar = nuts.getchar
53local getid = nuts.getid
54local getsubtype = nuts.getsubtype
55local getlist = nuts.getlist
56local getattr = nuts.getattr
57local getprop = nuts.getprop
58local getdirection = nuts.getdirection
59local isglyph = nuts.isglyph
60
61local setprop = nuts.setprop
62local setstate = nuts.setstate
63local setchar = nuts.setchar
64
65local insertnodebefore = nuts.insertbefore
66local insertnodeafter = nuts.insertafter
67local remove_node = nuts.remove
68local endofmath = nuts.endofmath
69
70local startofpar = nuts.startofpar
71
72local nodepool = nuts.pool
73
74local nodecodes = nodes.nodecodes
75local gluecodes = nodes.gluecodes
76local directioncodes = tex.directioncodes
77
78local glyph_code <const> = nodecodes.glyph
79local math_code <const> = nodecodes.math
80local kern_code <const> = nodecodes.kern
81local glue_code <const> = nodecodes.glue
82local dir_code <const> = nodecodes.dir
83local par_code <const> = nodecodes.par
84
85local parfillskip_code <const> = gluecodes.parfillskip
86
87local lefttoright_code <const> = directioncodes.lefttoright
88local righttoleft_code <const> = directioncodes.righttoleft
89
90local new_direction = nodepool.direction
91
92local insert = table.insert
93
94local fonthashes = fonts.hashes
95local fontchar = fonthashes.characters
96
97local chardirections = characters.directions
98local charmirrors = characters.mirrors
99local charclasses = characters.textclasses
100
101local directions = typesetters.directions
102local setcolor = directions.setcolor
103local getglobal = directions.getglobal
104
105local a_directions <const> = attributes.private('directions')
106
107local strip = false
108
109local s_isol = fonts.analyzers.states.isol
110
111local function stopdir(finish)
112 local n = new_direction(finish == righttoleft_code and righttoleft_code or lefttoright_code,true)
113 setprop(n,"direction",true)
114 return n
115end
116
117local function startdir(finish)
118 local n = new_direction(finish == righttoleft_code and righttoleft_code or lefttoright_code)
119 setprop(n,"direction",true)
120 return n
121end
122
123local function nextisright(current)
124 current = getnext(current)
125 local character, id = isglyph(current)
126 if character then
127 local direction = chardirections[character]
128 return direction == "r" or direction == "al" or direction == "an"
129 end
130end
131
132local function previsright(current)
133 current = getprev(current)
134 local character, id = isglyph(current)
135 if character then
136 local direction = chardirections[character]
137 return direction == "r" or direction == "al" or direction == "an"
138 end
139end
140
141local function process(start)
142
143 local head = start
144 local current = head
145 local autodir = 0
146 local embedded = 0
147 local override = 0
148 local pardir = 0
149 local textdir = 0
150 local done = false
151 local stack = { }
152 local top = 0
153 local obsolete = { }
154 local rlo = false
155 local lro = false
156 local prevattr = false
157 local fences = { }
158
159 while current do
160
161 local id = getid(current)
162 local next = getnext(current)
163 if id == math_code then
164 current = getnext(endofmath(next))
165 elseif getprop(current,"direction") then
166
167 current = next
168 else
169 local attr = getattr(current,a_directions)
170 if attr and attr > 0 then
171 if attr ~= prevattr then
172 if not getglobal(a) then
173 lro = false
174 rlo = false
175 end
176 prevattr = attr
177 end
178 end
179 local prop = true
180 if id == glyph_code then
181 if attr and attr > 0 then
182 local character, font = isglyph(current)
183 if character == 0 then
184
185
186 else
187 local direction = chardirections[character]
188 local reversed = false
189 if rlo or override > 0 then
190 if direction == "l" then
191 direction = "r"
192 reversed = true
193 end
194 elseif lro or override < 0 then
195 if direction == "r" or direction == "al" then
196 setstate(current,s_isol)
197 direction = "l"
198 reversed = true
199 end
200 end
201 if direction == "on" then
202 local mirror = charmirrors[character]
203 if mirror and fontchar[font][mirror] then
204 local class = charclasses[character]
205 if class == "open" then
206 if nextisright(current) then
207 setchar(current,mirror)
208
209 prop = "r"
210 elseif autodir < 0 then
211 setchar(current,mirror)
212
213 prop = "r"
214 else
215 mirror = false
216
217 prop = "l"
218 end
219 local fencedir = autodir == 0 and textdir or autodir
220 fences[#fences+1] = fencedir
221 elseif class == "close" and #fences > 0 then
222 local fencedir = fences[#fences]
223 fences[#fences] = nil
224 if fencedir < 0 then
225 setchar(current,mirror)
226
227 prop = "r"
228 else
229
230 prop = "l"
231 mirror = false
232 end
233 elseif autodir < 0 then
234 setchar(current,mirror)
235
236 prop = "r"
237 else
238
239 prop = "l"
240 mirror = false
241 end
242 else
243
244 end
245 if trace_directions then
246 setcolor(current,direction,false,mirror)
247 end
248 elseif direction == "l" then
249 if trace_directions then
250 setcolor(current,"l",reversed)
251 end
252
253 prop = "l"
254 elseif direction == "r" then
255 if trace_directions then
256 setcolor(current,"r",reversed)
257 end
258
259 prop = "r"
260 elseif direction == "en" then
261 if trace_directions then
262 setcolor(current,"l")
263 end
264
265 prop = "l"
266 elseif direction == "al" then
267 if trace_directions then
268 setcolor(current,"r")
269 end
270
271 prop = "r"
272 elseif direction == "an" then
273
274 if trace_directions then
275 setcolor(current,"l")
276 end
277
278 prop = "n"
279 elseif direction == "lro" then
280 top = top + 1
281 stack[top] = { override, embedded }
282 override = -1
283 obsolete[#obsolete+1] = current
284 goto obsolete
285 elseif direction == "rlo" then
286 top = top + 1
287 stack[top] = { override, embedded }
288 override = 1
289 obsolete[#obsolete+1] = current
290 goto obsolete
291 elseif direction == "lre" then
292 top = top + 1
293 stack[top] = { override, embedded }
294 embedded = 1
295 obsolete[#obsolete+1] = current
296 goto obsolete
297 elseif direction == "rle" then
298 top = top + 1
299 stack[top] = { override, embedded }
300 embedded = -1
301 obsolete[#obsolete+1] = current
302 goto obsolete
303 elseif direction == "pdf" then
304 if top > 0 then
305 local s = stack[top]
306 override = s[1]
307 embedded = s[2]
308 top = top - 1
309 else
310 override = 0
311 embedded = 0
312 end
313 obsolete[#obsolete+1] = current
314 goto obsolete
315 elseif trace_directions then
316 setcolor(current)
317
318 else
319
320 end
321 end
322 else
323
324 end
325 elseif id == glue_code then
326 if getsubtype(current) == parfillskip_code then
327
328 prop = "!"
329 else
330
331 prop = "g"
332 end
333 elseif id == kern_code then
334
335 prop = "k"
336 elseif id == dir_code then
337 local direction, pop = getdirection(current)
338 if direction == righttoleft_code then
339 if not pop then
340 autodir = -1
341 elseif embedded and embedded~= 0 then
342 autodir = embedded
343 else
344 autodir = 0
345 end
346 elseif direction == lefttoright_code then
347 if not pop then
348 autodir = 1
349 elseif embedded and embedded~= 0 then
350 autodir = embedded
351 else
352 autodir = 0
353 end
354 end
355 textdir = autodir
356
357 elseif id == par_code and startofpar(current) then
358 local direction = getdirection(current)
359 if direction == righttoleft_code then
360 autodir = -1
361 elseif direction == lefttoright_code then
362 autodir = 1
363 end
364 pardir = autodir
365 textdir = pardir
366
367 else
368
369 end
370 setprop(current,"direction",prop)
371 ::obsolete::
372 current = next
373 end
374 end
375
376
377
378
379 if done and strip then
380 local n = #obsolete
381 if n > 0 then
382 for i=1,n do
383 remove_node(head,obsolete[i],true)
384 end
385 if trace_directions then
386 report_directions("%s character nodes removed",n)
387 end
388 end
389 end
390
391 local state = false
392 local last = false
393 local collapse = true
394 current = head
395
396
397
398
399 while current do
400 local id = getid(current)
401 if id == math_code then
402
403 current = getnext(endofmath(getnext(current)))
404 else
405 local cp = getprop(current,"direction")
406 if cp == "n" then
407 local swap = state == "r"
408 if swap then
409 head = insertnodebefore(head,current,startdir(lefttoright_code))
410 end
411 setprop(current,"direction",true)
412 while true do
413 local n = getnext(current)
414 if n and getprop(n,"direction") == "n" then
415 current = n
416 setprop(current,"direction",true)
417 else
418 break
419 end
420 end
421 if swap then
422 head, current = insertnodeafter(head,current,stopdir(lefttoright_code))
423 end
424 elseif cp == "l" then
425 if state ~= "l" then
426 if state == "r" then
427 head = insertnodebefore(head,last or current,stopdir(righttoleft_code))
428 end
429 head = insertnodebefore(head,current,startdir(lefttoright_code))
430 state = "l"
431 done = true
432 end
433 last = false
434 elseif cp == "r" then
435 if state ~= "r" then
436 if state == "l" then
437 head = insertnodebefore(head,last or current,stopdir(lefttoright_code))
438 end
439 head = insertnodebefore(head,current,startdir(righttoleft_code))
440 state = "r"
441 done = true
442 end
443 last = false
444 elseif collapse then
445 if cp == "k" or cp == "g" then
446 last = last or current
447 else
448 last = false
449 end
450 else
451 if state == "r" then
452 head = insertnodebefore(head,current,stopdir(righttoleft_code))
453 elseif state == "l" then
454 head = insertnodebefore(head,current,stopdir(lefttoright_code))
455 end
456 state = false
457 last = false
458 end
459 setprop(current,"direction",true)
460 end
461 local next = getnext(current)
462 if next then
463 current = next
464 else
465 local sd = (state == "r" and stopdir(righttoleft_code)) or (state == "l" and stopdir(lefttoright_code))
466 if sd then
467 if id == glue_code and getsubtype(current) == parfillskip_code then
468 head = insertnodebefore(head,current,sd)
469 else
470 head = insertnodeafter(head,current,sd)
471 end
472 end
473 break
474 end
475 end
476
477 return head
478
479end
480
481directions.installhandler(interfaces.variables.default,process)
482 |