2 -- luatexja/ltj-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 luatexja.load_module('stack'); local ltjs = luatexja.stack
14 luatexja.load_module('jfont'); local ltjf = luatexja.jfont
15 luatexja.load_module('pretreat'); local ltjp = luatexja.pretreat
17 local has_attr = node.has_attribute
18 local set_attr = node.set_attribute
19 local insert_before = node.insert_before
20 local node_next = node.next
21 local round = tex.round
22 local ltjf_font_metric_table = ltjf.font_metric_table
23 local ltjf_find_char_class = ltjf.find_char_class
24 local node_new = node.new
25 local node_copy = node.copy
27 local ligature_head = 1
28 local ligature_tail = 2
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 = 512 -- Japanese character
46 local id_box_like = 256 -- vbox, shifted hbox
47 local id_pbox = 257 -- already processed nodes (by \unhbox)
48 local id_pbox_w = 258 -- cluster which consists of a whatsit
49 local sid_user = node.subtype('user_defined')
51 local sid_start_link = node.subtype('pdf_start_link')
52 local sid_start_thread = node.subtype('pdf_start_thread')
53 local sid_end_link = node.subtype('pdf_end_link')
54 local sid_end_thread = node.subtype('pdf_end_thread')
56 local ITALIC = luatexja.icflag_table.ITALIC
57 local PACKED = luatexja.icflag_table.PACKED
58 local KINSOKU = luatexja.icflag_table.KINSOKU
59 local FROM_JFM = luatexja.icflag_table.FROM_JFM
60 local KANJI_SKIP = luatexja.icflag_table.KANJI_SKIP
61 local XKANJI_SKIP = luatexja.icflag_table.XKANJI_SKIP
62 local PROCESSED = luatexja.icflag_table.PROCESSED
63 local IC_PROCESSED = luatexja.icflag_table.IC_PROCESSED
64 local BOXBDD = luatexja.icflag_table.BOXBDD
65 local PROCESSED_BEGIN_FLAG = luatexja.icflag_table.PROCESSED_BEGIN_FLAG
68 local table_current_stack
70 local attr_curjfnt = luatexbase.attributes['ltj@curjfnt']
71 local attr_icflag = luatexbase.attributes['ltj@icflag']
73 local function get_attr_icflag(p)
74 return (has_attr(p, attr_icflag) or 0)%PROCESSED_BEGIN_FLAG
77 -------------------- Helper functions
79 local function copy_attr(new, old)
83 -- This function is called only for acquiring `special' characters.
84 local function fast_find_char_class(c,m)
85 return m.chars[c] or 0
89 local function slow_find_char_class(c, m, oc)
91 local cls = ltjf_find_char_class(oc, m)
92 if xc ~= oc and cls==0 then cls = ltjf_find_char_class(-xc, m) end
96 local zero_glue = node_new(id_glue)
97 spec_zero_glue = node_new(id_glue_spec) -- must be public, since mentioned from other sources
98 local spec_zero_glue = spec_zero_glue
99 spec_zero_glue.width = 0; spec_zero_glue.stretch_order = 0; spec_zero_glue.stretch = 0
100 spec_zero_glue.shrink_order = 0; spec_zero_glue.shrink = 0
101 zero_glue.spec = spec_zero_glue
103 local function skip_table_to_spec(n)
104 local g, st = node_new(id_glue_spec), ltjs.fast_get_skip_table(n)
105 g.width = st.width; g.stretch = st.stretch; g.shrink = st.shrink
106 g.stretch_order = st.stretch_order; g.shrink_order = st.shrink_order
112 local function add_penalty(p,e)
115 if e<=-10000 then pp = 0 end
116 elseif pp<=-10000 then
117 if e>=10000 then pp = 0 end
120 if pp>=10000 then p.penalty = 10000
121 elseif pp<=-10000 then p.penalty = -10000
122 else p.penalty = pp end
128 diffmet_rule = math.two_paverage
129 function math.two_add(a,b) return a+b end
130 function math.two_average(a,b) return (a+b)*0.5 end
131 function math.two_paverage(a,b) return (a+b)*0.5 end
132 function math.two_pleft(a,b) return a end
133 function math.two_pright(a,b) return b end
135 local head -- the head of current list
138 local widow_Bp, widow_Np -- \jcharwidowpenalty 挿入位置管理用
140 local ihb_flag -- JFM グルー挿入抑止用 flag
141 -- on: \inhibitglue 指定時,hlist の周囲
143 -------------------- hlist 内の文字の検索
145 local first_char, last_char, find_first_char
147 local function check_box(box_ptr, box_end)
148 local p = box_ptr; local found_visible_node = false
150 find_first_char = false; last_char = nil
153 while p and p~=box_end do
155 if pid==id_kern and p.subtype==2 then
156 p = node_next(node_next(node_next(p))); pid = p.id -- p must be glyph_node
158 if pid==id_glyph then
160 if find_first_char then
161 first_char = p; find_first_char = false
163 last_char = p; found_visible_node = true; p=node_next(p)
164 if (not p) or p==box_end then
165 return found_visible_node
168 pid = p.id -- p must be non-nil
171 if get_attr_icflag(p)==IC_PROCESSED then
173 elseif p.subtype==2 then
174 p = node_next(node_next(p));
175 -- Note that another node_next will be executed outside this if-statement.
177 found_visible_node = true
178 if find_first_char then
179 find_first_char = false
184 elseif pid==id_hlist then
185 if PACKED == get_attr_icflag(p) then
186 if find_first_char then
187 first_char = p.head; find_first_char = false
189 last_char = p.head; found_visible_node = true
192 if check_box(p.head, nil) then found_visible_node = true end
193 else if find_first_char then
194 find_first_char = false
200 elseif pid==id_math then
201 if find_first_char then
202 first_char = p; find_first_char = false
204 last_char = p; found_visible_node = true
205 elseif not (pid==id_ins or pid==id_mark
206 or pid==id_adjust or pid==id_whatsit
207 or pid==id_penalty) then
208 found_visible_node = true
209 if find_first_char then
210 find_first_char = false
217 return found_visible_node
220 function check_box_high(Nx, box_ptr, box_end)
221 first_char = nil; last_char = nil; find_first_char = true
222 if check_box(box_ptr, box_end) then
223 local first_char = first_char
225 if first_char.id==id_glyph then
226 if first_char.font == (has_attr(first_char, attr_curjfnt) or -1) then
227 set_np_xspc_jachar(Nx, first_char)
229 set_np_xspc_alchar(Nx, first_char.char,first_char, ligature_head)
232 set_np_xspc_alchar(Nx, -1,first_char)
239 -------------------- Np の計算と情報取得
241 luatexbase.create_callback("luatexja.jfmglue.whatsit_getinfo", "data",
242 function (Np, lp, Nq)
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) return false end)
254 local function set_attr_icflag_processed(p)
255 if get_attr_icflag(p)<= ITALIC then
256 set_attr(p, attr_icflag, PROCESSED)
260 local function check_next_ickern(lp)
261 if lp.id == id_kern and ITALIC == get_attr_icflag(lp) then
262 set_attr(lp, attr_icflag, IC_PROCESSED)
263 Np.last = lp; return node_next(lp)
265 Np.last = Np.nuc; return lp
269 local function calc_np_pbox(lp, last)
270 Np.first = Np.first or lp; Np.id = id_pbox
271 local lpa = KINSOKU -- dummy=
272 set_attr(lp, attr_icflag, get_attr_icflag(lp));
273 while lp~=last and lpa>=PACKED and lpa<BOXBDD do
275 lp = node_next(lp); lpa = has_attr(lp, attr_icflag) or 0
276 -- get_attr_icflag() ではいけない!
278 return check_next_ickern(lp)
282 local calc_np_auxtable = {
283 [id_glyph] = function (lp)
284 Np.first, Np.nuc = (Np.first or lp), lp;
285 Np.id = (lp.font == (has_attr(lp, attr_curjfnt) or -1)) and id_jglyph or id_glyph
286 --set_attr_icflag_processed(lp) treated in ltj-setwidth.lua
287 return true, check_next_ickern(node_next(lp));
289 [id_hlist] = function(lp)
290 Np.first = Np.first or lp; Np.last = lp; Np.nuc = lp;
291 set_attr(lp, attr_icflag, PROCESSED)
292 --set_attr_icflag_processed(lp)
293 Np.id = (lp.shift~=0) and id_box_like or id_hlist
294 return true, node_next(lp)
296 box_like = function(lp)
297 Np.first = Np.first or lp; Np.nuc = lp; Np.last = lp;
298 Np.id = id_box_like; set_attr(lp, attr_icflag, PROCESSED)
299 -- set_attr_icflag_processed(lp);
300 return true, node_next(lp);
303 set_attr(lp, attr_icflag, PROCESSED)
304 -- set_attr_icflag_processed(lp);
305 return false, node_next(lp)
307 [id_whatsit] = function(lp)
308 if lp.subtype==sid_user then
309 if lp.user_id==luatexja.userid_table.IHB then
310 local lq = node_next(lp);
311 head = node.remove(head, lp); node.free(lp); ihb_flag = true
314 set_attr(lp, attr_icflag, PROCESSED)
315 -- set_attr_icflag_processed(lp)
316 luatexbase.call_callback("luatexja.jfmglue.whatsit_getinfo",
319 Np.id = id_pbox_w; Np.first = Np.nuc; Np.last = Np.nuc;
320 return true, node_next(lp)
322 return false, node_next(lp)
326 -- we do special treatment for these whatsit nodes.
327 if lp.subtype == sid_start_link or lp.subtype == sid_start_thread then
329 elseif lp.subtype == sid_end_link or lp.subtype == sid_end_thread then
330 Np.first, Nq.last = nil, lp;
332 set_attr(lp, attr_icflag, PROCESSED)
333 -- set_attr_icflag_processed(lp);
334 return false, node_next(lp)
337 [id_math] = function(lp)
338 Np.first, Np.nuc = (Np.first or lp), lp;
339 set_attr(lp, attr_icflag, PROCESSED) -- set_attr_icflag_processed(lp);
341 while lp.id~=id_math do
342 set_attr(lp, attr_icflag, PROCESSED) -- set_attr_icflag_processed(lp);
345 set_attr(lp, attr_icflag, PROCESSED) -- set_attr_icflag_processed(lp);
346 Np.last, Np.id = lp, id_math;
347 return true, node_next(lp);
349 discglue = function(lp)
350 Np.first, Np.nuc, Np.last = (Np.first or lp), lp, lp;
351 Np.id = lp.id; set_attr(lp, attr_icflag, PROCESSED) -- set_attr_icflag_processed(lp);
352 return true, node_next(lp)
354 [id_kern] = function(lp)
355 Np.first = Np.first or lp
356 if lp.subtype==2 then
357 set_attr(lp, attr_icflag, PROCESSED); lp = node_next(lp)
358 set_attr(lp, attr_icflag, PROCESSED); lp = node_next(lp)
359 set_attr(lp, attr_icflag, PROCESSED); lp = node_next(lp)
360 set_attr(lp, attr_icflag, PROCESSED); Np.nuc = lp
361 Np.id = (lp.font == (has_attr(lp, attr_curjfnt) or -1)) and id_jglyph or id_glyph
362 return true, check_next_ickern(node_next(lp));
364 Np.id = id_kern; set_attr(lp, attr_icflag, PROCESSED); -- set_attr_icflag_processed(lp);
365 Np.last = lp; return true, node_next(lp)
368 [id_penalty] = function(lp)
369 Bp[#Bp+1] = lp; set_attr(lp, attr_icflag, PROCESSED); -- set_attr_icflag_processed(lp);
370 return false, node_next(lp)
373 calc_np_auxtable[id_vlist] = calc_np_auxtable.box_like
374 calc_np_auxtable[id_rule] = calc_np_auxtable.box_like
375 calc_np_auxtable[13] = calc_np_auxtable.box_like
376 calc_np_auxtable[id_ins] = calc_np_auxtable.skip
377 calc_np_auxtable[id_mark] = calc_np_auxtable.skip
378 calc_np_auxtable[id_adjust] = calc_np_auxtable.skip
379 calc_np_auxtable[id_disc] = calc_np_auxtable.discglue
380 calc_np_auxtable[id_glue] = calc_np_auxtable.discglue
383 function calc_np(lp, last)
385 -- We assume lp = node_next(Np.last)
386 Np, Nq, ihb_flag = Nq, Np, nil
387 -- We clear `predefined' entries of Np before pairs() loop,
388 -- because using only pairs() loop is slower.
389 Np.post, Np.pre, Np.xspc = nil, nil, nil
390 Np.first, Np.id, Np.last, Np.met = nil, nil, nil
391 Np.auto_kspc, Np.auto_xspc, Np.char, Np.class, Np.nuc = nil, nil, nil, nil, nil
392 for k in pairs(Np) do Np[k] = nil end
394 for k = 1,#Bp do Bp[k] = nil end
396 local lpa = has_attr(lp, attr_icflag) or 0
399 if lpa%PROCESSED_BEGIN_FLAG == BOXBDD then
400 local lq = node_next(lp)
401 head = node.remove(head, lp); node.free(lp); lp = lq
402 else return calc_np_pbox(lp, last)
405 k, lp = calc_np_auxtable[lp.id](lp)
406 if k then return lp end
413 local calc_np = calc_np
415 -- extract informations from Np
416 -- We think that "Np is a Japanese character" if Np.met~=nil,
417 -- "Np is an alphabetic character" if Np.pre~=nil,
418 -- "Np is not a character" otherwise.
420 local PRE = luatexja.stack_table_index.PRE
421 local POST = luatexja.stack_table_index.POST
422 local KCAT = luatexja.stack_table_index.KCAT
423 local XSP = luatexja.stack_table_index.XSP
426 local attr_jchar_class = luatexbase.attributes['ltj@charclass']
427 local attr_orig_char = luatexbase.attributes['ltj@origchar']
428 local attr_autospc = luatexbase.attributes['ltj@autospc']
429 local attr_autoxspc = luatexbase.attributes['ltj@autoxspc']
430 function set_np_xspc_jachar(Nx, x)
431 local m = ltjf_font_metric_table[x.font]
432 local cls, c = slow_find_char_class(has_attr(x, attr_orig_char), m, x.char)
433 Nx.class = cls; set_attr(x, attr_jchar_class, cls)
434 Nx.met, Nx.char = m, c
435 Nx.pre = table_current_stack[PRE + c] or 0
436 Nx.post = table_current_stack[POST + c] or 0
437 Nx.xspc = table_current_stack[XSP + c] or 3
438 Nx.kcat = table_current_stack[KCAT + c] or 0
439 Nx.auto_kspc, Nx.auto_xspc = (has_attr(x, attr_autospc)==1), (has_attr(x, attr_autoxspc)==1)
441 local set_np_xspc_jachar = set_np_xspc_jachar
444 local floor = math.floor
445 function set_np_xspc_alchar(Nx, c,x, lig)
447 local xc, xs = x.components, x.subtype
449 while xc and xs and xs%4>=2 do
450 x = xc; xc, xs = x.components, x.subtype
454 while xc and xs and xs%4>=2 do
455 x = node.tail(xc); xc, xs = x.components, x.subtype
459 Nx.pre = table_current_stack[PRE + c] or 0
460 Nx.post = table_current_stack[POST + c] or 0
461 Nx.xspc = table_current_stack[XSP + c] or 3
464 Nx.pre, Nx.post, Nx.char = 0, 0, -1
465 Nx.xspc = table_current_stack[XSP - 1] or 3
468 Nx.auto_xspc = (has_attr(x, attr_autoxspc)==1)
470 local set_np_xspc_alchar = set_np_xspc_alchar
473 function extract_np()
474 local x, i = Np.nuc, Np.id;
475 if i == id_jglyph then return set_np_xspc_jachar(Np, x)
476 elseif i == id_glyph then return set_np_xspc_alchar(Np, x.char, x, ligature_head)
477 elseif i == id_hlist then Np.last_char = check_box_high(Np, x.head, nil)
478 elseif i == id_pbox then Np.last_char = check_box_high(Np, Np.first, node_next(Np.last))
479 elseif i == id_disc then Np.last_char = check_box_high(Np, x.replace, nil)
480 elseif i == id_math then return set_np_xspc_alchar(Np, -1, x)
484 -- change the information for the next loop
485 -- (will be done if Nx is an alphabetic character or a hlist)
486 function after_hlist(Nx)
487 local s = Nx.last_char
489 if s.id==id_glyph then
490 if s.font == (has_attr(s, attr_curjfnt) or -1) then
491 set_np_xspc_jachar(Nx, s)
493 set_np_xspc_alchar(Nx, s.char, s, ligature_tail)
496 set_np_xspc_alchar(Nx, -1, s)
499 Nx.pre, Nx.met = nil, nil
503 function after_alchar(Nx)
505 return set_np_xspc_alchar(Nx, x.char,x, ligature_tail)
509 local after_hlist, after_alchar, extract_np = after_hlist, after_alchar, extract_np
511 -------------------- 最下層の処理
513 -- change penalties (or create a new penalty, if needed)
514 local function handle_penalty_normal(post, pre, g)
515 local a = (pre or 0) + (post or 0)
517 if (a~=0 and not(g and g.id==id_kern)) then
518 local p = node_new(id_penalty); --copy_attr(p, Nq.nuc)
519 if a<-10000 then a = -10000 elseif a>10000 then a = 10000 end
521 head = insert_before(head, Np.first, p)
523 set_attr(p, attr_icflag, KINSOKU)
525 else for _, v in pairs(Bp) do add_penalty(v,a) end
529 local function handle_penalty_always(post, pre, g)
530 local a = (pre or 0) + (post or 0)
532 if not (g and g.id==id_glue) then
533 local p = node_new(id_penalty); --copy_attr(p, Nq.nuc)
534 if a<-10000 then a = -10000 elseif a>10000 then a = 10000 end
536 head = insert_before(head, Np.first, p)
538 set_attr(p, attr_icflag, KINSOKU)
540 else for _, v in pairs(Bp) do add_penalty(v,a) end
544 local function handle_penalty_suppress(post, pre, g)
545 local a = (pre or 0) + (post or 0)
547 if g and g.id==id_glue then
548 local p = node_new(id_penalty); --copy_attr(p, Nq.nuc)
549 p.penalty = 10000; head = insert_before(head, Np.first, p)
551 set_attr(p, attr_icflag, KINSOKU)
553 else for _, v in pairs(Bp) do add_penalty(v,a) end
557 -- 和文文字間の JFM glue を node 化
558 local function new_jfm_glue(m, bc, ac)
559 -- bc, ac: char classes
560 local z = m.char_type[bc]
561 local g, d = z.glue[ac], 0
563 g,d = node_copy(g[1]), g[2];
564 g.spec = node_copy(g.spec); -- node_copy は spec をコピーする
568 g = node_new(id_kern); --copy_attr(g, Nn.nuc)
569 g.subtype = 1; g.kern, d = k[1], k[2]
570 set_attr(g, attr_icflag, FROM_JFM);
576 -- Nq.last (kern w) .... (glue/kern g) Np.first
577 local function real_insert(g)
579 head = insert_before(head, Np.first, g)
585 -------------------- 和文文字間空白量の決定
590 local function get_kanjiskip_normal()
591 if Np.auto_kspc or Nq.auto_kspc then
592 return node_copy(kanji_skip)
594 local g = node_copy(zero_glue)
595 set_attr(g, attr_icflag, KANJI_SKIP)
599 local function get_kanjiskip_jfm()
601 if Np.auto_kspc or Nq.auto_kspc then
602 g = node_new(id_glue); --copy_attr(g, Nq.nuc)
603 local gx = node_new(id_glue_spec);
604 gx.stretch_order, gx.shrink_order = 0, 0
605 local pm, qm = Np.met, Nq.met
606 local bk = qm.kanjiskip or {0, 0, 0}
607 if (pm.char_type==qm.char_type) and (qm.var==pm.var) then
608 gx.width = bk[1]; gx.stretch = bk[2]; gx.shrink = bk[3]
610 local ak = pm.kanjiskip or {0, 0, 0}
611 gx.width = round(diffmet_rule(bk[1], ak[1]))
612 gx.stretch = round(diffmet_rule(bk[2], ak[2]))
613 gx.shrink = -round(diffmet_rule(-bk[3], -ak[3]))
617 g = node_copy(zero_glue)
619 set_attr(g, attr_icflag, KANJI_SKIP)
623 local function calc_ja_ja_aux(gb,ga, db, da)
624 local rbb, rab = (1-db)/2, (1-da)/2 -- 「前の文字」由来のグルーの割合
625 local rba, raa = (1+db)/2, (1+da)/2 -- 「前の文字」由来のグルーの割合
626 if diffmet_rule ~= math.two_pleft and diffmet_rule ~= math.two_pright
627 and diffmet_rule ~= math.two_paverage then
628 rbb, rab, rba, raa = 1,0,0,1
631 if ga then gb = node_new(id_kern); gb.kern = 0 else return nil end
633 ga = node_new(id_kern); ga.kern = 0
636 local k = node.type(gb.id) .. node.type(ga.id)
637 if k == 'glueglue' then
639 gb.spec.width = round(diffmet_rule(
640 rbb*gb.spec.width + rba*ga.spec.width,
641 rab*gb.spec.width + raa*ga.spec.width ))
642 gb.spec.stretch = round(diffmet_rule(
643 rbb*gb.spec.stretch + rba*ga.spec.stretch,
644 rab*gb.spec.stretch + raa*ga.spec.stretch ))
645 gb.spec.shrink = -round(diffmet_rule(
646 -rbb*gb.spec.shrink - rba*ga.spec.shrink,
647 -rab*gb.spec.shrink - raa*ga.spec.shrink ))
650 elseif k == 'kernkern' then
652 gb.kern = round(diffmet_rule(
653 rbb*gb.kern + rba*ga.kern,
654 rab*gb.kern + raa*ga.kern ))
657 elseif k == 'kernglue' then
658 -- gb: kern, ga: glue
659 ga.spec.width = round(diffmet_rule(
660 rbb*gb.kern + rba*ga.spec.width,
661 rab*gb.kern + raa*ga.spec.width ))
662 ga.spec.stretch = round(diffmet_rule(
663 rba*ga.spec.stretch, raa*ga.spec.stretch ))
664 ga.spec.shrink = -round(diffmet_rule(
665 -rba*ga.spec.shrink,-raa*ga.spec.shrink ))
669 -- gb: glue, ga: kern
670 gb.spec.width = round(diffmet_rule(
671 rba*ga.kern + rbb*gb.spec.width,
672 raa*ga.kern + rab*gb.spec.width ))
673 gb.spec.stretch = round(diffmet_rule(
674 rbb*gb.spec.stretch, rab*gb.spec.stretch ))
675 gb.spec.shrink = -round(diffmet_rule(
676 -rbb*gb.spec.shrink,-rab*gb.spec.shrink ))
682 local function calc_ja_ja_glue()
683 if ihb_flag then return nil
685 local qm, pm = Nq.met, Np.met
686 if (qm.char_type==pm.char_type) and (qm.var==pm.var) then
687 return new_jfm_glue(qm, Nq.class, Np.class)
689 local npn, nqn = Np.nuc, Nq.nuc
690 local gb, db = new_jfm_glue(qm, Nq.class,
691 slow_find_char_class(has_attr(npn, attr_orig_char), qm, npn.char))
692 local ga, da = new_jfm_glue(pm,
693 slow_find_char_class(has_attr(nqn, attr_orig_char), pm, nqn.char),
695 return calc_ja_ja_aux(gb, ga, db, da);
700 -------------------- 和欧文間空白量の決定
704 local function get_xkanjiskip_normal(Nn)
705 if (Nq.xspc>=2) and (Np.xspc%2==1) and (Nq.auto_xspc or Np.auto_xspc) then
706 return node_copy(xkanji_skip)
708 local g = node_copy(zero_glue)
709 set_attr(g, attr_icflag, XKANJI_SKIP)
713 local function get_xkanjiskip_jfm(Nn)
715 if (Nq.xspc>=2) and (Np.xspc%2==1) and (Nq.auto_xspc or Np.auto_xspc) then
716 g = node_new(id_glue); --copy_attr(g, Nn.nuc)
717 local gx = node_new(id_glue_spec);
718 gx.stretch_order, gx.shrink_order = 0, 0
719 local bk = Nn.met.xkanjiskip or {0, 0, 0}
720 gx.width = bk[1]; gx.stretch = bk[2]; gx.shrink = bk[3]
723 g = node_copy(zero_glue)
725 set_attr(g, attr_icflag, XKANJI_SKIP)
731 -------------------- 隣接した「塊」間の処理
733 local function get_OA_skip()
736 return new_jfm_glue(pm,
737 fast_find_char_class(((Nq.id == id_math and -1) or (type(Nq.char)=='string' and Nq.char or 'jcharbdd')), pm), Np.class)
741 local function get_OB_skip()
744 return new_jfm_glue(qm, Nq.class,
745 fast_find_char_class(((Np.id == id_math and -1) or'jcharbdd'), qm))
750 -- (anything) .. jachar
751 local function handle_np_jachar(mode)
753 if qid==id_jglyph or ((qid==id_pbox or qid==id_pbox_w) and Nq.met) then
754 local g = calc_ja_ja_glue() or get_kanjiskip() -- M->K
755 handle_penalty_normal(Nq.post, Np.pre, g); real_insert(g)
756 elseif Nq.met then -- qid==id_hlist
757 local g = get_OA_skip() or get_kanjiskip() -- O_A->K
758 handle_penalty_normal(0, Np.pre, g); real_insert(g)
760 local g = get_OA_skip() or get_xkanjiskip(Np) -- O_A->X
761 handle_penalty_normal((qid==id_hlist and 0 or Nq.post), Np.pre, g); real_insert(g)
763 local g = get_OA_skip() -- O_A
764 if qid==id_glue then handle_penalty_normal(0, Np.pre, g)
765 elseif qid==id_kern then handle_penalty_suppress(0, Np.pre, g)
766 else handle_penalty_always(0, Np.pre, g)
770 if mode and Np.kcat%2~=1 then
771 widow_Np.first, widow_Bp, Bp = Np.first, Bp, widow_Bp
776 -- jachar .. (anything)
777 local function handle_nq_jachar()
779 local g = get_OB_skip() or get_xkanjiskip(Nq) -- O_B->X
780 handle_penalty_normal(Nq.post, (Np.id==id_hlist and 0 or Np.pre), g); real_insert(g)
782 local g = get_OB_skip() -- O_B
783 if Np.id==id_glue then handle_penalty_normal(Nq.post, 0, g)
784 elseif Np.id==id_kern then handle_penalty_suppress(Nq.post, 0, g)
785 else handle_penalty_always(Nq.post, 0, g)
791 -- (anything) .. (和文文字で始まる hlist)
792 local function handle_np_ja_hlist()
794 if qid==id_jglyph or ((qid==id_pbox or Nq.id == id_pbox_w) and Nq.met) then
795 local g = get_OB_skip() or get_kanjiskip() -- O_B->K
796 handle_penalty_normal(Nq.post, 0, g); real_insert(g)
797 elseif Nq.met then -- Nq.id==id_hlist
798 local g = get_kanjiskip() -- K
799 handle_penalty_suppress(0, 0, g); real_insert(g)
801 local g = get_xkanjiskip(Np) -- X
802 handle_penalty_suppress(0, 0, g); real_insert(g)
806 -- (和文文字で終わる hlist) .. (anything)
807 local function handle_nq_ja_hlist()
809 local g = get_xkanjiskip(Nq) -- X
810 handle_penalty_suppress(0, 0, g); real_insert(g)
815 -- Nq が前側のクラスタとなることによる修正
817 local adjust_nq_aux = {
818 [id_glyph] = function()
820 return set_np_xspc_alchar(Nq, x.char,x, 2)
821 end, -- after_alchar(Nq)
822 [id_hlist] = function() after_hlist(Nq) end,
823 [id_pbox] = function() after_hlist(Nq) end,
824 [id_disc] = function() after_hlist(Nq) end,
825 [id_pbox_w] = function()
826 luatexbase.call_callback("luatexja.jfmglue.whatsit_after",
832 local x = adjust_nq_aux[Nq.id]
838 -------------------- 開始・終了時の処理
841 local JWP = luatexja.stack_table_index.JWP
842 local function handle_list_tail(mode)
845 -- the current list is to be line-breaked.
846 -- Insert \jcharwidowpenalty
847 Bp = widow_Bp; Np = widow_Np
849 handle_penalty_normal(0,
850 table_current_stack[JWP] or 0)
853 -- the current list is the contents of a hbox
854 local npi, pm = Np.id, Np.met
855 if npi == id_jglyph or (npi==id_pbox and pm) then
856 local g = new_jfm_glue(pm, Np.class, fast_find_char_class('boxbdd', pm))
858 set_attr(g, attr_icflag, BOXBDD)
859 head = node.insert_after(head, Np.last, g)
866 local function handle_list_head(par_indented)
867 local npi, pm = Np.id, Np.met
868 if npi == id_jglyph or (npi==id_pbox and pm) then
870 local g = new_jfm_glue(pm, fast_find_char_class(par_indented, pm), Np.class)
872 set_attr(g, attr_icflag, BOXBDD)
873 if g.id==id_glue and #Bp==0 then
874 local h = node_new(id_penalty); --copy_attr(h, Np.nuc)
875 h.penalty = 10000; set_attr(h, attr_icflag, BOXBDD)
877 head = insert_before(head, Np.first, g)
884 -- return value: (the initial cursor lp), (last node)
885 local function init_var(mode)
886 -- 1073741823: max_dimen
887 Bp, widow_Bp, widow_Np = {}, {}, {first = nil}
888 table_current_stack = ltjs.table_current_stack
890 kanji_skip = node_new(id_glue)
891 kanji_skip.spec = skip_table_to_spec('kanjiskip')
892 set_attr(kanji_skip, attr_icflag, KANJI_SKIP)
893 get_kanjiskip = (kanji_skip.spec.width == 1073741823)
894 and get_kanjiskip_jfm or get_kanjiskip_normal
896 xkanji_skip = node_new(id_glue)
897 xkanji_skip.spec = skip_table_to_spec('xkanjiskip')
898 set_attr(xkanji_skip, attr_icflag, XKANJI_SKIP)
899 get_xkanjiskip = (xkanji_skip.spec.width == 1073741823)
900 and get_xkanjiskip_jfm or get_xkanjiskip_normal
903 auto_kspc=nil, auto_xspc=nil, char=nil, class=nil,
904 first=nil, id=nil, last=nil, met=nil, nuc=nil,
905 post=nil, pre=nil, xspc=nil,
908 auto_kspc=nil, auto_xspc=nil, char=nil, class=nil,
909 first=nil, id=nil, last=nil, met=nil, nuc=nil,
910 post=nil, pre=nil, xspc=nil,
913 -- the current list is to be line-breaked:
914 -- hbox from \parindent is skipped.
915 local lp, par_indented, lpi, lps = head, 'boxbdd', head.id, head.subtype
916 while lp and ((lpi==id_whatsit and lps~=sid_user)
917 or ((lpi==id_hlist) and (lps==3))) do
918 if (lpi==id_hlist) and (lps==3) then par_indented = 'parbdd' end
919 lp=node_next(lp); lpi, lps = lp.id, lp.subtype end
920 return lp, node.tail(head), par_indented
922 -- the current list is the contents of a hbox:
924 local g = node_new(id_kern)
925 node.insert_after(head, node.tail(head), g); last = g
926 return head, g, 'boxbdd'
930 local function cleanup(mode, last)
931 -- adjust attr_icflag for avoiding error
932 tex.setattribute('global', attr_icflag, 0)
933 node.free(kanji_skip); node.free(xkanji_skip)
935 local h = node_next(head)
936 if h.id == id_penalty and h.penalty == 10000 then
938 if h.id == id_glue and h.subtype == 15 and not h.next then
944 head = node.remove(head, last); node.free(last);-- remove the sentinel
945 set_attr(head, attr_icflag,
946 get_attr_icflag(head) + PROCESSED_BEGIN_FLAG);
950 -------------------- 外部から呼ばれる関数
953 function main(ahead, mode)
954 if not ahead then return ahead end
956 local lp, last, par_indented = init_var(mode);
957 lp = calc_np(lp, last)
959 extract_np(); handle_list_head(par_indented)
961 return cleanup(mode, last)
963 lp = calc_np(lp, last)
967 local pid, pm = Np.id, Np.met
969 if pid == id_jglyph then
970 handle_np_jachar(mode)
972 if pid==id_hlist then handle_np_ja_hlist()
973 else handle_np_jachar() end
975 if Nq.id==id_hlist then handle_nq_ja_hlist()
976 else handle_nq_jachar() end
978 lp = calc_np(lp, last)
980 handle_list_tail(mode)
981 return cleanup(mode, last)
985 local IHB = luatexja.userid_table.IHB
986 local BPAR = luatexja.userid_table.BPAR
989 function create_inhibitglue_node()
990 local tn = node_new(id_whatsit, sid_user)
991 tn.user_id=IHB; tn.type=100; tn.value=1
995 -- Node for indicating beginning of a paragraph
997 function create_beginpar_node()
998 local tn = node_new(id_whatsit, sid_user)
999 tn.user_id=BPAR; tn.type=100; tn.value=1
1003 local function whatsit_callback(Np, lp, Nq)
1004 if Np and Np.nuc then return Np
1005 elseif Np and lp.user_id == BPAR then
1006 Np.first = lp; Np.nuc = lp; Np.last = lp
1009 Np.pre = 0; Np.post = 0
1011 Np.auto_xspc = false
1016 local function whatsit_after_callback(s, Nq, Np)
1017 if not s and Nq.nuc.user_id == BPAR then
1018 local x, y = node.prev(Nq.nuc), Nq.nuc
1019 Nq.first, Nq.nuc, Nq.last = x, x, x
1020 head = node.remove(head, y)
1025 luatexbase.add_to_callback("luatexja.jfmglue.whatsit_getinfo", whatsit_callback,
1026 "luatexja.beginpar.np_info", 1)
1027 luatexbase.add_to_callback("luatexja.jfmglue.whatsit_after", whatsit_after_callback,
1028 "luatexja.beginpar.np_info_after", 1)