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
76
77local glyph_code = nodecodes.glyph
78local math_code = nodecodes.math
79local kern_code = nodecodes.kern
80local glue_code = nodecodes.glue
81local dir_code = nodecodes.dir
82local par_code = nodecodes.par
83
84local dirvalues = nodes.dirvalues
85local lefttoright_code = dirvalues.lefttoright
86local righttoleft_code = dirvalues.righttoleft
87
88local parfillskip_code = gluecodes.parfillskip
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 = 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 if id == glyph_code then
180 if attr and attr > 0 then
181 local character, font = isglyph(current)
182 if character == 0 then
183
184 setprop(current,"direction",true)
185 else
186 local direction = chardirections[character]
187 local reversed = false
188 if rlo or override > 0 then
189 if direction == "l" then
190 direction = "r"
191 reversed = true
192 end
193 elseif lro or override < 0 then
194 if direction == "r" or direction == "al" then
195 setstate(current,s_isol)
196 direction = "l"
197 reversed = true
198 end
199 end
200 if direction == "on" then
201 local mirror = charmirrors[character]
202 if mirror and fontchar[font][mirror] then
203 local class = charclasses[character]
204 if class == "open" then
205 if nextisright(current) then
206 setchar(current,mirror)
207 setprop(current,"direction","r")
208 elseif autodir < 0 then
209 setchar(current,mirror)
210 setprop(current,"direction","r")
211 else
212 mirror = false
213 setprop(current,"direction","l")
214 end
215 local fencedir = autodir == 0 and textdir or autodir
216 fences[#fences+1] = fencedir
217 elseif class == "close" and #fences > 0 then
218 local fencedir = fences[#fences]
219 fences[#fences] = nil
220 if fencedir < 0 then
221 setchar(current,mirror)
222 setprop(current,"direction","r")
223 else
224 setprop(current,"direction","l")
225 mirror = false
226 end
227 elseif autodir < 0 then
228 setchar(current,mirror)
229 setprop(current,"direction","r")
230 else
231 setprop(current,"direction","l")
232 mirror = false
233 end
234 else
235 setprop(current,"direction",true)
236 end
237 if trace_directions then
238 setcolor(current,direction,false,mirror)
239 end
240 elseif direction == "l" then
241 if trace_directions then
242 setcolor(current,"l",reversed)
243 end
244 setprop(current,"direction","l")
245 elseif direction == "r" then
246 if trace_directions then
247 setcolor(current,"r",reversed)
248 end
249 setprop(current,"direction","r")
250 elseif direction == "en" then
251 if trace_directions then
252 setcolor(current,"l")
253 end
254 setprop(current,"direction","l")
255 elseif direction == "al" then
256 if trace_directions then
257 setcolor(current,"r")
258 end
259 setprop(current,"direction","r")
260 elseif direction == "an" then
261
262 if trace_directions then
263 setcolor(current,"l")
264 end
265 setprop(current,"direction","n")
266 elseif direction == "lro" then
267 top = top + 1
268 stack[top] = { override, embedded }
269 override = -1
270 obsolete[#obsolete+1] = current
271 elseif direction == "rlo" then
272 top = top + 1
273 stack[top] = { override, embedded }
274 override = 1
275 obsolete[#obsolete+1] = current
276 elseif direction == "lre" then
277 top = top + 1
278 stack[top] = { override, embedded }
279 embedded = 1
280 obsolete[#obsolete+1] = current
281 elseif direction == "rle" then
282 top = top + 1
283 stack[top] = { override, embedded }
284 embedded = -1
285 obsolete[#obsolete+1] = current
286 elseif direction == "pdf" then
287 if top > 0 then
288 local s = stack[top]
289 override = s[1]
290 embedded = s[2]
291 top = top - 1
292 else
293 override = 0
294 embedded = 0
295 end
296 obsolete[#obsolete+1] = current
297 elseif trace_directions then
298 setcolor(current)
299 setprop(current,"direction",true)
300 else
301 setprop(current,"direction",true)
302 end
303 end
304 else
305 setprop(current,"direction",true)
306 end
307 elseif id == glue_code then
308 if getsubtype(current) == parfillskip_code then
309 setprop(current,"direction",'!')
310 else
311 setprop(current,"direction",'g')
312 end
313 elseif id == kern_code then
314 setprop(current,"direction",'k')
315 elseif id == dir_code then
316 local direction, pop = getdirection(current)
317 if direction == righttoleft_code then
318 if not pop then
319 autodir = -1
320 elseif embedded and embedded~= 0 then
321 autodir = embedded
322 else
323 autodir = 0
324 end
325 elseif direction == lefttoright_code then
326 if not pop then
327 autodir = 1
328 elseif embedded and embedded~= 0 then
329 autodir = embedded
330 else
331 autodir = 0
332 end
333 end
334 textdir = autodir
335 setprop(current,"direction",true)
336 elseif id == par_code and startofpar(current) then
337 local direction = getdirection(current)
338 if direction == righttoleft_code then
339 autodir = -1
340 elseif direction == lefttoright_code then
341 autodir = 1
342 end
343 pardir = autodir
344 textdir = pardir
345 setprop(current,"direction",true)
346 else
347 setprop(current,"direction",true)
348 end
349 current = next
350 end
351 end
352
353
354
355
356 if done and strip then
357 local n = #obsolete
358 if n > 0 then
359 for i=1,n do
360 remove_node(head,obsolete[i],true)
361 end
362 if trace_directions then
363 report_directions("%s character nodes removed",n)
364 end
365 end
366 end
367
368 local state = false
369 local last = false
370 local collapse = true
371 current = head
372
373
374
375
376 while current do
377 local id = getid(current)
378 if id == math_code then
379
380 current = getnext(endofmath(getnext(current)))
381 else
382 local cp = getprop(current,"direction")
383 if cp == "n" then
384 local swap = state == "r"
385 if swap then
386 head = insertnodebefore(head,current,startdir(lefttoright_code))
387 end
388 setprop(current,"direction",true)
389 while true do
390 local n = getnext(current)
391 if n and getprop(n,"direction") == "n" then
392 current = n
393 setprop(current,"direction",true)
394 else
395 break
396 end
397 end
398 if swap then
399 head, current = insertnodeafter(head,current,stopdir(lefttoright_code))
400 end
401 elseif cp == "l" then
402 if state ~= "l" then
403 if state == "r" then
404 head = insertnodebefore(head,last or current,stopdir(righttoleft_code))
405 end
406 head = insertnodebefore(head,current,startdir(lefttoright_code))
407 state = "l"
408 done = true
409 end
410 last = false
411 elseif cp == "r" then
412 if state ~= "r" then
413 if state == "l" then
414 head = insertnodebefore(head,last or current,stopdir(lefttoright_code))
415 end
416 head = insertnodebefore(head,current,startdir(righttoleft_code))
417 state = "r"
418 done = true
419 end
420 last = false
421 elseif collapse then
422 if cp == "k" or cp == "g" then
423 last = last or current
424 else
425 last = false
426 end
427 else
428 if state == "r" then
429 head = insertnodebefore(head,current,stopdir(righttoleft_code))
430 elseif state == "l" then
431 head = insertnodebefore(head,current,stopdir(lefttoright_code))
432 end
433 state = false
434 last = false
435 end
436 setprop(current,"direction",true)
437 end
438 local next = getnext(current)
439 if next then
440 current = next
441 else
442 local sd = (state == "r" and stopdir(righttoleft_code)) or (state == "l" and stopdir(lefttoright_code))
443 if sd then
444 if id == glue_code and getsubtype(current) == parfillskip_code then
445 head = insertnodebefore(head,current,sd)
446 else
447 head = insertnodeafter(head,current,sd)
448 end
449 end
450 break
451 end
452 end
453
454 return head
455
456end
457
458directions.installhandler(interfaces.variables.default,process)
459 |