1 ------------------------------------------------------------------------
2 -- MAIN PROCESS STEP 3: insert \xkanjiskip (prefix: none)
3 ------------------------------------------------------------------------
4 local ltjs = luatexja.stack
6 local node_type = node.type
7 local node_new = node.new
8 local node_prev = node.prev
9 local node_next = node.next
10 local node_copy = node.copy
11 local has_attr = node.has_attribute
12 local set_attr = node.set_attribute
13 local node_insert_before = node.insert_before
14 local node_insert_after = node.insert_after
15 local node_hpack = node.hpack
16 local round = tex.round
18 local id_penalty = node.id('penalty')
19 local id_glyph = node.id('glyph')
20 local id_glue = node.id('glue')
21 local id_glue_spec = node.id('glue_spec')
22 local id_kern = node.id('kern')
23 local id_hlist = node.id('hlist')
24 local id_ins = node.id('ins')
25 local id_mark = node.id('mark')
26 local id_adjust = node.id('adjust')
27 local id_math = node.id('math')
28 local id_whatsit = node.id('whatsit')
30 local attr_icflag = luatexbase.attributes['ltj@icflag']
31 local attr_autospc = luatexbase.attributes['ltj@autospc']
32 local attr_autoxspc = luatexbase.attributes['ltj@autoxspc']
33 local attr_curjfnt = luatexbase.attributes['ltj@curjfnt']
34 local max_dimen = 1073741823
48 -- (glyph_node nr) ... (node nq) <GLUE> ,,, (node np)
49 local np, nq, nrc, nrf
53 local after_wchar = 2 -- nr is a Japanese glyph_node
54 local insert_skip = no_skip
57 local function is_japanese_glyph_node(p)
58 return p and (p.id==id_glyph)
59 and (p.font==has_attr(p,attr_curjfnt))
62 local function get_zero_glue()
63 local g = node_new(id_glue_spec)
64 g.width = 0; g.stretch_order = 0; g.stretch = 0
65 g.shrink_order = 0; g.shrink = 0
69 local function skip_table_to_spec(n)
70 local g = node_new(id_glue_spec)
71 local st = ltjs.get_skip_table(n, ltj.box_stack_level)
72 g.width = st.width; g.stretch = st.stretch; g.shrink = st.shrink
73 g.stretch_order = st.stretch_order; g.shrink_order = st.shrink_order
77 local function add_glue_spec(g,h)
79 g.width = g.width + h.width
80 if g.stretch_order<h.stretch_order then
81 g.stretch_order = h.stretch_order
83 elseif g.stretch_order==h.stretch_order then
84 g.stretch = g.stretch + h.stretch
86 if g.shrink_order<h.shrink_order then
87 g.shrink_order = h.shrink_order
89 elseif g.shrink_order==h.shrink_order then
90 g.shrink = g.shrink + h.shrink
94 -- lowest part of \xkanjiskip
95 local function get_xkanji_skip_from_jfm(pf)
97 local px = { ltj.font_metric_table[pf].size,
98 ltj.font_metric_table[pf].jfm }
99 local i = ltj.metrics[px[2]].xkanjiskip
101 return { round(i[1]*px[1]), round(i[2]*px[1]), round(i[3]*px[1]) }
108 local function insert_xkanjiskip_node(q, f, p)
109 if nr_spc[2] or np_spc[2] then
110 local g = node_new(id_glue); g.subtype = 0
111 if xkanji_skip.width==max_dimen then -- use xkanjiskip from JFM
112 local gx = node_new(id_glue_spec)
113 gx.stretch_order = 0; gx.shrink_order = 0
114 local ak = get_xkanji_skip_from_jfm(f)
116 gx.width = ak[1]; gx.stretch = ak[2]; gx.shrink = ak[3]
117 else gx = get_zero_glue() -- fallback
120 else g.spec=node_copy(xkanji_skip)
122 local h = node_prev(p)
123 if h and has_attr(h, attr_icflag)==TEMPORARY then
124 if h.id==id_kern then
125 g.spec.width = g.spec.width + h.kern
126 set_attr(g,attr_icflag,XKANJI_SKIP)
127 node_insert_after(head, q, g)
128 head = node.remove(head, h)
131 add_glue_spec(h.spec, g.spec)
132 node.free(g.spec); node.free(g)
135 set_attr(g,attr_icflag,XKANJI_SKIP)
136 node_insert_after(head, q, g)
141 local function insert_ascii_kanji_xkskip(q, p)
142 if ltjs.get_penalty_table('xsp', p.char, 3, ltj.box_stack_level)<=1 then return end
143 insert_xkanjiskip_node(q, p.font, p)
146 local function insert_kanji_ascii_xkskip(q, p)
149 while p.components and p.subtype
150 and math.floor(p.subtype/2)%2==1 do
151 p = p.components; c = p.char
153 if ltjs.get_penalty_table('xsp', c, 3, ltj.box_stack_level)%2 == 1 then
154 if ltjs.get_penalty_table('xsp', nrc, 3, ltj.box_stack_level)%2 == 0 then g = false end
157 if g then insert_xkanjiskip_node(q, nrf, p) end
160 local function set_insert_skip_after_achar(p)
162 while p.components and p.subtype
163 and math.floor(p.subtype/2)%2 == 1 do
164 p=node.tail(p.components); c = p.char
166 if ltjs.get_penalty_table('xsp', c, 3, ltj.box_stack_level)>=2 then
167 insert_skip = after_schar
169 insert_skip = no_skip
173 -- lowest part of \kanjiskip
174 local function get_kanji_skip_from_jfm(pf)
176 local px = { ltj.font_metric_table[pf].size,
177 ltj.font_metric_table[pf].jfm }
178 local i = ltj.metrics[px[2]].kanjiskip
180 return { round(i[1]*px[1]), round(i[2]*px[1]), round(i[3]*px[1]) }
187 local function insert_kanji_skip(ope, p)
188 local g = node_new(id_glue); g.subtype=0
189 if nr_spc[1] or np_spc[1] then
190 if kanji_skip.width==max_dimen then -- use kanjiskip from JFM
191 local gx = node_new(id_glue_spec);
192 gx.stretch_order = 0; gx.shrink_order = 0
193 local bk = get_kanji_skip_from_jfm(nrf)
194 local ak = get_kanji_skip_from_jfm(p.font)
197 gx.width = round(ltj.ja_diffmet_rule(bk[1], ak[1]))
198 gx.stretch = round(ltj.ja_diffmet_rule(bk[2], ak[2]))
199 gx.shrink = -round(ltj.ja_diffmet_rule(-bk[3], -ak[3]))
201 gx.width = bk[1]; gx.stretch = bk[2]; gx.shrink = bk[3]
204 gx.width = ak[1]; gx.stretch = ak[2]; gx.shrink = ak[3]
205 else gx = get_zero_glue() -- fallback
208 else g.spec=node_copy(kanji_skip)
210 else g.spec = get_zero_glue()
212 local h = node_prev(p)
213 if h and has_attr(h, attr_icflag)==TEMPORARY then
214 if h.id==id_kern then
215 g.spec.width = g.spec.width + h.kern
216 head = node.remove(head, h)
218 set_attr(g,attr_icflag,KANJI_SKIP)
221 add_glue_spec(h.spec, g.spec)
222 node.free(g.spec); node.free(g)
225 set_attr(g,attr_icflag,KANJI_SKIP)
230 -- When p is a glyph_node ...
231 local function insks_around_char()
232 if is_japanese_glyph_node(np) then
233 if insert_skip==after_wchar then
234 insert_kanji_skip(node_insert_before, np)
235 elseif insert_skip==after_schar then
236 insert_ascii_kanji_xkskip(nq, np)
238 insert_skip=after_wchar
239 nrc = np.char; nrf = np.font; nr_spc = np_spc
241 if insert_skip==after_wchar then
242 insert_kanji_ascii_xkskip(nq, np)
244 set_insert_skip_after_achar(np); nr_spc = np_spc
249 -- Return first and last glyph nodes in a hbox
250 local first_char = nil
251 local last_char = nil
252 local find_first_char = nil
253 local function check_box(box_ptr)
254 local p = box_ptr; local found_visible_node = false
256 find_first_char = false; first_char = nil; last_char = nil
260 if p.id==id_glyph then
262 if find_first_char then
263 first_char = p; find_first_char = false
265 last_char = p; found_visible_node = true; p=node_next(p)
266 if not p then return found_visible_node end
269 if p.id==id_hlist then
270 if has_attr(p, attr_icflag)==PACKED then
271 for q in node.traverse_id(id_glyph, p.head) do
272 if find_first_char then
273 first_char = q; find_first_char = false
275 last_char = q; found_visible_node = true
279 if check_box(p.head) then found_visible_node = true end
280 else if find_first_char then
281 find_first_char = false
287 elseif p.id==id_ins or p.id==id_mark
288 or p.id==id_adjust or p.id==id_whatsit
289 or p.id==id_penalty then
292 found_visible_node = true
293 if find_first_char then
294 find_first_char = false
301 return found_visible_node
304 -- When np is a hlist_node ...
305 local function insks_around_hbox()
307 find_first_char = true; first_char = nil; last_char = nil
308 if check_box(np.head) then
310 if is_japanese_glyph_node(first_char) then
311 nrc = first_char.char; nrf = first_char.font
312 if insert_skip==after_schar then
313 insert_ascii_kanji_xkskip(nq, first_char)
314 elseif insert_skip==after_wchar then
315 np_spc = { has_attr(first_char, attr_autospc)==1,
316 has_attr(first_char, attr_autoxspc)==1 }
317 insert_kanji_skip(node_insert_before, np)
319 insert_skip = after_wchar
320 elseif first_char then
321 if insert_skip==after_wchar then
322 insert_kanji_ascii_xkskip(nq, first_char)
324 set_insert_skip_after_achar(first_char)
327 if is_japanese_glyph_node(last_char) then
328 insert_skip = after_wchar
329 nrc = last_char.char; nrf = last_char.font
330 nr_spc = { has_attr(last_char, attr_autospc)==1,
331 has_attr(last_char, attr_autoxspc)==1 }
332 if is_japanese_glyph_node(node_next(np)) then
333 insert_kanji_skip(node_insert_after, np)
335 elseif last_char then
336 set_insert_skip_after_achar(last_char)
337 nr_spc = { has_attr(last_char, attr_autospc)==1,
338 has_attr(last_char, attr_autoxspc)==1 }
339 else insert_skip = no_skip
341 else insert_skip = no_skip
343 else insert_skip = no_skip
348 -- When np is a penalty ...
349 local function insks_around_penalty()
353 -- When np is a kern ...
355 local function insks_around_kern()
356 if np.subtype==1 then -- \kern or \/
357 local i = has_attr(np, attr_icflag)
358 if not i or i==FROM_JFM then -- \kern
359 insert_skip = no_skip
360 elseif i==ITALIC or i==LINE_END or i==TEMPORARY then
363 elseif np.subtype==2 then
364 -- (np = kern from \accent) .. (accent char) .. (kern from \accent) .. (glyph)
365 np = node_next(node_next(np))
366 else -- kern from TFM
371 -- When np is a math_node ...
372 local function insks_around_math()
373 local g = { char = -1 }
374 if (np.subtype==0) and (insert_skip==after_wchar) then
375 insert_kanji_ascii_xkskip(nq, g)
376 insert_skip = no_skip
378 nq = np; set_insert_skip_after_achar(g); nr_spc = np_spc
382 function ltj.int_insert_kanji_skip(ahead)
383 kanji_skip=skip_table_to_spec('kanjiskip')
384 xkanji_skip=skip_table_to_spec('xkanjiskip')
386 np = head; nq = nil; insert_skip = no_skip
388 np_spc = { (has_attr(np, attr_autospc)==1),
389 (has_attr(np, attr_autoxspc)==1) }
390 if np.id==id_glyph then
392 np_spc = { has_attr(np, attr_autospc)==1,
393 has_attr(np, attr_autoxspc)==1 }
394 insks_around_char(); np=node_next(np)
395 until (not np) or np.id~=id_glyph
397 if np.id==id_hlist then
399 elseif np.id==id_penalty then
400 insks_around_penalty()
401 elseif np.id==id_kern then
403 elseif np.id==id_math then
405 elseif np.id==id_ins or np.id==id_mark
406 or np.id==id_adjust or np.id==id_whatsit then
410 -- rule, disc, glue, margin_kern
411 insert_skip = no_skip