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)
14 luatexja.load_module('stack'); local ltjs = luatexja.stack
15 luatexja.load_module('jfont'); local ltjf = luatexja.jfont
16 luatexja.load_module('pretreat'); local ltjp = luatexja.pretreat
18 local has_attr = node.has_attribute
19 local set_attr = node.set_attribute
20 local insert_before = node.insert_before
21 local node_next = node.next
22 local round = tex.round
23 local uniq_id = 0 -- unique id
24 local ltjs_get_penalty_table = ltjs.get_penalty_table
25 local ltjf_font_metric_table = ltjf.font_metric_table
26 local ltjf_find_char_class = ltjf.find_char_class
28 local ligature_head = 1
29 local ligature_tail = 2
31 local id_glyph = node.id('glyph')
32 local id_hlist = node.id('hlist')
33 local id_vlist = node.id('vlist')
34 local id_rule = node.id('rule')
35 local id_ins = node.id('ins')
36 local id_mark = node.id('mark')
37 local id_adjust = node.id('adjust')
38 local id_disc = node.id('disc')
39 local id_whatsit = node.id('whatsit')
40 local id_math = node.id('math')
41 local id_glue = node.id('glue')
42 local id_kern = node.id('kern')
43 local id_penalty = node.id('penalty')
45 local id_glue_spec = node.id('glue_spec')
46 local id_jglyph = node.id('glyph') + 256 -- Japanese character
47 local id_box_like = node.id('hlist') + 256 -- vbox, shifted hbox
48 local id_pbox = node.id('hlist') + 512 -- already processed nodes (by \unhbox)
49 local id_pbox_w = node.id('hlist') + 513 -- cluster which consists of a whatsit
50 local sid_user = node.subtype('user_defined')
52 local sid_start_link = node.subtype('pdf_start_link')
53 local sid_start_thread = node.subtype('pdf_start_thread')
54 local sid_end_link = node.subtype('pdf_end_link')
55 local sid_end_thread = node.subtype('pdf_end_thread')
65 local IC_PROCESSED = 9
71 local attr_orig_char = luatexbase.attributes['ltj@origchar']
72 local attr_curjfnt = luatexbase.attributes['ltj@curjfnt']
73 local attr_icflag = luatexbase.attributes['ltj@icflag']
74 local attr_autospc = luatexbase.attributes['ltj@autospc']
75 local attr_autoxspc = luatexbase.attributes['ltj@autoxspc']
76 local attr_uniqid = luatexbase.attributes['ltj@uniqid']
77 local max_dimen = 1073741823
81 -------------------- Helper functions
83 local function copy_attr(new, old)
87 -- This function is called only for acquiring `special' characters.
88 local function fast_find_char_class(c,m)
89 return m.size_cache.chars[c] or 0
92 local spec_zero_glue = node.new(id_glue_spec)
93 spec_zero_glue.width = 0; spec_zero_glue.stretch_order = 0; spec_zero_glue.stretch = 0
94 spec_zero_glue.shrink_order = 0; spec_zero_glue.shrink = 0
96 local function get_zero_spec()
97 return node.copy(spec_zero_glue)
100 local function skip_table_to_spec(n)
101 local g = node.new(id_glue_spec)
102 local st = ltjs.get_skip_table(n, box_stack_level)
103 g.width = st.width; g.stretch = st.stretch; g.shrink = st.shrink
104 g.stretch_order = st.stretch_order; g.shrink_order = st.shrink_order
109 local function add_penalty(p,e)
110 if p.penalty>=10000 then
111 if e<=-10000 then p.penalty = 0 end
112 elseif p.penalty<=-10000 then
113 if e>=10000 then p.penalty = 0 end
115 p.penalty = p.penalty + e
116 if p.penalty>=10000 then p.penalty = 10000
117 elseif p.penalty<=-10000 then p.penalty = -10000 end
123 diffmet_rule = math.two_average
124 function math.two_add(a,b) return a+b end
125 function math.two_average(a,b) return (a+b)*0.5 end
127 -------------------- idea
128 -- 2 node の間に glue/kern/penalty を挿入する.
129 -- 基本方針: char node q と char node p の間
132 -- first: 最初の node,nuc: p,last: 最後の node
135 -- 実際の glue は Np.last, Nq.first の間に挿入される
136 -- Bp: Np.last, Nq.first の間の penalty node 達の配列
138 -- Np, Nq, Bp, widow_Bp について
140 -- 1回のループごとに Nq = Np, Np = (new table) となるのは効率が悪いので,
141 -- Np <-> Nq 入れ替え,その後 Np をクリアすることでテーブルを再利用.
142 -- 同様の関係は Bp, widow_Bp にも.
146 -- node x が non-char node のときは,x のみ
147 -- x が char_node のときは,
148 -- - x が \accent の第二引数だったとき
149 -- [kern2 kern y kern2] x の 3 node が核に加わる
150 -- - x の直後に \/ 由来 kern があったとき
151 -- その \/ 由来の kern が核に加わる
153 -- ins, mark, adjust, whatsit, penalty
155 -- Nq.last .. + .. Bp.first .... Bp[last] .... * .. Np.first
156 -- +: kern from LINEEND はここに入る
157 -- *: jfm glue はここに入る
159 local head -- the head of current list
162 local widow_Bp, widow_Np -- \jcharwidowpenalty 挿入位置管理用
164 local ihb_flag -- JFM グルー挿入抑止用 flag
165 -- on: \inhibitglue 指定時,hlist の周囲
167 -------------------- hlist 内の文字の検索
169 local first_char, last_char, find_first_char
171 local function check_box(box_ptr, box_end)
172 local p = box_ptr; local found_visible_node = false
174 find_first_char = false; first_char = nil; last_char = nil
177 while p and p~=box_end do
179 if pid==id_kern and p.subtype==2 then
180 p = node_next(node_next(node_next(p))); pid = p.id -- p must be glyph_node
182 if pid==id_glyph then
184 if find_first_char then
185 first_char = p; find_first_char = false
187 last_char = p; found_visible_node = true; p=node_next(p)
188 if (not p) or p==box_end then return found_visible_node end
190 pid = p.id -- p must be non-nil
192 if pid==id_kern and has_attr(p, attr_icflag)==IC_PROCESSED then
194 elseif pid==id_hlist then
195 if PACKED == has_attr(p, attr_icflag) then
196 if find_first_char then
197 first_char = p.head; find_first_char = false
199 last_char = p.head; found_visible_node = true
202 if check_box(p.head, nil) then found_visible_node = true end
203 else if find_first_char then
204 find_first_char = false
210 elseif not (pid==id_ins or pid==id_mark
211 or pid==id_adjust or pid==id_whatsit
212 or pid==id_penalty) then
213 found_visible_node = true
214 if find_first_char then
215 find_first_char = false
222 return found_visible_node
225 function check_box_high(Nx, box_ptr, box_end)
226 first_char = nil; last_char = nil; find_first_char = true
227 if check_box(box_ptr, box_end) then
229 if first_char.font == has_attr(first_char, attr_curjfnt) then
230 set_np_xspc_jachar(Nx, first_char)
232 set_np_xspc_alchar(Nx, first_char.char,first_char, ligature_head)
239 -------------------- Np の計算と情報取得
241 luatexbase.create_callback("luatexja.jfmglue.whatsit_getinfo", "data",
242 function (Np, lp, Nq, box_stack_level)
243 if Np.nuc then return Np
245 return Np -- your code
248 luatexbase.create_callback("luatexja.jfmglue.whatsit_after", "data",
249 function (stat, Nq, Np, box_stack_level) return false end)
254 local function set_attr_icflag_processed(p)
255 if (has_attr(p, attr_icflag) or 0)<= ITALIC then
256 set_attr(p, attr_uniqid, uniq_id)
257 set_attr(p, attr_icflag, PROCESSED)
261 local function check_next_ickern(lp)
262 if lp.id == id_kern and ITALIC == has_attr(lp, attr_icflag) then
263 set_attr(lp, attr_icflag, IC_PROCESSED)
264 set_attr(lp, attr_uniqid, uniq_id)
265 Np.last = lp; return node_next(lp)
267 Np.last = Np.nuc; return lp
271 local function calc_np_pbox(lp, last)
272 local uid = has_attr(lp, attr_uniqid)
273 Np.first = Np.first or lp; Np.id = id_pbox
274 local lpa = KINSOKU -- dummy=
275 while lp~=last and lpa>=PACKED and lpa~=BOXBDD
276 and uid == has_attr(lp, attr_uniqid) do
277 Np.nuc = lp; set_attr(lp, attr_uniqid, uniq_id)
278 lp = node_next(lp); lpa = has_attr(lp, attr_icflag) or 0
280 return check_next_ickern(lp)
284 local calc_np_auxtable = {
285 [id_glyph] = function(lp)
286 Np.first, Np.nuc = (Np.first or lp), lp;
287 Np.id = (lp.font == has_attr(lp, attr_curjfnt)) and id_jglyph or id_glyph
288 set_attr(lp, attr_uniqid, uniq_id)
289 --set_attr_icflag_processed(lp) treated in ltj-setwidth.lua
290 return true, check_next_ickern(node_next(lp));
292 [id_hlist] = function(lp)
293 Np.first = Np.first or lp; Np.last = lp; Np.nuc = lp;
294 set_attr_icflag_processed(lp)
295 Np.id = (lp.shift~=0) and id_box_like or id_hlist
296 return true, node_next(lp)
298 box_like = function(lp)
299 Np.first = Np.first or lp; Np.nuc = lp; Np.last = lp;
300 Np.id = id_box_like; set_attr_icflag_processed(lp);
301 return true, node_next(lp);
304 set_attr_icflag_processed(lp); return false, node_next(lp)
306 [id_whatsit] = function(lp)
307 if lp.subtype==sid_user then
308 if lp.user_id==30111 then
309 local lq = node_next(lp);
310 head = node.remove(head, lp); node.free(lp); ihb_flag = true
313 set_attr_icflag_processed(lp)
314 luatexbase.call_callback("luatexja.jfmglue.whatsit_getinfo",
315 Np, lp, Nq, box_stack_level)
317 Np.id = id_pbox_w; Np.first = Np.nuc; Np.last = Np.nuc;
318 return true, node_next(lp)
320 return false, node_next(lp)
324 -- we do special treatment for these whatsit nodes.
325 if lp.subtype == sid_start_link or lp.subtype == sid_start_thread then
327 elseif lp.subtype == sid_end_link or lp.subtype == sid_end_thread then
328 Np.first, Nq.last = nil, lp;
330 set_attr_icflag_processed(lp); return false, node_next(lp)
333 [id_math] = function(lp)
334 Np.first, Np.nuc = (Np.first or lp), lp;
335 set_attr_icflag_processed(lp); lp = node_next(lp)
336 while lp.id~=id_math do
337 set_attr_icflag_processed(lp); lp = node_next(lp)
339 set_attr_icflag_processed(lp);
340 Np.last, Np.id = lp, id_math;
341 return true, node_next(lp);
343 discglue = function(lp)
344 Np.first, Np.nuc, Np.last = (Np.first or lp), lp, lp;
345 Np.id = lp.id; set_attr_icflag_processed(lp); return true, node_next(lp)
347 [id_kern] = function(lp)
348 Np.first = Np.first or lp
349 if lp.subtype==2 then
350 set_attr_icflag_processed(lp); lp = node_next(lp)
351 set_attr_icflag_processed(lp); lp = node_next(lp)
352 set_attr_icflag_processed(lp); lp = node_next(lp)
353 set_attr_icflag_processed(lp); Np.nuc = lp
354 Np.id = (lp.font == has_attr(lp, attr_curjfnt)) and id_jglyph or id_glyph
355 return true, check_next_ickern(node_next(lp));
357 Np.id = id_kern; set_attr_icflag_processed(lp);
358 Np.last = lp; return true, node_next(lp)
361 [id_penalty] = function(lp)
362 Bp[#Bp+1] = lp; set_attr_icflag_processed(lp);
363 return false, node_next(lp)
366 calc_np_auxtable[id_vlist] = calc_np_auxtable.box_like
367 calc_np_auxtable[id_rule] = calc_np_auxtable.box_like
368 calc_np_auxtable[13] = calc_np_auxtable.box_like
369 calc_np_auxtable[id_ins] = calc_np_auxtable.skip
370 calc_np_auxtable[id_mark] = calc_np_auxtable.skip
371 calc_np_auxtable[id_adjust] = calc_np_auxtable.skip
372 calc_np_auxtable[id_disc] = calc_np_auxtable.discglue
373 calc_np_auxtable[id_glue] = calc_np_auxtable.discglue
375 function calc_np(lp, last)
377 -- We assume lp = node_next(Np.last)
378 Np, Nq, ihb_flag = Nq, Np, false
379 for k in pairs(Np) do Np[k] = nil end
380 for k = 1,#Bp do Bp[k] = nil end
382 local lpa = has_attr(lp, attr_icflag) or 0
384 if lpa == BOXBDD then
385 local lq = node_next(lp)
386 head = node.remove(head, lp); node.free(lp); lp = lq
387 else return calc_np_pbox(lp, last)
390 k, lp = calc_np_auxtable[lp.id](lp)
391 if k then return lp end
399 local calc_np = calc_np
401 -- extract informations from Np
402 -- We think that "Np is a Japanese character" if Np.met~=nil,
403 -- "Np is an alphabetic character" if Np.pre~=nil,
404 -- "Np is not a character" otherwise.
408 local attr_jchar_class = luatexbase.attributes['ltj@charclass']
409 function set_np_xspc_jachar(Nx, x)
410 local m = ltjf_font_metric_table[x.font]
411 local c = has_attr(x, attr_orig_char) or 0
412 local cls = ltjf_find_char_class(x.char, m)
413 if cls==0 and c ~= x.char then cls = ltjf_find_char_class(-c, m) end
414 Nx.class = cls; set_attr(x, attr_jchar_class, cls)
415 Nx.lend = m.size_cache.char_type[cls].kern[fast_find_char_class('lineend', m)] or 0
416 Nx.met, Nx.var, Nx.char = m, m.var, c
417 Nx.pre = ltjs_get_penalty_table('pre', c, 0, box_stack_level)
418 Nx.post = ltjs_get_penalty_table('post', c, 0, box_stack_level)
419 local y = ltjs_get_penalty_table('xsp', c, 3, box_stack_level)
420 Nx.xspc_before, Nx.xspc_after = (y%2==1), (y>=2)
421 Nx.auto_kspc, Nx.auto_xspc = (has_attr(x, attr_autospc)==1), (has_attr(x, attr_autoxspc)==1)
423 local set_np_xspc_jachar = set_np_xspc_jachar
426 local floor = math.floor
427 function set_np_xspc_alchar(Nx, c,x, lig)
429 if lig == ligature_head then
430 while x.components and x.subtype and math.floor(x.subtype*0.5)%2==1 do
431 x = x.components; c = x.char
434 while x.components and x.subtype and math.floor(x.subtype*0.5)%2==1 do
435 x = node.tail(x.components); c = x.char
438 Nx.pre = ltjs_get_penalty_table('pre', c, 0, box_stack_level)
439 Nx.post = ltjs_get_penalty_table('post', c, 0, box_stack_level)
442 Nx.pre, Nx.post, Nx.char = 0, 0, -1
445 local y = ltjs_get_penalty_table('xsp', c, 3, box_stack_level)
446 Nx.xspc_before, Nx.xspc_after = (y%2==1), (y>=2)
447 Nx.auto_xspc = (has_attr(x, attr_autoxspc)==1)
449 local set_np_xspc_alchar = set_np_xspc_alchar
452 function extract_np()
453 local x, i = Np.nuc, Np.id;
454 if i == id_jglyph then return set_np_xspc_jachar(Np, x)
455 elseif i == id_glyph then return set_np_xspc_alchar(Np, x.char, x, ligature_head)
456 elseif i == id_hlist then Np.last_char = check_box_high(Np, x.head, nil)
457 elseif i == id_pbox then Np.last_char = check_box_high(Np, Np.first, node_next(Np.last))
458 elseif i == id_disc then Np.last_char = check_box_high(Np, x.replace, nil)
459 elseif i == id_math then return set_np_xspc_alchar(Np, -1, x)
463 -- change the information for the next loop
464 -- (will be done if Nx is an alphabetic character or a hlist)
465 function after_hlist(Nx)
466 local s = Nx.last_char
468 if s.font == has_attr(s, attr_curjfnt) then
469 set_np_xspc_jachar(Nx, s)
471 set_np_xspc_alchar(Nx, s.char, s, ligature_tail)
474 Nx.pre, Nx.met = nil, nil
478 function after_alchar(Nx)
480 return set_np_xspc_alchar(Nx, x.char,x, ligature_tail)
484 local after_hlist, after_alchar, extract_np = after_hlist, after_alchar, extract_np
486 -------------------- 最下層の処理
488 local function lineend_fix(g)
489 if g and g.id==id_kern then
491 elseif Nq.lend~=0 then
493 g = node.new(id_kern); copy_attr(g, Nq.nuc);
494 g.subtype = 1; g.kern = -Nq.lend;
495 set_attr(g, attr_icflag, LINEEND)
496 set_attr(g, attr_uniqid, uniq_id)
497 elseif g.id==id_kern then
498 g.kern = g.kern - Nq.lend
500 g.spec.width = g.spec.width - Nq.lend
506 -- change penalties (or create a new penalty, if needed)
507 local function handle_penalty_normal(post, pre, g)
508 local a = (pre or 0) + (post or 0)
510 if (a~=0 and not(g and g.id==id_kern)) or Nq.lend~=0 then
511 local p = node.new(id_penalty); copy_attr(p, Nq.nuc)
512 if a<-10000 then a = -10000 elseif a>10000 then a = 10000 end
514 head = insert_before(head, Np.first, p)
516 set_attr(p, attr_icflag, KINSOKU)
517 set_attr(p, attr_uniqid, uniq_id)
519 --else for _, v in pairs(Bp) do v.penalty = v.penalty + a end
520 else for _, v in pairs(Bp) do add_penalty(v,a) end
524 local function handle_penalty_always(post, pre, g)
525 local a = (pre or 0) + (post or 0)
527 if not (g and g.id==id_glue) or Nq.lend~=0 then
528 local p = node.new(id_penalty); copy_attr(p, Nq.nuc)
529 if a<-10000 then a = -10000 elseif a>10000 then a = 10000 end
531 head = insert_before(head, Np.first, p)
533 set_attr(p, attr_icflag, KINSOKU)
534 set_attr(p, attr_uniqid, uniq_id)
536 else for _, v in pairs(Bp) do add_penalty(v,a) end
540 local function handle_penalty_suppress(post, pre, g)
541 local a = (pre or 0) + (post or 0)
543 if g and g.id==id_glue then
544 local p = node.new(id_penalty); copy_attr(p, Nq.nuc)
545 p.penalty = 10000; head = insert_before(head, Np.first, p)
547 set_attr(p, attr_icflag, KINSOKU)
548 set_attr(p, attr_uniqid, uniq_id)
550 else for _, v in pairs(Bp) do add_penalty(v,a) end
554 -- 和文文字間の JFM glue を node 化
555 local function new_jfm_glue(Nn, bc, ac)
556 -- bc, ac: char classes
558 local z = Nn.met.size_cache.char_type[bc]
559 local gt = z.glue[ac]
561 local h = node.new(id_glue_spec)
562 h.width, h.stretch, h.shrink = gt[1], gt[2], gt[3]
563 h.stretch_order, h.shrink_order = 0, 0
564 g = node.new(id_glue); copy_attr(g, Nn.nuc)
565 g.subtype = 0; g.spec = h
566 set_attr(g, attr_icflag, FROM_JFM); set_attr(g, attr_uniqid, uniq_id)
567 elseif z.kern[ac] then
568 g = node.new(id_kern); copy_attr(g, Nn.nuc)
569 g.subtype = 1; g.kern = z.kern[ac]
570 set_attr(g, attr_icflag, FROM_JFM); set_attr(g, attr_uniqid, uniq_id)
575 -- Nq.last (kern w) .... (glue/kern g) Np.first
576 local function real_insert(w, g)
578 local h = node.new(id_kern); copy_attr(h, Nq.nuc)
579 set_attr(h, attr_icflag, LINE_END)
580 set_attr(h, attr_uniqid, uniq_id)
581 h.kern = w; h.subtype = 1
582 head = node.insert_after(head, Nq.last, h)
585 head = insert_before(head, Np.first, g)
590 -------------------- 和文文字間空白量の決定
593 local function get_kanji_skip_from_jfm(Nn)
594 local i = Nn.met.size_cache.kanjiskip
596 return { i[1], i[2], i[3] }
600 local function get_kanjiskip()
601 local g = node.new(id_glue); copy_attr(g, Nq.nuc)
602 if Np.auto_kspc or Nq.auto_kspc then
603 if kanji_skip.width == max_dimen then
604 local gx = node.new(id_glue_spec);
605 gx.stretch_order = 0; gx.shrink_order = 0
606 local bk = get_kanji_skip_from_jfm(Nq)
608 if (Np.met.size_cache==Nq.met.size_cache) and (Nq.var==Np.var) then
611 ak = get_kanji_skip_from_jfm(Np)
615 gx.width = round(diffmet_rule(bk[1], ak[1]))
616 gx.stretch = round(diffmet_rule(bk[2], ak[2]))
617 gx.shrink = -round(diffmet_rule(-bk[3], -ak[3]))
619 gx.width = bk[1]; gx.stretch = bk[2]; gx.shrink = bk[3]
622 gx.width = ak[1]; gx.stretch = ak[2]; gx.shrink = ak[3]
623 else node.free(gx); gx = get_zero_spec() -- fallback
626 else g.spec=node.copy(kanji_skip) end
628 g.spec = get_zero_spec()
630 set_attr(g, attr_icflag, KANJI_SKIP)
631 set_attr(g, attr_uniqid, uniq_id)
635 local function calc_ja_ja_aux(gb,ga)
639 if not ga then return gb end
640 local k = node.type(gb.id) .. node.type(ga.id)
641 if k == 'glueglue' then
643 gb.spec.width = round(diffmet_rule(gb.spec.width, ga.spec.width))
644 gb.spec.stretch = round(diffmet_rule(gb.spec.stretch,ga.spec.shrink))
645 gb.spec.shrink = -round(diffmet_rule(-gb.spec.shrink, -ga.spec.shrink))
648 elseif k == 'kernkern' then
650 gb.kern = round(diffmet_rule(gb.kern, ga.kern))
653 elseif k == 'kernglue' then
654 -- gb: kern, ga: glue
655 ga.spec.width = round(diffmet_rule(gb.kern,ga.spec.width))
656 ga.spec.stretch = round(diffmet_rule(ga.spec.stretch, 0))
657 ga.spec.shrink = -round(diffmet_rule(-ga.spec.shrink, 0))
661 -- gb: glue, ga: kern
662 gb.spec.width = round(diffmet_rule(ga.kern, gb.spec.width))
663 gb.spec.stretch = round(diffmet_rule(gb.spec.stretch, 0))
664 gb.spec.shrink = -round(diffmet_rule(-gb.spec.shrink, 0))
671 local function calc_ja_ja_glue()
672 if ihb_flag then return nil
673 elseif (Nq.met.size_cache==Np.met.size_cache) and (Nq.var==Np.var) then
674 return new_jfm_glue(Nq, Nq.class, Np.class)
676 return calc_ja_ja_aux(new_jfm_glue(Nq, Nq.class, fast_find_char_class('diffmet',Nq.met)),
677 new_jfm_glue(Np, fast_find_char_class('diffmet',Np.met), Np.class))
681 -------------------- 和欧文間空白量の決定
684 local function get_xkanji_skip_from_jfm(Nn)
685 local i = Nn.met.size_cache.xkanjiskip
687 return i and { i[1], i[2], i[3] }
691 local function get_xkanjiskip(Nn)
692 local g = node.new(id_glue); copy_attr(g, Nn.nuc)
693 if Nq.xspc_after and Np.xspc_before and (Nq.auto_xspc or Np.auto_xspc) then
694 if xkanji_skip.width == max_dimen then
695 local gx = node.new(id_glue_spec);
696 gx.stretch_order = 0; gx.shrink_order = 0
697 local bk = get_xkanji_skip_from_jfm(Nn)
699 gx.width = bk[1]; gx.stretch = bk[2]; gx.shrink = bk[3]
700 else node.free(gx); gx = get_zero_spec() -- fallback
703 else g.spec=node.copy(xkanji_skip) end
705 g.spec = get_zero_spec()
707 set_attr(g, attr_icflag, XKANJI_SKIP)
708 set_attr(g, attr_uniqid, uniq_id)
713 -------------------- 隣接した「塊」間の処理
715 local function get_OA_skip()
717 return new_jfm_glue(Np, fast_find_char_class(((Nq.id == id_math and -1) or 'jcharbdd'), Np.met), Np.class)
721 local function get_OB_skip()
723 return new_jfm_glue(Nq, Nq.class, fast_find_char_class(((Np.id == id_math and -1) or'jcharbdd'), Nq.met))
728 -- (anything) .. jachar
729 local function handle_np_jachar(mode)
730 if Nq.id==id_jglyph or ((Nq.id==id_pbox or Nq.id==id_pbox_w) and Nq.met) then
731 local g = lineend_fix(calc_ja_ja_glue() or get_kanjiskip()) -- M->K
732 handle_penalty_normal(Nq.post, Np.pre, g); real_insert(Nq.lend, g)
733 elseif Nq.met then -- Nq.id==id_hlist
734 local g = get_OA_skip() or get_kanjiskip() -- O_A->K
735 handle_penalty_normal(0, Np.pre, g); real_insert(0, g)
737 local g = get_OA_skip() or get_xkanjiskip(Np) -- O_A->X
738 if Nq.id==id_hlist then Nq.post = 0 end
739 handle_penalty_normal(Nq.post, Np.pre, g); real_insert(0, g)
741 local g = get_OA_skip() -- O_A
742 if Nq.id==id_glue then handle_penalty_normal(0, Np.pre, g)
743 elseif Nq.id==id_kern then handle_penalty_suppress(0, Np.pre, g)
744 else handle_penalty_always(0, Np.pre, g)
748 -- \jcharwidowpenalty 挿入予定箇所更新
749 if mode and ltjs_get_penalty_table('kcat', Np.char, 0, box_stack_level)%2~=1 then
750 widow_Np.first, widow_Bp, Bp = Np.first, Bp, widow_Bp
754 -- jachar .. (anything)
755 local function handle_nq_jachar()
757 if Np.id==id_hlist then Np.pre = 0 end
758 local g = lineend_fix(get_OB_skip() or get_xkanjiskip(Nq)) -- O_B->X
759 handle_penalty_normal(Nq.post, Np.pre, g); real_insert(Nq.lend, g)
761 local g = lineend_fix(get_OB_skip()) -- O_B
762 if Np.id==id_glue then handle_penalty_normal(Nq.post, 0, g)
763 elseif Np.id==id_kern then handle_penalty_suppress(Nq.post, 0, g)
764 else handle_penalty_always(Nq.post, 0, g)
766 real_insert(Nq.lend, g)
770 -- (anything) .. (和文文字で始まる hlist)
771 local function handle_np_ja_hlist()
772 if Nq.id==id_jglyph or ((Nq.id==id_pbox or Nq.id == id_pbox_w) and Nq.met) then
773 local g = lineend_fix(get_OB_skip() or get_kanjiskip()) -- O_B->K
774 handle_penalty_normal(Nq.post, 0, g); real_insert(Nq.lend, g)
775 elseif Nq.met then -- Nq.id==id_hlist
776 local g = get_kanjiskip() -- K
777 handle_penalty_suppress(0, 0, g); real_insert(0, g)
779 local g = get_xkanjiskip(Np) -- X
780 handle_penalty_suppress(0, 0, g); real_insert(0, g)
784 -- (和文文字で終わる hlist) .. (anything)
785 local function handle_nq_ja_hlist()
787 local g = get_xkanjiskip(Nq) -- X
788 handle_penalty_suppress(0, 0, g); real_insert(0, g)
792 -- Nq が前側のクラスタとなることによる修正
793 local function adjust_nq()
794 if Nq.id==id_glyph then after_alchar(Nq)
795 elseif Nq.id==id_hlist or Nq.id==id_pbox or Nq.id==id_disc then after_hlist(Nq)
796 elseif Nq.id == id_pbox_w then
797 luatexbase.call_callback("luatexja.jfmglue.whatsit_after",
798 false, Nq, Np, box_stack_level)
802 -------------------- 開始・終了時の処理
805 local function handle_list_tail(mode)
808 -- the current list is to be line-breaked:
809 if Np.id == id_jglyph or (Np.id==id_pbox and Np.met) then
811 g = node.new(id_kern); g.subtype = 0; g.kern = Np.lend
812 copy_attr(g, Np.nuc); set_attr(g, attr_icflag, BOXBDD)
813 node.insert_after(head, Np.last, g)
816 -- Insert \jcharwidowpenalty
817 Bp = widow_Bp; Np = widow_Np; Nq.lend = 0
819 handle_penalty_normal(0,
820 ltjs_get_penalty_table('jwp', 0, 0, box_stack_level))
823 -- the current list is the contents of a hbox
824 if Np.id == id_jglyph or (Np.id==id_pbox and Np.met) then
825 local g = new_jfm_glue(Np, Np.class, fast_find_char_class('boxbdd',Np.met))
827 set_attr(g, attr_icflag, BOXBDD)
828 head = node.insert_after(head, Np.last, g)
835 local function handle_list_head(par_indented)
836 if Np.id == id_jglyph or (Np.id==id_pbox and Np.met) then
840 g = new_jfm_glue(Np, fast_find_char_class('parbdd',Np.met), Np.class)
842 g = new_jfm_glue(Np, fast_find_char_class('boxbdd',Np.met), Np.class)
845 set_attr(g, attr_icflag, BOXBDD)
846 if g.id==id_glue and #Bp==0 then
847 local h = node.new(id_penalty); copy_attr(h, Np.nuc)
848 h.penalty = 10000; set_attr(h, attr_icflag, BOXBDD)
850 head = insert_before(head, Np.first, g)
857 -- return value: (the initial cursor lp), (last node)
858 local function init_var(mode)
860 if uniq_id == 0x7FFFFFF then uniq_id = 0 end
861 Bp = {}; widow_Bp = {}; widow_Np = {first = nil}
862 box_stack_level = ltjp.box_stack_level
863 kanji_skip=skip_table_to_spec('kanjiskip')
864 xkanji_skip=skip_table_to_spec('xkanjiskip')
866 auto_kspc=nil, auto_xspc=nil, char=nil, class=nil,
867 first=nil, id=nil, last=nil, lend=0, met=nil, nuc=nil,
868 post=nil, pre=nil, xspc_after=nil, xspc_before=nil,
871 auto_kspc=nil, auto_xspc=nil, char=nil, class=nil,
872 first=nil, id=nil, last=nil, lend=0, met=nil, nuc=nil,
873 post=nil, pre=nil, xspc_after=nil, xspc_before=nil,
876 -- the current list is to be line-breaked:
877 -- hbox from \parindent is skipped.
878 local lp, par_indented = head, false
879 while lp and ((lp.id==id_whatsit and lp.subtype~=sid_user)
880 or ((lp.id==id_hlist) and (lp.subtype==3))) do
881 if (lp.id==id_hlist) and (lp.subtype==3) then par_indented = true end
883 return lp, node.tail(head), par_indented
885 -- the current list is the contents of a hbox:
886 -- insert a sentinelEG
887 local g = node.new(id_kern)
888 node.insert_after(head, node.tail(head), g); last = g
889 return head, g, false
893 local function cleanup(mode, last)
894 -- adjust attr_icflag for avoiding error
895 tex.setattribute('global', attr_icflag, 0)
896 node.free(kanji_skip); node.free(xkanji_skip)
898 local h = node_next(head)
899 if h.id == id_penalty and h.penalty == 10000 then
901 if h.id == id_glue and h.subtype == 15 and not h.next then
907 head = node.remove(head, last); node.free(last);-- remove the sentinel
911 -------------------- 外部から呼ばれる関数
914 function main(ahead, mode)
915 if not ahead then return ahead end
917 local lp, last, par_indented = init_var(mode);
918 lp = calc_np(lp, last)
920 extract_np(); handle_list_head(par_indented)
922 return cleanup(mode, last)
924 lp = calc_np(lp, last)
926 extract_np(); adjust_nq()
928 if Np.id == id_jglyph then
929 handle_np_jachar(mode)
931 if Np.id==id_hlist then handle_np_ja_hlist()
932 else handle_np_jachar() end
934 if Nq.id==id_hlist then handle_nq_ja_hlist()
935 else handle_nq_jachar() end
937 lp = calc_np(lp, last)
939 handle_list_tail(mode)
940 return cleanup(mode, last)
945 function create_inhibitglue_node()
946 local tn = node.new(id_whatsit, sid_user)
947 tn.user_id=30111; tn.type=100; tn.value=1
951 -- Node for indicating beginning of a paragraph
953 function create_beginpar_node()
954 local tn = node.new(id_whatsit, sid_user)
955 tn.user_id=30114; tn.type=100; tn.value=1
961 local function whatsit_callback(Np, lp, Nq, bsl)
962 if Np and Np.nuc then return Np
963 elseif Np and lp.user_id == 30114 then
964 Np.first = lp; Np.nuc = lp; Np.last = lp
967 Np.pre = 0; Np.post = 0
968 Np.xspc_before = false
969 Np.xspc_after = false
974 local function whatsit_after_callback(s, Nq, Np, bsl)
975 if not s and Nq.nuc.user_id == 30114 then
976 local x, y = node.prev(Nq.nuc), Nq.nuc
977 Nq.first, Nq.nuc, Nq.last = x, x, x
978 head = node.remove(head, y)
983 luatexbase.add_to_callback("luatexja.jfmglue.whatsit_getinfo", whatsit_callback,
984 "luatexja.beginpar.np_info", 1)
985 luatexbase.add_to_callback("luatexja.jfmglue.whatsit_after", whatsit_after_callback,
986 "luatexja.beginpar.np_info_after", 1)