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
|
--
-- This is file `babel-bidi-basic-r.lua',
-- generated with the docstrip utility.
--
-- The original source files were:
--
-- babel.dtx (with options: `basic-r')
--
--
-- Copyright (C) 2012-2023 Javier Bezos and Johannes L. Braams.
-- Copyright (C) 1989-2012 Johannes L. Braams and
-- any individual authors listed elsewhere in this file.
-- All rights reserved.
--
--
-- This file is part of the Babel system.
-- --------------------------------------
--
-- It may be distributed and/or modified under the
-- conditions of the LaTeX Project Public License, either version 1.3
-- of this license or (at your option) any later version.
-- The latest version of this license is in
-- http://www.latex-project.org/lppl.txt
-- and version 1.3 or later is part of all distributions of LaTeX
-- version 2003/12/01 or later.
--
-- This work has the LPPL maintenance status "maintained".
--
-- The Current Maintainer of this work is Javier Bezos.
--
-- The list of derived (unpacked) files belonging to the distribution
-- and covered by LPPL is defined by the unpacking scripts (with
-- extension |.ins|) which are part of the distribution.
--
Babel = Babel or {}
Babel.bidi_enabled = true
require('babel-data-bidi.lua')
local characters = Babel.characters
local ranges = Babel.ranges
local DIR = node.id("dir")
local function dir_mark(head, from, to, outer)
dir = (outer == 'r') and 'TLT' or 'TRT' -- ie, reverse
local d = node.new(DIR)
d.dir = '+' .. dir
node.insert_before(head, from, d)
d = node.new(DIR)
d.dir = '-' .. dir
node.insert_after(head, to, d)
end
function Babel.bidi(head, ispar)
local first_n, last_n -- first and last char with nums
local last_es -- an auxiliary 'last' used with nums
local first_d, last_d -- first and last char in L/R block
local dir, dir_real
local strong = ('TRT' == tex.pardir) and 'r' or 'l'
local strong_lr = (strong == 'l') and 'l' or 'r'
local outer = strong
local new_dir = false
local first_dir = false
local inmath = false
local last_lr
local type_n = ''
for item in node.traverse(head) do
-- three cases: glyph, dir, otherwise
if item.id == node.id'glyph'
or (item.id == 7 and item.subtype == 2) then
local itemchar
if item.id == 7 and item.subtype == 2 then
itemchar = item.replace.char
else
itemchar = item.char
end
local chardata = characters[itemchar]
dir = chardata and chardata.d or nil
if not dir then
for nn, et in ipairs(ranges) do
if itemchar < et[1] then
break
elseif itemchar <= et[2] then
dir = et[3]
break
end
end
end
dir = dir or 'l'
if inmath then dir = ('TRT' == tex.mathdir) and 'r' or 'l' end
if new_dir then
attr_dir = 0
for at in node.traverse(item.attr) do
if at.number == Babel.attr_dir then
attr_dir = at.value % 3
end
end
if attr_dir == 1 then
strong = 'r'
elseif attr_dir == 2 then
strong = 'al'
else
strong = 'l'
end
strong_lr = (strong == 'l') and 'l' or 'r'
outer = strong_lr
new_dir = false
end
if dir == 'nsm' then dir = strong end -- W1
dir_real = dir -- We need dir_real to set strong below
if dir == 'al' then dir = 'r' end -- W3
if strong == 'al' then
if dir == 'en' then dir = 'an' end -- W2
if dir == 'et' or dir == 'es' then dir = 'on' end -- W6
strong_lr = 'r' -- W3
end
elseif item.id == node.id'dir' and not inmath then
new_dir = true
dir = nil
elseif item.id == node.id'math' then
inmath = (item.subtype == 0)
else
dir = nil -- Not a char
end
if dir == 'en' or dir == 'an' or dir == 'et' then
if dir ~= 'et' then
type_n = dir
end
first_n = first_n or item
last_n = last_es or item
last_es = nil
elseif dir == 'es' and last_n then -- W3+W6
last_es = item
elseif dir == 'cs' then -- it's right - do nothing
elseif first_n then -- & if dir = any but en, et, an, es, cs, inc nil
if strong_lr == 'r' and type_n ~= '' then
dir_mark(head, first_n, last_n, 'r')
elseif strong_lr == 'l' and first_d and type_n == 'an' then
dir_mark(head, first_n, last_n, 'r')
dir_mark(head, first_d, last_d, outer)
first_d, last_d = nil, nil
elseif strong_lr == 'l' and type_n ~= '' then
last_d = last_n
end
type_n = ''
first_n, last_n = nil, nil
end
if dir == 'l' or dir == 'r' then
if dir ~= outer then
first_d = first_d or item
last_d = item
elseif first_d and dir ~= strong_lr then
dir_mark(head, first_d, last_d, outer)
first_d, last_d = nil, nil
end
end
if dir and not last_lr and dir ~= 'l' and outer == 'r' then
item.char = characters[item.char] and
characters[item.char].m or item.char
elseif (dir or new_dir) and last_lr ~= item then
local mir = outer .. strong_lr .. (dir or outer)
if mir == 'rrr' or mir == 'lrr' or mir == 'rrl' or mir == 'rlr' then
for ch in node.traverse(node.next(last_lr)) do
if ch == item then break end
if ch.id == node.id'glyph' and characters[ch.char] then
ch.char = characters[ch.char].m or ch.char
end
end
end
end
if dir == 'l' or dir == 'r' then
last_lr = item
strong = dir_real -- Don't search back - best save now
strong_lr = (strong == 'l') and 'l' or 'r'
elseif new_dir then
last_lr = nil
end
end
if last_lr and outer == 'r' then
for ch in node.traverse_id(node.id'glyph', node.next(last_lr)) do
if characters[ch.char] then
ch.char = characters[ch.char].m or ch.char
end
end
end
if first_n then
dir_mark(head, first_n, last_n, outer)
end
if first_d then
dir_mark(head, first_d, last_d, outer)
end
return node.prev(head) or head
end
|