2 -- luatexja/jfmglue.lua
4 luatexbase.provides_module({
5 name = 'luatexja.jfmglue',
8 description = 'Insertion process of JFM glues and kanjiskip',
10 module('luatexja.jfmglue', package.seeall)
11 local err, warn, info, log = luatexbase.errwarinf(_NAME)
13 local ltjb = luatexja.base
14 local ltjs = luatexja.stack
16 local node_type = node.type
17 local node_new = node.new
18 local node_remove = node.remove
19 local node_prev = node.prev
20 local node_next = node.next
21 local node_copy = node.copy
22 local node_tail = node.tail
23 local node_free = node.free
24 local has_attr = node.has_attribute
25 local set_attr = node.set_attribute
26 local node_insert_before = node.insert_before
27 local node_insert_after = node.insert_after
28 local round = tex.round
30 local id_glyph = node.id('glyph')
31 local id_hlist = node.id('hlist')
32 local id_vlist = node.id('vlist')
33 local id_rule = node.id('rule')
34 local id_ins = node.id('ins')
35 local id_mark = node.id('mark')
36 local id_adjust = node.id('adjust')
37 local id_disc = node.id('disc')
38 local id_whatsit = node.id('whatsit')
39 local id_math = node.id('math')
40 local id_glue = node.id('glue')
41 local id_kern = node.id('kern')
42 local id_penalty = node.id('penalty')
44 local id_glue_spec = node.id('glue_spec')
45 local id_jglyph = node.id('glyph') + 256
46 local id_box_like = node.id('hlist') + 256
47 local id_pbox = node.id('hlist') + 512
48 local sid_user = node.subtype('user_defined')
63 local attr_jchar_class = luatexbase.attributes['ltj@charclass']
64 local attr_curjfnt = luatexbase.attributes['ltj@curjfnt']
65 local attr_icflag = luatexbase.attributes['ltj@icflag']
66 local attr_autospc = luatexbase.attributes['ltj@autospc']
67 local attr_autoxspc = luatexbase.attributes['ltj@autoxspc']
68 local max_dimen = 1073741823
72 -------------------- Helper functions
74 local function find_char_class(c,m)
75 return m.chars[c] or 0
78 local function get_zero_glue()
79 local g = node_new(id_glue_spec)
80 g.width = 0; g.stretch_order = 0; g.stretch = 0
81 g.shrink_order = 0; g.shrink = 0
85 local function skip_table_to_spec(n)
86 local g = node_new(id_glue_spec)
87 local st = ltjs.get_skip_table(n, ltj.box_stack_level)
88 g.width = st.width; g.stretch = st.stretch; g.shrink = st.shrink
89 g.stretch_order = st.stretch_order; g.shrink_order = st.shrink_order
94 local function add_penalty(p,e)
95 if p.penalty>=10000 then
96 if e<=-10000 then p.penalty = 0 end
97 elseif p.penalty<=-10000 then
98 if e>=10000 then p.penalty = 0 end
100 p.penalty = p.penalty + e
101 if p.penalty>=10000 then p.penalty = 10000
102 elseif p.penalty<=-10000 then p.penalty = -10000 end
108 diffmet_rule = math.two_average
109 function math.two_add(a,b) return a+b end
110 function math.two_average(a,b) return (a+b)/2 end
112 -------------------- idea
113 -- 2 node の間に glue/kern/penalty を挿入する.
114 -- 基本方針: char node q と char node p の間
117 -- first: 最初の node,nuc: p,last: 最後の node
120 -- 実際の glue は Np.last, Nq.first の間に挿入される
121 -- Bp: Np.last, Nq.first の間の penalty node 達の配列
124 -- node x が non-char node のときは,x のみ
125 -- x が char_node のときは,
126 -- - x が \accent の第二引数だったとき
127 -- [kern2 kern y kern2] x の 3 node が核に加わる
128 -- - x の直後に \/ 由来 kern があったとき
129 -- その \/ 由来の kern が核に加わる
131 -- ins, mark, adjust, whatsit, penalty
133 -- Nq.last .. + .. Bp.first .... Bp[last] .... * .. Np.first
134 -- +: kern from LINEEND はここに入る
135 -- *: jfm glue はここに入る
137 local head -- the head of current list
138 local last -- the last node of current list
139 local lp -- 外側での list 走査時のカーソル
142 local widow_Bp, widow_Np -- \jcharwidowpenalty 挿入位置管理用
144 local ihb_flag -- JFM グルー挿入抑止用 flag
145 -- on: \inhibitglue 指定時,hlist の周囲
147 -------------------- hlist 内の文字の検索
149 local first_char, last_char, find_first_char
151 local function check_box(box_ptr, box_end)
152 local p = box_ptr; local found_visible_node = false
154 find_first_char = false; first_char = nil; last_char = nil
161 p = node_next(node_next(node_next(p))); pid = p.id
162 elseif has_attr(p, attr_icflag)==ITALIC then
163 p = node_next(p); pid = p.id
166 if pid==id_glyph then
168 if find_first_char then
169 first_char = p; find_first_char = false
171 last_char = p; found_visible_node = true; p=node_next(p)
172 if not p then return found_visible_node end
175 if pid==id_hlist then
176 if has_attr(p, attr_icflag)==PACKED then
177 for q in node.traverse_id(id_glyph, p.head) do
178 if find_first_char then
179 first_char = q; find_first_char = false
181 last_char = q; found_visible_node = true
185 if check_box(p.head) then found_visible_node = true end
186 else if find_first_char then
187 find_first_char = false
193 elseif not (pid==id_ins or pid==id_mark
194 or pid==id_adjust or pid==id_whatsit
195 or pid==id_penalty) then
196 found_visible_node = true
197 if find_first_char then
198 find_first_char = false
205 return found_visible_node
208 -------------------- Np の計算と情報取得
210 local function check_next_ickern()
211 if lp.id == id_kern and has_attr(lp, attr_icflag)==ITALIC then
212 Np.last = lp; lp = node_next(lp)
213 else Np.last = Np.nuc end
216 local function calc_np()
217 -- We assume lp = node_next(Np.last)
220 Nq = Np; Bp = {}; Bp[0] = 0; Np = {}; ihb_flag = false
222 lpi = lp.id; lpa = has_attr(lp, attr_icflag) or 0
223 if lp==last then Np = nil; return
224 elseif lpi == id_ins or lpi == id_mark or lpi == id_adjust then
226 elseif lpi == id_penalty then
227 table.insert(Bp, lp); Bp[0] = Bp[0] + 1; lp = node_next(lp)
228 elseif lpi == id_whatsit then
229 if lp.subtype==sid_user and lp.user_id==30111 then
230 local lq = node_next(lp)
231 head = node_remove(head, lp); lp = lq; ihb_flag = true
235 else -- a `cluster' is found
238 if lpa == BOXBDD then
239 local lq = node_next(lp)
240 head = node_remove(head, lp); lp = lq
242 Np.id = id_pbox; Np.nuc = lp
243 while lp~=last and lpa>=2 and lpa~=7 do
244 lp = node_next(lp); lpa = has_attr(lp, attr_icflag) or 0
246 Np.last = lp; check_next_ickern(); return
248 elseif lpi == id_glyph then -- id_[j]glyph
249 if lp.font == has_attr(lp, attr_curjfnt) then Np.id = id_jglyph
250 else Np.id = id_glyph end
251 Np.nuc = lp; lp = node_next(lp); check_next_ickern(); return
252 elseif lpi == id_hlist then -- hlist
253 Np.nuc = lp; Np.last = lp
254 if lp.shift~=0 then Np.id = id_box_like
256 lp = node_next(lp); return
257 elseif lpi == id_vlist or lpi == id_rule then -- id_box_like
258 Np.nuc = lp; Np.last = lp; Np.id = id_box_like; break
259 elseif lpi == id_math then -- id_math
260 Np.nuc = lp; lp = node_next(lp)
261 while lp.id~=id_math do lp = node_next(lp) end
263 elseif lpi == id_kern and lp.subtype==2 then -- id_kern
264 lp = node_next(node_next(node_next(lp)))
265 Np.nuc = lp ; lp = node_next(lp); check_next_ickern(); return
266 else -- id_disc, id_glue, id_kern
271 Np.last = lp; Np.id = lpi; lp = node_next(lp)
274 -- extract informations from Np
275 -- We think that "Np is a Japanese character" if Np.met~=nil,
276 -- "Np is an alphabetic character" if Np.pre~=nil,
277 -- "Np is not a character" otherwise.
280 local function set_np_xspc_jachar(c,x)
281 Np.class = has_attr(x, attr_jchar_class)
283 local z = ltj.font_metric_table[x.font]
285 Np.met = ltj.metrics[z.jfm]
287 Np.pre = ltjs.get_penalty_table('pre', c, 0, ltj.box_stack_level)
288 Np.post = ltjs.get_penalty_table('post', c, 0, ltj.box_stack_level)
289 z = find_char_class('lineend', Np.met)
290 local y = Np.met.char_type[Np.class]
291 if y.kern and y.kern[z] then
292 Np.lend = round(Np.size*y.kern[z])
296 y = ltjs.get_penalty_table('xsp', c, 3, ltj.box_stack_level)
297 Np.xspc_before = (y>=2)
298 Np.xspc_after = (y%2==1)
299 Np.auto_kspc = (has_attr(x, attr_autospc)==1)
300 Np.auto_xspc = (has_attr(x, attr_autoxspc)==1)
304 local ligature_head = 1
305 local ligature_tail = 2
306 local function set_np_xspc_alchar(c,x, lig)
308 if lig == ligature_head then
309 while x.components and x.subtype and math.floor(x.subtype/2)%2==1 do
310 x = x.components; c = x.char
313 while x.components and x.subtype and math.floor(x.subtype/2)%2==1 do
314 x = node_tail(x.components); c = x.char
317 Np.pre = ltjs.get_penalty_table('pre', c, 0, ltj.box_stack_level)
318 Np.post = ltjs.get_penalty_table('post', c, 0, ltj.box_stack_level)
320 Np.pre = 0; Np.post = 0
323 local y = ltjs.get_penalty_table('xsp', c, 3, ltj.box_stack_level)
324 Np.xspc_before = (y%2==1)
325 Np.xspc_after = (y>=2)
326 Np.auto_xspc = (has_attr(x, attr_autoxspc)==1)
330 local function extract_np()
332 if Np.id == id_jglyph then
333 set_np_xspc_jachar(x.char, x)
334 elseif Np.id == id_glyph then
335 set_np_xspc_alchar(x.char, x, ligature_head)
336 elseif Np.id == id_hlist then
337 find_first_char = true; first_char = nil; last_char = nil
338 if check_box(x.head, nil) then
340 if first_char.font == has_attr(first_char, attr_curjfnt) then
341 set_np_xspc_jachar(first_char.char,first_char)
343 set_np_xspc_alchar(first_char.char,first_char, ligature_head)
347 elseif Np.id == id_pbox then -- mikann
348 find_first_char = true; first_char = nil; last_char = nil
349 if check_box(Np.first, Np.last) then
351 if first_char.font == has_attr(first_char, attr_curjfnt) then
352 set_np_xspc_jachar(first_char.char,first_char)
354 set_np_xspc_alchar(first_char.char,first_char, ligature_head)
358 elseif Np.id == id_disc then
359 find_first_char = true; first_char = nil; last_char = nil
360 if check_box(x.replace, nil) then
362 if first_char.font == has_attr(first_char, attr_curjfnt) then
363 set_np_xspc_jachar(first_char.char,first_char)
365 set_np_xspc_alchar(first_char.char,first_char, ligature_head)
369 elseif Np.id == id_math then
370 set_np_xspc_alchar(-1, x)
374 -- change the information for the next loop
375 -- (will be done if Np is an alphabetic character or a hlist)
376 local function after_hlist()
378 if last_char.font == has_attr(last_char, attr_curjfnt) then
379 set_np_xspc_jachar(last_char.char,last_char, ligature_after)
381 set_np_xspc_alchar(last_char.char,last_char, ligature_after)
384 Np.pre = nil; Np.met = nil
387 local function after_alchar()
389 set_np_xspc_alchar(x.char,x, ligature_after)
393 -------------------- 最下層の処理
395 local function lineend_fix(g)
396 if g and g.id==id_kern then
398 elseif Nq.lend and Nq.lend~=0 then
400 g = node_new(id_kern); g.subtype = 1
401 g.kern = -Nq.lend; set_attr(g, attr_icflag, LINEEND)
402 elseif g.id==id_kern then
403 g.kern = g.kern - Nq.lend
405 g.spec.width = g.spec.width - Nq.lend
411 -- change penalties (or create a new penalty, if needed)
412 local function handle_penalty_normal(pre, post, g)
413 local a = (pre or 0) + (post or 0)
415 if (a~=0 and not(g and g.id==id_kern)) or Nq.lend~=0 then
416 local p = node_new(id_penalty)
417 if a<-10000 then a = -10000 elseif a>10000 then a = 10000 end
419 head = node_insert_before(head, Np.first, p)
420 Bp[1] = p; Bp[0] = 1; set_attr(p, attr_icflag, KINSOKU)
423 for i, v in ipairs(Bp) do add_penalty(v,a) end
427 local function handle_penalty_always(pre, post, g)
428 local a = (pre or 0) + (post or 0)
430 if not (g and g.id==id_glue) or Nq.lend~=0 then
431 local p = node_new(id_penalty)
432 if a<-10000 then a = -10000 elseif a>10000 then a = 10000 end
434 head = node_insert_before(head, Np.first, p)
435 Bp[1] = p; Bp[0] = 1; set_attr(p, attr_icflag, KINSOKU)
438 for i, v in ipairs(Bp) do add_penalty(v,a) end
442 local function handle_penalty_suppress(pre, post, g)
443 local a = (pre or 0) + (post or 0)
445 if g and g.id==id_glue then
446 local p = node_new(id_penalty)
447 p.penalty = 10000; head = node_insert_before(head, Np.first, p)
448 Bp[1] = p; Bp[0] = 1; set_attr(p, attr_icflag, KINSOKU)
451 for i, v in ipairs(Bp) do add_penalty(v,a) end
455 -- 和文文字間の JFM glue を node 化
456 local function new_jfm_glue(Nn, bc, ac)
457 -- bc, ac: char classes
459 local z = Nn.met.char_type[bc]
460 if z.glue and z.glue[ac] then
461 local h = node_new(id_glue_spec)
462 h.width = round(Nn.size*z.glue[ac][1])
463 h.stretch = round(Nn.size*z.glue[ac][2])
464 h.shrink = round(Nn.size*z.glue[ac][3])
465 h.stretch_order=0; h.shrink_order=0
466 g = node_new(id_glue)
467 g.subtype = 0; g.spec = h
468 elseif z.kern and z.kern[ac] then
469 g = node_new(id_kern)
470 g.subtype = 1; g.kern = round(Nn.size*z.kern[ac])
472 if g then set_attr(g, attr_icflag, FROM_JFM) end
476 -- Nq.last (kern w) .... (glue/kern g) Np.first
477 local function real_insert(w, g)
479 local h = node_new(id_kern)
480 set_attr(h, attr_icflag, LINE_END)
481 h.kern = Nq.lend; h.subtype = 1
482 head = node_insert_after(head, Nq.last, h)
485 head = node_insert_before(head, Np.first, g)
490 -------------------- 和文文字間空白量の決定
493 local function get_kanji_skip_from_jfm(Nn)
494 local i = Nn.met.kanjiskip
496 return { round(i[1]*Nn.size), round(i[2]*Nn.size), round(i[3]*Nn.size) }
500 local function get_kanjiskip()
501 local g = node_new(id_glue)
502 if Np.auto_kspc or Nq.auto_kspc then
503 if kanji_skip.width == max_dimen then
504 local gx = node_new(id_glue_spec);
505 gx.stretch_order = 0; gx.shrink_order = 0
506 local bk = get_kanji_skip_from_jfm(Nq)
508 if (Np.met==Nq.met) and (Nq.size==Np.size) and (Nq.var==Np.var) then
511 ak = get_kanji_skip_from_jfm(Np)
515 gx.width = round(diffmet_rule(bk[1], ak[1]))
516 gx.stretch = round(diffmet_rule(bk[2], ak[2]))
517 gx.shrink = -round(diffmet_rule(-bk[3], -ak[3]))
519 gx.width = bk[1]; gx.stretch = bk[2]; gx.shrink = bk[3]
522 gx.width = ak[1]; gx.stretch = ak[2]; gx.shrink = ak[3]
523 else gx = get_zero_glue() -- fallback
526 else g.spec=node_copy(kanji_skip) end
528 local gx = get_zero_glue()
531 set_attr(g, attr_icflag, KANJI_SKIP)
535 local function calc_ja_ja_aux(gb,ga)
539 if not ga then return gb end
540 local k = node.type(gb.id) .. node.type(ga.id)
541 if k == 'glueglue' then
543 gb.spec.width = round(diffmet_rule(gb.spec.width, ga.spec.width))
544 gb.spec.stretch = round(diffmet_rule(gb.spec.stretch,ga.spec.shrink))
545 gb.spec.shrink = -round(diffmet_rule(-gb.spec.shrink, -ga.spec.shrink))
548 elseif k == 'kernkern' then
550 gb.kern = round(diffmet_rule(gb.kern, ga.kern))
553 elseif k == 'kernglue' then
554 -- gb: kern, ga: glue
555 ga.spec.width = round(diffmet_rule(gb.kern,ga.spec.width))
556 ga.spec.stretch = round(diffmet_rule(ga.spec.stretch, 0))
557 ga.spec.shrink = -round(diffmet_rule(-ga.spec.shrink, 0))
561 -- gb: glue, ga: kern
562 gb.spec.width = round(diffmet_rule(ga.kern, gb.spec.width))
563 gb.spec.stretch = round(diffmet_rule(gb.spec.stretch, 0))
564 gb.spec.shrink = -round(diffmet_rule(-gb.spec.shrink, 0))
571 local function calc_ja_ja_glue()
572 if ihb_flag then return nil
573 elseif (Nq.size==Np.size) and (Nq.met==Np.met) and (Nq.var==Np.var) then
574 return new_jfm_glue(Nq, Nq.class, Np.class)
576 local g = new_jfm_glue(Nq, Nq.class,
577 find_char_class('diffmet',Nq.met))
578 local h = new_jfm_glue(Np, find_char_class('diffmet',Np.met),
580 return calc_ja_ja_aux(g,h)
584 -------------------- 和欧文間空白量の決定
587 local function get_xkanji_skip_from_jfm(Nn)
588 local i = Nn.met.xkanjiskip
590 return { round(i[1]*Nn.size), round(i[2]*Nn.size), round(i[3]*Nn.size) }
594 local function get_xkanjiskip(Nn)
595 local g = node_new(id_glue)
596 if Nq.xspc_after and Np.xspc_before and (Nq.auto_xspc or Np.auto_xspc) then
597 if xkanji_skip.width == max_dimen then
598 local gx = node_new(id_glue_spec);
599 gx.stretch_order = 0; gx.shrink_order = 0
600 local bk = get_xkanji_skip_from_jfm(Nn)
602 gx.width = bk[1]; gx.stretch = bk[2]; gx.shrink = bk[3]
603 else gx = get_zero_glue() -- fallback
606 else g.spec=node_copy(xkanji_skip) end
608 local gx = get_zero_glue()
611 set_attr(g, attr_icflag, XKANJI_SKIP)
616 -------------------- 隣接した「塊」間の処理
618 local function get_OA_skip()
620 return new_jfm_glue(Np, find_char_class('jcharbdd',Np.met), Np.class)
624 local function get_OB_skip()
626 return new_jfm_glue(Nq, Nq.class, find_char_class('jcharbdd',Nq.met))
631 -- (anything) .. jachar
632 local function handle_np_jachar()
634 if Nq.id==id_jglyph or (Nq.id==id_pbox and Nq.met) then
635 g = calc_ja_ja_glue() or get_kanjiskip() -- M->K
637 handle_penalty_normal(Nq.post, Np.pre, g); real_insert(Nq.lend, g)
638 elseif Nq.met then -- Nq.id==id_hlist
639 g = get_OA_skip() or get_kanjiskip() -- O_A->K
640 handle_penalty_normal(0, Np.pre, g); real_insert(0, g)
642 g = get_OA_skip() or get_xkanjiskip(Np) -- O_A->X
643 handle_penalty_normal(Nq.post, Np.pre, g); real_insert(0, g)
645 g = get_OA_skip() -- O_A
646 if Nq.id==id_glue then handle_penalty_normal(0, Np.pre, g)
647 elseif Nq.id==id_kern then handle_penalty_suppress(0, Np.pre, g)
648 else handle_penalty_always(0, Np.pre, g)
652 -- \jcharwidowpenalty 挿入予定箇所更新
653 if ltjs.get_penalty_table('kcat', Np.char, 0, ltj.box_stack_level)%2~=1 then
654 widow_Np = Np; widow_Bp = Bp
658 -- jachar .. (anything)
659 local function handle_nq_jachar()
662 g = get_OB_skip() or get_xkanjiskip(Nq) -- O_B->X
664 handle_penalty_normal(Nq.post, Np.pre, g); real_insert(Nq.lend, g)
666 g = get_OB_skip(); g = lineend_fix(g) -- O_B
667 if Np.id==id_glue then handle_penalty_normal(Nq.post, 0, g)
668 elseif Np.id==id_kern then handle_penalty_suppress(Nq.post, 0, g)
669 else handle_penalty_always(Nq.post, 0, g)
671 real_insert(Nq.lend, g)
675 -- (anything) .. (和文文字で終わる hlist)
676 local function handle_nq_ja_hlist()
678 if Nq.id==id_jglyph or (Nq.id==id_pbox and Nq.pre) then
679 g = get_OB_skip() or get_kanjiskip() -- O_B->K
681 handle_penalty_normal(Nq.post, Np.pre, g); real_insert(Nq.lend, g)
682 elseif Nq.met then -- Nq.id==id_hlist
683 g = get_kanjiskip() -- K
684 handle_penalty_suppress(0, 0, g); real_insert(0, g)
686 g = get_xkanjiskip(Np) -- X
687 handle_penalty_suppress(0, 0, g); real_insert(0, g)
691 -- (和文文字で終わる hlist) .. (anything)
692 local function handle_np_ja_hlist()
695 g = get_xkanjiskip(Nq) -- X
696 handle_penalty_suppress(0, 0, g); real_insert(0, g)
700 -------------------- 開始・終了時の処理
703 local function handle_list_tail()
706 -- the current list is to be line-breaked:
707 if Np.id == id_jglyph or (Np.id==id_pbox and Np.met) then
709 g = node_new(id_kern); g.subtype = 0; g.kern = Np.lend
710 set_attr(g, attr_icflag, LINE_END)
711 node_insert_after(head, Np.last, g)
714 -- Insert \jcharwidowpenalty
715 Bp = widow_Bp; Np = widow_Np
717 handle_penalty_normal(0,
718 ltjs.get_penalty_table('jwp', 0, 0, ltj.box_stack_level))
721 -- the current list is the contents of a hbox
722 if Np.id == id_jglyph or (Np.id==id_pbox and Np.met) then
723 local g = new_jfm_glue(Np, Np.class, find_char_class('boxbdd',Np.met))
725 set_attr(g, attr_icflag, FROM_JFM)
726 head = node_insert_after(head, Np.last, g)
729 head = node_remove(head, last) -- remove the sentinel
734 local function handle_list_head()
735 if Np.id == id_jglyph or (Np.id==id_pbox and Np.met) then
736 local g = new_jfm_glue(Np, find_char_class('boxbdd',Np.met), Np.class)
738 set_attr(g, attr_icflag, FROM_JFM)
739 if g.id==id_glue then
740 handle_penalty_suppress(0, 0)
742 head = node_insert_before(head, Np.first, g)
748 local function init_var()
749 lp = head; widow_Bp = nil; widow_Np = nil
750 kanji_skip=skip_table_to_spec('kanjiskip')
751 xkanji_skip=skip_table_to_spec('xkanjiskip')
753 -- the current list is to be line-breaked:
754 -- hbox from \parindent is skipped.
755 while lp and (lp.id==id_whatsit or ((lp.id==id_hlist) and (lp.subtype==3))) do
759 -- the current list is the contents of a hbox:
761 last=node.tail(head); local g = node_new(id_kern)
762 node_insert_after(head, last, g); last = g
766 -------------------- 外部から呼ばれる関数
769 function main(ahead, amode)
770 if not ahead then return ahead end
771 head = ahead; mode = amode; init_var(); calc_np()
773 extract_np(); handle_list_head()
776 head = node_remove(head, last) -- remove the sentinel
784 if Np.id == id_jglyph then
787 if Np.id==id_hlist then handle_np_ja_hlist()
788 else handle_np_jachar() end
790 if Nq.id==id_hlist then handle_nq_ja_hlist()
791 else handle_nq_jachar() end
794 if Np.id==id_glyph then after_alchar()
795 elseif Np.id==id_hlist or Np.id==id_pbox or Np.id==id_disc then after_hlist()
799 handle_list_tail(); return head
803 function create_inhibitglue_node()
804 local g=node_new(id_whatsit, sid_user)
805 g.user_id=30111; g.type=100; g.value=1; node.write(g)