2 -- luatexja/ltj-jfmglue.lua
4 luatexbase.provides_module({
5 name = 'luatexja.jfmglue',
7 description = 'Insertion process of JFM glues and kanjiskip',
9 module('luatexja.jfmglue', package.seeall)
10 local err, warn, info, log = luatexbase .errwarinf(_NAME)
12 luatexja.load_module('base'); local ltjb = luatexja.base
13 luatexja.load_module('stack'); local ltjs = luatexja.stack
14 luatexja.load_module('jfont'); local ltjf = luatexja.jfont
15 luatexja.load_module('direction'); local ltjd = luatexja.direction
16 luatexja.load_module('setwidth'); local ltjw = luatexja.setwidth
19 local Dnode = node.direct or node
21 local nullfunc = function(n) return n end
22 local to_node = (Dnode ~= node) and Dnode.tonode or nullfunc
23 local to_direct = (Dnode ~= node) and Dnode.todirect or nullfunc
25 local setfield = (Dnode ~= node) and Dnode.setfield or function(n, i, c) n[i] = c end
26 local getfield = (Dnode ~= node) and Dnode.getfield or function(n, i) return n[i] end
27 local getid = (Dnode ~= node) and Dnode.getid or function(n) return n.id end
28 local getfont = (Dnode ~= node) and Dnode.getfont or function(n) return n.font end
29 local getlist = (Dnode ~= node) and Dnode.getlist or function(n) return n.head end
30 local getchar = (Dnode ~= node) and Dnode.getchar or function(n) return n.char end
31 local getsubtype = (Dnode ~= node) and Dnode.getsubtype or function(n) return n.subtype end
33 local has_attr = Dnode.has_attribute
34 local set_attr = Dnode.set_attribute
35 local insert_before = Dnode.insert_before
36 local insert_after = Dnode.insert_after
37 local node_next = (Dnode ~= node) and Dnode.getnext or node.next
38 local round = tex.round
39 local ltjd_make_dir_whatsit = ltjd.make_dir_whatsit
40 local ltjf_font_metric_table = ltjf.font_metric_table
41 local ltjf_find_char_class = ltjf.find_char_class
42 local node_new = Dnode.new
43 local node_copy = Dnode.copy
44 local node_remove = Dnode.remove
45 local node_tail = Dnode.tail
46 local node_free = Dnode.free
47 local node_end_of_math = Dnode.end_of_math
49 local id_glyph = node.id('glyph')
50 local id_hlist = node.id('hlist')
51 local id_vlist = node.id('vlist')
52 local id_rule = node.id('rule')
53 local id_ins = node.id('ins')
54 local id_mark = node.id('mark')
55 local id_adjust = node.id('adjust')
56 local id_disc = node.id('disc')
57 local id_whatsit = node.id('whatsit')
58 local id_math = node.id('math')
59 local id_glue = node.id('glue')
60 local id_kern = node.id('kern')
61 local id_penalty = node.id('penalty')
63 local id_glue_spec = node.id('glue_spec')
64 local id_jglyph = 512 -- Japanese character
65 local id_box_like = 256 -- vbox, shifted hbox
66 local id_pbox = 257 -- already processed nodes (by \unhbox)
67 local id_pbox_w = 258 -- cluster which consists of a whatsit
68 local sid_user = node.subtype('user_defined')
69 local lang_ja = luatexja.lang_ja
71 local sid_start_link = node.subtype('pdf_start_link')
72 local sid_start_thread = node.subtype('pdf_start_thread')
73 local sid_end_link = node.subtype('pdf_end_link')
74 local sid_end_thread = node.subtype('pdf_end_thread')
76 local ITALIC = luatexja.icflag_table.ITALIC
77 local PACKED = luatexja.icflag_table.PACKED
78 local KINSOKU = luatexja.icflag_table.KINSOKU
79 local FROM_JFM = luatexja.icflag_table.FROM_JFM
80 local PROCESSED = luatexja.icflag_table.PROCESSED
81 local IC_PROCESSED = luatexja.icflag_table.IC_PROCESSED
82 local BOXBDD = luatexja.icflag_table.BOXBDD
83 local PROCESSED_BEGIN_FLAG = luatexja.icflag_table.PROCESSED_BEGIN_FLAG
85 local attr_icflag = luatexbase.attributes['ltj@icflag']
88 local table_current_stack
93 local set_np_xspc_jachar
94 local set_np_xspc_jachar_hbox
96 local ltjs_orig_char_table = ltjs.orig_char_table
98 local function get_attr_icflag(p)
99 return (has_attr(p, attr_icflag) or 0)%PROCESSED_BEGIN_FLAG
102 -------------------- Helper functions
104 -- This function is called only for acquiring `special' characters.
105 local function fast_find_char_class(c,m)
106 return m.chars[c] or 0
110 local slow_find_char_class
112 local start_time_measure = ltjb.start_time_measure
113 local stop_time_measure = ltjb.stop_time_measure
114 slow_find_char_class = function (c, m, oc)
115 local cls = ltjf_find_char_class(oc, m)
116 if oc~=c and c and cls==0 then
117 return ltjf_find_char_class(c, m)
124 local zero_glue = node_new(id_glue)
125 spec_zero_glue = to_node(node_new(id_glue_spec))
126 -- must be public, since mentioned from other sources
127 local spec_zero_glue = to_direct(spec_zero_glue)
128 setfield(spec_zero_glue, 'width', 0)
129 setfield(spec_zero_glue, 'stretch', 0)
130 setfield(spec_zero_glue, 'shrink', 0)
131 setfield(spec_zero_glue, 'stretch_order', 0)
132 setfield(spec_zero_glue, 'shrink_order', 0)
133 setfield(zero_glue, 'spec', spec_zero_glue)
135 local function skip_table_to_spec(n)
136 local g, st = node_new(id_glue_spec), ltjs.fast_get_stack_skip(n)
137 setfield(g, 'width', st.width)
138 setfield(g, 'stretch', st.stretch)
139 setfield(g, 'shrink', st.shrink)
140 setfield(g, 'stretch_order', st.stretch_order)
141 setfield(g, 'shrink_order', st.shrink_order)
147 local function add_penalty(p,e)
148 local pp = getfield(p, 'penalty')
150 if e<=-10000 then setfield(p, 'penalty', 0) end
151 elseif pp<=-10000 then
152 if e>=10000 then setfield(p, 'penalty', 0) end
155 if pp>=10000 then setfield(p, 'penalty', 10000)
156 elseif pp<=-10000 then setfield(p, 'penalty', -10000)
157 else setfield(p, 'penalty', pp) end
162 diffmet_rule = math.two_paverage
163 function math.two_add(a,b) return a+b end
164 function math.two_average(a,b) return (a+b)*0.5 end
165 function math.two_paverage(a,b) return (a+b)/2 end
166 function math.two_pleft(a,b) return a end
167 function math.two_pright(a,b) return b end
169 local head -- the head of current list
172 local widow_Bp, widow_Np -- \jcharwidowpenalty 挿入位置管理用
174 local non_ihb_flag -- JFM グルー挿入抑止用 flag
175 -- false: \inhibitglue 指定時 true: それ以外
177 -------------------- hlist 内の文字の検索
179 local first_char, last_char, find_first_char
181 local ltjd_glyph_from_packed = ltjd.glyph_from_packed
182 local function check_box(box_ptr, box_end)
183 local p = box_ptr; local found_visible_node = false
185 find_first_char = false; last_char = nil
188 while p and p~=box_end do
190 if pid==id_kern and getsubtype(p)==2 then
191 p = node_next(node_next(node_next(p))); pid = getid(p) -- p must be glyph_node
193 if pid==id_glyph then
195 if find_first_char then
196 first_char = p; find_first_char = false
198 last_char = p; found_visible_node = true; p=node_next(p)
199 if (not p) or p==box_end then
200 return found_visible_node
202 until getid(p)~=id_glyph
203 pid = getid(p) -- p must be non-nil
206 local pa = get_attr_icflag(p)
207 if pa==IC_PROCESSED then
209 elseif getsubtype(p)==2 then
210 p = node_next(node_next(p));
211 -- Note that another node_next will be executed outside this if-statement.
213 found_visible_node = true
214 find_first_char = false; last_char = nil
216 elseif pid==id_hlist then
217 if PACKED == get_attr_icflag(p) then
218 local s = ltjd_glyph_from_packed(p)
219 if find_first_char then
220 first_char = s; find_first_char = false
222 last_char = s; found_visible_node = true
224 if getfield(p, 'shift')==0 then
226 if check_box(getlist(p), nil) then found_visible_node = true end
228 find_first_char = false; last_char = nil
231 elseif pid==id_math then
232 if find_first_char then
233 first_char = p; find_first_char = false
235 last_char = p; found_visible_node = true
236 elseif pid==id_rule and get_attr_icflag(p)==PACKED then
238 elseif not (pid==id_ins or pid==id_mark
239 or pid==id_adjust or pid==id_whatsit
240 or pid==id_penalty) then
241 found_visible_node = true
242 find_first_char = false; last_char = nil
246 return found_visible_node
249 function check_box_high(Nx, box_ptr, box_end)
250 first_char = nil; last_char = nil; find_first_char = true
251 if check_box(box_ptr, box_end) then
252 local first_char = first_char
254 if getid(first_char)==id_glyph then
255 if getfield(first_char, 'lang') == lang_ja then
256 set_np_xspc_jachar_hbox(Nx, first_char)
258 set_np_xspc_alchar(Nx, getchar(first_char),first_char, 1)
261 set_np_xspc_alchar(Nx, -1,first_char)
268 -------------------- Np の計算と情報取得
270 luatexbase.create_callback("luatexja.jfmglue.whatsit_getinfo", "data",
271 function (Np, lp, Nq)
272 if Np.nuc then return Np
274 return Np -- your code
277 luatexbase.create_callback("luatexja.jfmglue.whatsit_after", "data",
278 function (stat, Nq, Np) return false end)
284 local traverse = Dnode.traverse
285 local function check_next_ickern(lp)
286 if lp and getid(lp) == id_kern and ITALIC == get_attr_icflag(lp) then
287 set_attr(lp, attr_icflag, IC_PROCESSED)
288 Np.last = lp; return node_next(lp)
290 Np.last = Np.nuc; return lp
294 local function calc_np_pbox(lp, last)
295 local first, lpa, nc = (not Np.first), KINSOKU, nil
296 Np.first = Np.first or lp; Np.id = id_pbox
297 set_attr(lp, attr_icflag, get_attr_icflag(lp));
298 while lp ~=last and (lpa>=PACKED) and (lpa<BOXBDD) do
299 local lpi = getid(lp)
300 if lpi==id_hlist or lpi==id_vlist then
301 head, lp, nc = ltjd_make_dir_whatsit(head, lp, list_dir, 'jfm pbox')
302 Np.first = first and nc or Np.first
303 elseif (lpi==id_rule) and (lpa==PACKED) then
305 nc, lp = lp, node_next(lp)
307 nc, lp = lp, node_next(lp)
309 first, lpa = false, (lp and has_attr(lp, attr_icflag) or 0)
310 -- get_attr_icflag() ではいけない!
313 lp = check_next_ickern(lp)
314 Np.last_char = check_box_high(Np, Np.first, lp)
318 local ltjw_apply_ashift_math = ltjw.apply_ashift_math
319 local ltjw_apply_ashift_disc = ltjw.apply_ashift_disc
320 local min, max = math.min, math.max
321 local function calc_np_aux_glyph_common(lp)
323 Np.first= (Np.first or lp)
324 if getfield(lp, 'lang') == lang_ja then
326 local m, mc, cls = set_np_xspc_jachar(Np, lp)
328 lp, head, npi, npf = capsule_glyph(lp, m, mc[cls], head, tex_dir)
329 Np.first = (Np.first~=Np.nuc) and Np.first or npf or npi
331 return true, check_next_ickern(lp);
334 set_np_xspc_alchar(Np, getchar(lp), lp, 1)
336 local first_glyph, last_glyph = lp
337 set_attr(lp, attr_icflag, PROCESSED); Np.last = lp
338 local y_adjust = has_attr(lp,attr_ablshift) or 0
339 local node_depth = getfield(lp, 'depth') + min(y_adjust, 0)
340 local adj_depth = (y_adjust>0) and (getfield(lp, 'depth') + y_adjust) or 0
341 setfield(lp, 'yoffset', getfield(lp, 'yoffset') - y_adjust)
343 for lx in traverse(lp) do
344 local lai = get_attr_icflag(lx)
345 if lx==last or lai>=PACKED then
348 local lid = getid(lx)
349 if lid==id_glyph and getfield(lx, 'lang') ~= lang_ja then
351 last_glyph = lx; set_attr(lx, attr_icflag, PROCESSED); Np.last = lx
352 y_adjust = has_attr(lx,attr_ablshift) or 0
353 node_depth = max(getfield(lx, 'depth') + min(y_adjust, 0), node_depth)
354 adj_depth = (y_adjust>0) and max(getfield(lx, 'depth') + y_adjust, adj_depth) or adj_depth
355 setfield(lx, 'yoffset', getfield(lx, 'yoffset') - y_adjust)
356 elseif lid==id_kern then
357 local ls = getsubtype(lx)
358 if ls==2 then -- アクセント用の kern
359 set_attr(lx, attr_icflag, PROCESSED)
360 lx = node_next(lx) -- lx: アクセント本体
361 setfield(lx, 'yoffset', getfield(lx, 'yoffset') - (has_attr(lx,attr_ablshift) or 0))
362 lx = node_next(node_next(lx))
365 elseif (ls==1 and lai==ITALIC) then
366 Np.last = lx; set_attr(lx, attr_icflag, IC_PROCESSED)
376 if adj_depth>node_depth then
377 r = node_new(id_rule)
378 setfield(r, 'width', 0); setfield(r, 'height', 0)
379 setfield(r, 'depth',adj_depth); setfield(r, 'dir', tex_dir)
380 set_attr(r, attr_icflag, PROCESSED)
383 Np.last_char = last_glyph
384 if r then insert_after(head, first_glyph, r) end
389 local nf, nc = getfont(npn), getchar(npn)
390 local ct = (font.getfont(nf) or font.fonts[nf] ).characters[nc]
391 if not ct then -- variation selector
393 elseif (ct.left_protruding or 0) == 0 then
394 head = insert_before(head, npn, r)
395 Np.first = (Np.first==npn) and r or npn
396 elseif (ct.right_protruding or 0) == 0 then
397 insert_after(head, npn, r); Np.last, lp = r, r
399 ltjb.package_warning_no_line(
401 'Check depth of glyph node ' .. tostring(npn) .. '(font=' .. nf
402 .. ', char=' .. nc .. '), because its \\lpcode is ' .. tostring(ct.left_protruding)
403 .. ' and its \\rpcode is ' .. tostring(ct.right_protruding)
411 local calc_np_auxtable = {
412 [id_glyph] = calc_np_aux_glyph_common,
413 [id_hlist] = function(lp)
415 head, lp, op, flag = ltjd_make_dir_whatsit(head, lp, list_dir, 'jfm hlist')
416 set_attr(op, attr_icflag, PROCESSED)
417 Np.first = Np.first or op; Np.last = op; Np.nuc = op;
418 if (flag or getfield(op, 'shift')~=0) then
422 Np.last_char = check_box_high(Np, getlist(op), nil)
426 [id_vlist] = function(lp)
428 head, lp, op = ltjd_make_dir_whatsit(head, lp, list_dir, 'jfm:' .. getid(lp))
429 Np.first = Np.first or op; Np.last = op; Np.nuc = op;
433 box_like = function(lp)
434 Np.first = Np.first or lp; Np.last = lp; Np.nuc = lp;
436 return true, node_next(lp)
439 set_attr(lp, attr_icflag, PROCESSED)
440 return false, node_next(lp)
442 [id_whatsit] = function(lp)
443 local lps = getsubtype(lp)
444 if lps==sid_user then
445 if getfield(lp, 'user_id')==luatexja.userid_table.IHB then
446 local lq = node_next(lp);
447 head = node_remove(head, lp); node_free(lp); non_ihb_flag = false
450 set_attr(lp, attr_icflag, PROCESSED)
451 luatexbase.call_callback("luatexja.jfmglue.whatsit_getinfo",
454 Np.id = id_pbox_w; Np.first = Np.nuc; Np.last = Np.nuc;
455 return true, node_next(lp)
457 return false, node_next(lp)
461 -- we do special treatment for these whatsit nodes.
462 if lps == sid_start_link or lps == sid_start_thread then
464 elseif lps == sid_end_link or lps == sid_end_thread then
465 Np.first, Nq.last = nil, lp;
467 set_attr(lp, attr_icflag, PROCESSED)
468 return false, node_next(lp)
471 [id_math] = function(lp)
472 Np.first, Np.nuc = (Np.first or lp), lp;
473 set_attr(lp, attr_icflag, PROCESSED)
474 set_np_xspc_alchar(Np, -1, lp)
475 local end_math = node_end_of_math(lp)
476 ltjw_apply_ashift_math(lp, end_math, attr_ablshift)
477 set_attr(end_math, attr_icflag, PROCESSED)
478 Np.last, Np.id = end_math, id_math;
479 return true, node_next(end_math);
481 [id_glue] = function(lp)
482 Np.first, Np.nuc, Np.last = (Np.first or lp), lp, lp;
483 Np.id = getid(lp); set_attr(lp, attr_icflag, PROCESSED)
484 return true, node_next(lp)
486 [id_disc] = function(lp)
487 Np.first, Np.nuc, Np.last = (Np.first or lp), lp, lp;
488 Np.id = getid(lp); set_attr(lp, attr_icflag, PROCESSED)
489 ltjw_apply_ashift_disc(lp, (list_dir==dir_tate), tex_dir)
490 Np.last_char = check_box_high(Np, getfield(lp, 'replace'), nil)
491 return true, node_next(lp)
493 [id_kern] = function(lp)
494 if getsubtype(lp)==2 then
495 Np.first = Np.first or lp
496 set_attr(lp, attr_icflag, PROCESSED); lp = node_next(lp)
497 set_attr(lp, attr_icflag, PROCESSED); lp = node_next(lp)
498 set_attr(lp, attr_icflag, PROCESSED); lp = node_next(lp)
499 set_attr(lp, attr_icflag, PROCESSED);
500 return calc_np_aux_glyph_common(lp)
502 Np.first = Np.first or lp
503 Np.id = id_kern; set_attr(lp, attr_icflag, PROCESSED)
504 Np.last = lp; return true, node_next(lp)
507 [id_penalty] = function(lp)
508 Bp[#Bp+1] = lp; set_attr(lp, attr_icflag, PROCESSED)
509 return false, node_next(lp)
512 calc_np_auxtable[id_rule] = calc_np_auxtable.box_like
513 calc_np_auxtable[13] = calc_np_auxtable.box_like
514 calc_np_auxtable[id_ins] = calc_np_auxtable.skip
515 calc_np_auxtable[id_mark] = calc_np_auxtable.skip
516 calc_np_auxtable[id_adjust] = calc_np_auxtable.skip
518 function calc_np(last, lp)
520 -- We assume lp = node_next(Np.last)
521 Np, Nq, non_ihb_flag = Nq, Np, true
522 -- We clear `predefined' entries of Np before pairs() loop,
523 -- because using only pairs() loop is slower.
524 Np.post, Np.pre, Np.xspc = nil, nil, nil
525 Np.first, Np.id, Np.last, Np.met, Np.class= nil, nil, nil, nil
526 Np.auto_kspc, Np.auto_xspc, Np.char, Np.nuc = nil, nil, nil, nil
527 for k in pairs(Np) do Np[k] = nil end
529 for k = 1,#Bp do Bp[k] = nil end
531 local lpa = has_attr(lp, attr_icflag) or 0
534 if lpa%PROCESSED_BEGIN_FLAG == BOXBDD then
535 local lq = node_next(lp)
536 head = node_remove(head, lp); node_free(lp); lp = lq
538 return calc_np_pbox(lp, last)
541 k, lp = calc_np_auxtable[getid(lp)](lp)
542 if k then return lp end
549 -- extract informations from Np
550 -- We think that "Np is a Japanese character" if Np.met~=nil,
551 -- "Np is an alphabetic character" if Np.pre~=nil,
552 -- "Np is not a character" otherwise.
553 after_hlist = nil -- global
554 local after_alchar, extract_np
556 local PRE = luatexja.stack_table_index.PRE
557 local POST = luatexja.stack_table_index.POST
558 local KCAT = luatexja.stack_table_index.KCAT
559 local XSP = luatexja.stack_table_index.XSP
560 local dir_tate = luatexja.dir_table.dir_tate
563 local attr_jchar_class = luatexbase.attributes['ltj@charclass']
564 local attr_jchar_code = luatexbase.attributes['ltj@charcode']
565 local attr_autospc = luatexbase.attributes['ltj@autospc']
566 local attr_autoxspc = luatexbase.attributes['ltj@autoxspc']
567 --local ltjf_get_vert_glyph = ltjf.get_vert_glyph
568 function set_np_xspc_jachar(Nx, x)
569 local m = ltjf_font_metric_table[getfont(x)]
570 local c, c_glyph = ltjs_orig_char_table[x], getchar(x)
572 local cls = slow_find_char_class(c, m, c_glyph)
573 Nx.met, Nx.class, Nx.char = m, cls, c;
574 local mc = m.char_type; Nx.char_type = mc
575 if cls~=0 then set_attr(x, attr_jchar_class, cls) end
576 if c~=c_glyph then set_attr(x, attr_jchar_code, c) end
577 Nx.pre = table_current_stack[PRE + c] or 0
578 Nx.post = table_current_stack[POST + c] or 0
579 Nx.xspc = table_current_stack[XSP + c] or 3
580 Nx.kcat = table_current_stack[KCAT + c] or 0
581 Nx.auto_kspc, Nx.auto_xspc = (has_attr(x, attr_autospc)==1), (has_attr(x, attr_autoxspc)==1)
584 function set_np_xspc_jachar_hbox(Nx, x)
585 local m = ltjf_font_metric_table[getfont(x)]
586 local c = has_attr(x, attr_jchar_code) or getchar(x)
587 Nx.met, Nx.char = m, c; Nx.class = has_attr(x, attr_jchar_class) or 0;
588 local mc = m.char_type; Nx.char_type = mc
589 Nx.pre = table_current_stack[PRE + c] or 0
590 Nx.post = table_current_stack[POST + c] or 0
591 Nx.xspc = table_current_stack[XSP + c] or 3
592 Nx.kcat = table_current_stack[KCAT + c] or 0
593 Nx.auto_kspc, Nx.auto_xspc = (has_attr(x, attr_autospc)==1), (has_attr(x, attr_autoxspc)==1)
597 local floor = math.floor
598 function set_np_xspc_alchar(Nx, c,x, lig)
600 local f = (lig ==1) and nullfunc or node_tail
601 local xc, xs = getfield(x, 'components'), getsubtype(x)
602 while xc and xs and xs%4>=2 do
603 x = f(xc); xc, xs = getfield(x, 'components'), getsubtype(x)
606 Nx.pre = table_current_stack[PRE + c] or 0
607 Nx.post = table_current_stack[POST + c] or 0
608 Nx.xspc = table_current_stack[XSP + c] or 3
610 Nx.pre, Nx.post = 0, 0
611 Nx.xspc = table_current_stack[XSP - 1] or 3
614 Nx.auto_xspc = (has_attr(x, attr_autoxspc)==1)
616 local set_np_xspc_alchar = set_np_xspc_alchar
618 -- change the information for the next loop
619 -- (will be done if Nx is an alphabetic character or a hlist)
620 after_hlist = function (Nx)
621 local s = Nx.last_char
623 if getid(s)==id_glyph then
624 if getfield(s, 'lang') == lang_ja then
625 set_np_xspc_jachar_hbox(Nx, s)
627 set_np_xspc_alchar(Nx, getchar(s), s, 2)
630 set_np_xspc_alchar(Nx, -1, s)
633 Nx.pre, Nx.met = nil, nil
637 after_alchar = function (Nx)
638 local x = Nx.last_char
639 return set_np_xspc_alchar(Nx, getchar(x), x, 2)
644 -------------------- 最下層の処理
646 -- change penalties (or create a new penalty, if needed)
647 local function handle_penalty_normal(post, pre, g)
648 local a = (pre or 0) + (post or 0)
650 if (a~=0 and not(g and getid(g)==id_kern)) then
651 local p = node_new(id_penalty)
652 if a<-10000 then a = -10000 elseif a>10000 then a = 10000 end
653 setfield(p, 'penalty', a)
654 head = insert_before(head, Np.first, p)
656 set_attr(p, attr_icflag, KINSOKU)
658 else for _, v in pairs(Bp) do add_penalty(v,a) end
662 local function handle_penalty_always(post, pre, g)
663 local a = (pre or 0) + (post or 0)
665 if not (g and getid(g)==id_glue) or a~=0 then
666 local p = node_new(id_penalty)
667 if a<-10000 then a = -10000 elseif a>10000 then a = 10000 end
668 setfield(p, 'penalty', a)
669 head = insert_before(head, Np.first, p)
671 set_attr(p, attr_icflag, KINSOKU)
673 else for _, v in pairs(Bp) do add_penalty(v,a) end
677 local function handle_penalty_suppress(post, pre, g)
678 local a = (pre or 0) + (post or 0)
680 if g and getid(g)==id_glue then
681 local p = node_new(id_penalty)
682 setfield(p, 'penalty', 10000); head = insert_before(head, Np.first, p)
684 set_attr(p, attr_icflag, KINSOKU)
686 else for _, v in pairs(Bp) do add_penalty(v,a) end
690 -- 和文文字間の JFM glue を node 化
691 local function new_jfm_glue(mc, bc, ac)
692 -- bc, ac: char classes
696 local f = node_new(id_glue)
697 set_attr(f, attr_icflag, g.priority)
698 setfield(f, 'spec', node_copy(g[2]))
699 return f, g.ratio, g.kanjiskip_natural, g.kanjiskip_stretch, g.kanjiskip_shrink
701 return node_copy(g[2]), g.ratio, false, false, false
707 -- Nq.last (kern w) .... (glue/kern g) Np.first
708 local function real_insert(g)
710 head = insert_before(head, Np.first, g)
716 -------------------- 和文文字間空白量の決定
719 local bg_ag = 2*id_glue - id_glue
720 local bg_ak = 2*id_glue - id_kern
721 local bk_ag = 2*id_kern - id_glue
722 local bk_ak = 2*id_kern - id_kern
724 local function blend_diffmet(b, a, rb, ra)
725 return round(diffmet_rule((1-rb)*b+rb*a, (1-ra)*b+ra*a))
727 calc_ja_ja_aux = function (gb, ga, db, da)
728 if diffmet_rule ~= math.two_pleft and diffmet_rule ~= math.two_pright
729 and diffmet_rule ~= math.two_paverage then
734 gb = node_new(id_kern); setfield(gb, 'kern', 0)
737 ga = node_new(id_kern); setfield(ga, 'kern', 0)
740 local k = 2*getid(gb) - getid(ga)
742 local bs, as = getfield(gb, 'spec'), getfield(ga, 'spec')
744 setfield(bs, 'width', blend_diffmet(
745 getfield(bs, 'width'), getfield(as, 'width'), db, da))
746 setfield(bs, 'stretch', blend_diffmet(
747 getfield(bs, 'stretch'), getfield(as, 'stretch'), db, da))
748 setfield(bs, 'shrink', -blend_diffmet(
749 -getfield(bs, 'shrink'), -getfield(as, 'shrink'), db, da))
752 elseif k == bk_ak then
754 setfield(gb, 'kern', blend_diffmet(
755 getfield(gb, 'kern'), getfield(ga, 'kern'), db, da))
758 elseif k == bk_ag then
759 local as = getfield(ga, 'spec')
760 -- gb: kern, ga: glue
761 setfield(as, 'width', blend_diffmet(
762 getfield(gb, 'kern'), getfield(as, 'width'), db, da))
763 setfield(as, 'stretch', blend_diffmet(
764 0, getfield(as, 'stretch'), db, da))
765 setfield(as, 'shrink', -blend_diffmet(
766 0, -getfield(as, 'shrink'), db, da))
770 local bs = getfield(gb, 'spec')
771 -- gb: glue, ga: kern
772 setfield(bs, 'width', blend_diffmet(
773 getfield(bs, 'width'), getfield(ga, 'kern'), db, da))
774 setfield(bs, 'stretch', blend_diffmet(
775 getfield(bs, 'stretch'), 0, db, da))
776 setfield(bs, 'shrink', -blend_diffmet(
777 -getfield(bs, 'shrink'), 0, db, da))
784 local null_skip_table = {0, 0, 0}
786 local get_kanjiskip, kanjiskip_jfm_flag
787 local calc_ja_ja_glue
789 local KANJI_SKIP = luatexja.icflag_table.KANJI_SKIP
790 local KANJI_SKIP_JFM = luatexja.icflag_table.KANJI_SKIP_JFM
792 get_kanjiskip_low = function(flag, qm, bn, bp, bh)
793 if flag or (qm.with_kanjiskip and (bn or bp or bh)) then
794 if kanjiskip_jfm_flag then
795 local g = node_new(id_glue);
796 local gx = node_new(id_glue_spec);
797 setfield(gx, 'stretch_order', 0); setfield(gx, 'shrink_order', 0)
798 local bk = qm.kanjiskip or null_skip_table
799 setfield(gx, 'width', bn and (bn*bk[1]) or 0)
800 setfield(gx, 'stretch', bp and (bp*bk[2]) or 0)
801 setfield(gx, 'shrink', bh and (bh*bk[3]) or 0)
802 setfield(g, 'spec', gx)
803 set_attr(g, attr_icflag, KANJI_SKIP_JFM)
806 return node_copy(kanji_skip)
808 local g = node_new(id_glue);
809 local gx = node_new(id_glue_spec);
810 setfield(gx, 'stretch_order', 0); setfield(gx, 'shrink_order', 0)
811 local ks = getfield(kanji_skip, 'spec')
812 setfield(gx, 'width', bn and (bn*getfield(ks, 'width')) or 0)
813 setfield(gx, 'stretch', bp and (bp*getfield(ks, 'stretch')) or 0)
814 setfield(gx, 'shrink', bh and (bh*getfield(ks, 'shrink')) or 0)
815 setfield(g, 'spec', gx)
816 set_attr(g, attr_icflag, KANJI_SKIP_JFM)
822 get_kanjiskip = function()
823 if Np.auto_kspc or Nq.auto_kspc then
824 local pm, qm = Np.met, Nq.met
825 if (pm.char_type==qm.char_type) and (qm.var==pm.var) then
826 return get_kanjiskip_low(true, qm, 1, 1, 1)
828 local gb = get_kanjiskip_low(true, qm, 1, 1, 1)
829 local ga = get_kanjiskip_low(true, pm, 1, 1, 1)
830 return calc_ja_ja_aux(gb, ga, 0, 1)
833 local g = node_copy(zero_glue)
834 set_attr(g, attr_icflag, kanjiskip_jfm_flag and KANJI_SKIP_JFM or KANJI_SKIP)
839 calc_ja_ja_glue = function ()
840 local qm, pm = Nq.met, Np.met
841 local qmc, pmc = qm.char_type, pm.char_type
842 if (qmc==pmc) and (qm.var==pm.var) then
843 local g, _, kn, kp, kh = new_jfm_glue(qmc, Nq.class, Np.class)
844 return g, (Np.auto_kspc or Nq.auto_kspc) and get_kanjiskip_low(false, qm, kn, kp, kh)
846 local npn, nqn = Np.nuc, Nq.nuc
847 local gb, db, bn, bp, bh
848 = new_jfm_glue(qmc, Nq.class,
849 slow_find_char_class(Np.char,
851 local ga, da, an, ap, ah
853 slow_find_char_class(Nq.char,
856 local g = calc_ja_ja_aux(gb, ga, db, da)
858 if (pmc==qmc) and (qm.var==pm.var) then
859 gb = get_kanjiskip_low(false, qm, bn, bp, bh)
860 ga = get_kanjiskip_low(false, pm, an, ap, ah)
861 k = calc_ja_ja_aux(gb, ga, db, da)
868 -------------------- 和欧文間空白量の決定
871 local get_xkanjiskip, xkanjiskip_jfm_flag
872 local get_xkanjiskip_normal, get_xkanjiskip_jfm
874 local XKANJI_SKIP = luatexja.icflag_table.XKANJI_SKIP
875 local XKANJI_SKIP_JFM = luatexja.icflag_table.XKANJI_SKIP_JFM
877 get_xkanjiskip_low = function(flag, qm, bn, bp, bh)
878 if flag or (qm.with_kanjiskip and (bn or bp or bh)) then
879 if xkanjiskip_jfm_flag then
880 local g = node_new(id_glue);
881 local gx = node_new(id_glue_spec);
882 setfield(gx, 'stretch_order', 0); setfield(gx, 'shrink_order', 0)
883 local bk = qm.xkanjiskip or null_skip_table
884 setfield(gx, 'width', bn and bk[1] or 0)
885 setfield(gx, 'stretch', bp and bk[2] or 0)
886 setfield(gx, 'shrink', bh and bk[3] or 0)
887 setfield(g, 'spec', gx)
888 set_attr(g, attr_icflag, XKANJI_SKIP_JFM)
891 return node_copy(xkanji_skip)
893 local g = node_new(id_glue);
894 local gx = node_new(id_glue_spec);
895 setfield(gx, 'stretch_order', 0); setfield(gx, 'shrink_order', 0)
896 local ks = getfield(xkanji_skip, 'spec')
897 setfield(gx, 'width', bn and getfield(ks, 'width') or 0)
898 setfield(gx, 'stretch', bp and getfield(ks, 'stretch') or 0)
899 setfield(gx, 'shrink', bh and getfield(ks, 'shrink') or 0)
900 setfield(g, 'spec', gx)
901 set_attr(g, attr_icflag, XKANJI_SKIP_JFM)
907 get_xkanjiskip = function(Nn)
908 if (Nq.xspc>=2) and (Np.xspc%2==1) and (Nq.auto_xspc or Np.auto_xspc) then
909 return get_xkanjiskip_low(true, Nn.met, 1, 1, 1)
911 local g = node_copy(zero_glue)
912 set_attr(g, attr_icflag, xkanjiskip_jfm_flag and XKANJI_SKIP_JFM or XKANJI_SKIP)
918 -------------------- 隣接した「塊」間の処理
920 local function get_OA_skip(is_kanji)
922 local g, _, kn, kp, kh = new_jfm_glue(
924 fast_find_char_class((Nq.id == id_math and -1 or 'jcharbdd'), pm),
928 k = (Np.auto_kspc or Nq.auto_kspc) and get_kanjiskip_low(false, pm, kn, kp, kh)
929 elseif is_kanji==1 then
930 k = ((Nq.xspc>=2) and (Np.xspc%2==1) and (Nq.auto_xspc or Np.auto_xspc))
931 and get_xkanjiskip_low(false, pm, kn, kp, kh)
935 local function get_OB_skip(is_kanji)
937 local g, _, kn, kp, kh = new_jfm_glue(
938 qm.char_type, Nq.class,
939 fast_find_char_class((Np.id == id_math and -1 or'jcharbdd'), qm))
942 k = (Np.auto_kspc or Nq.auto_kspc) and get_kanjiskip_low(false, qm, kn, kp, kh)
943 elseif is_kanji==1 then
944 k = ((Nq.xspc>=2) and (Np.xspc%2==1) and (Nq.auto_xspc or Np.auto_xspc))
945 and get_xkanjiskip_low(false, qm, kn, kp, kh)
950 -- (anything) .. jachar
951 local function handle_np_jachar(mode)
953 if qid==id_jglyph or ((qid==id_pbox or qid==id_pbox_w) and Nq.met) then
955 if non_ihb_flag then g, k = calc_ja_ja_glue() end -- M->K
956 if not g then g = get_kanjiskip() end
957 handle_penalty_normal(Nq.post, Np.pre, g);
958 real_insert(g); real_insert(k)
959 elseif Nq.met then -- qid==id_hlist
961 if non_ihb_flag then g, k = get_OA_skip(0) end -- O_A->K
962 if not g then g = get_kanjiskip() end
963 handle_penalty_normal(0, Np.pre, g); real_insert(g); real_insert(k)
966 if non_ihb_flag then g, k = get_OA_skip(1) end -- O_A->X
967 if not g then g = get_xkanjiskip(Np) end
968 handle_penalty_normal((qid==id_hlist and 0 or Nq.post), Np.pre, g);
969 real_insert(g); real_insert(k)
971 local g = non_ihb_flag and (get_OA_skip()) -- O_A
972 if qid==id_glue then handle_penalty_normal(0, Np.pre, g)
973 elseif qid==id_kern then handle_penalty_suppress(0, Np.pre, g)
974 else handle_penalty_always(0, Np.pre, g)
978 if mode and Np.kcat%2~=1 then
979 widow_Np.first, widow_Bp, Bp = Np.first, Bp, widow_Bp
984 -- jachar .. (anything)
985 local function handle_nq_jachar()
987 local g = non_ihb_flag and get_OB_skip(1) or get_xkanjiskip(Nq) -- O_B->X
988 handle_penalty_normal(Nq.post, (Np.id==id_hlist and 0 or Np.pre), g); real_insert(g)
990 local g =non_ihb_flag and (get_OB_skip()) -- O_B
991 if Np.id==id_glue then handle_penalty_normal(Nq.post, 0, g)
992 elseif Np.id==id_kern then handle_penalty_suppress(Nq.post, 0, g)
993 else handle_penalty_always(Nq.post, 0, g)
999 -- (anything) .. (和文文字で始まる hlist)
1000 local function handle_np_ja_hlist()
1002 if qid==id_jglyph or ((qid==id_pbox or Nq.id == id_pbox_w) and Nq.met) then
1003 local g = non_ihb_flag and get_OB_skip(0) or get_kanjiskip() -- O_B->K
1004 handle_penalty_normal(Nq.post, 0, g); real_insert(g)
1005 elseif Nq.met then -- Nq.id==id_hlist
1006 local g = get_kanjiskip() -- K
1007 handle_penalty_suppress(0, 0, g); real_insert(g)
1009 local g = get_xkanjiskip(Np) -- X
1010 handle_penalty_suppress(0, 0, g); real_insert(g)
1014 -- (和文文字で終わる hlist) .. (anything)
1015 local function handle_nq_ja_hlist()
1017 local g = get_xkanjiskip(Nq) -- X
1018 handle_penalty_suppress(0, 0, g); real_insert(g)
1023 -- Nq が前側のクラスタとなることによる修正
1025 local adjust_nq_aux = {
1026 [id_glyph] = function() after_alchar(Nq) end, -- after_alchar(Nq)
1027 [id_hlist] = function() after_hlist(Nq) end,
1028 [id_pbox] = function() after_hlist(Nq) end,
1029 [id_disc] = function() after_hlist(Nq) end,
1030 [id_pbox_w] = function()
1031 luatexbase.call_callback("luatexja.jfmglue.whatsit_after",
1036 function adjust_nq()
1037 local x = adjust_nq_aux[Nq.id]
1043 -------------------- 開始・終了時の処理
1047 local JWP = luatexja.stack_table_index.JWP
1048 local function handle_list_tail(mode)
1049 adjust_nq(); Np = Nq
1051 -- the current list is to be line-breaked.
1052 -- Insert \jcharwidowpenalty
1053 Bp = widow_Bp; Np = widow_Np
1055 handle_penalty_normal(0, table_current_stack[JWP] or 0)
1058 -- the current list is the contents of a hbox
1059 local npi, pm = Np.id, Np.met
1060 if npi == id_jglyph or (npi==id_pbox and pm) then
1061 local g = new_jfm_glue(pm.char_type, Np.class, fast_find_char_class('boxbdd', pm))
1063 set_attr(g, attr_icflag, BOXBDD)
1064 head = insert_after(head, Np.last, g)
1071 local function handle_list_head(par_indented)
1072 local npi, pm = Np.id, Np.met
1073 if npi == id_jglyph or (npi==id_pbox and pm) then
1074 if non_ihb_flag then
1075 local g = new_jfm_glue(pm.char_type, fast_find_char_class(par_indented, pm), Np.class)
1077 set_attr(g, attr_icflag, BOXBDD)
1078 if getid(g)==id_glue and #Bp==0 then
1079 local h = node_new(id_penalty)
1080 setfield(h, 'penalty', 10000); set_attr(h, attr_icflag, BOXBDD)
1082 head = insert_before(head, Np.first, g)
1089 -- return value: (the initial cursor lp), (last node)
1092 local KANJI_SKIP = luatexja.icflag_table.KANJI_SKIP
1093 local XKANJI_SKIP = luatexja.icflag_table.XKANJI_SKIP
1094 local KSK = luatexja.stack_table_index.KSK
1095 local XSK = luatexja.stack_table_index.XSK
1096 local dir_yoko = luatexja.dir_table.dir_yoko
1097 local dir_tate = luatexja.dir_table.dir_tate
1098 local attr_yablshift = luatexbase.attributes['ltj@yablshift']
1099 local attr_tablshift = luatexbase.attributes['ltj@tablshift']
1100 local table_pool = {
1101 {}, {}, {first=nil},
1102 { auto_kspc=nil, auto_xspc=nil, char=nil, class=nil,
1103 first=nil, id=nil, last=nil, met=nil, nuc=nil,
1104 post=nil, pre=nil, xspc=nil, },
1105 { auto_kspc=nil, auto_xspc=nil, char=nil, class=nil,
1106 first=nil, id=nil, last=nil, met=nil, nuc=nil,
1107 post=nil, pre=nil, xspc=nil, },
1109 init_var = function (mode,dir)
1110 -- 1073741823: max_dimen
1111 Bp, widow_Bp, widow_Np, Np, Nq
1112 = table_pool[1], table_pool[2], table_pool[3], table_pool[4], table_pool[5]
1113 for i=1,5 do for j,_ in pairs(table_pool[i]) do table_pool[i][j]=nil end end
1114 table_current_stack = ltjs.table_current_stack
1116 list_dir, tex_dir = (ltjs.list_dir or dir_yoko), (dir or 'TLT')
1117 local is_dir_tate = list_dir==dir_tate
1118 capsule_glyph = is_dir_tate and ltjw.capsule_glyph_tate or ltjw.capsule_glyph_yoko
1119 attr_ablshift = is_dir_tate and attr_tablshift or attr_yablshift
1120 local TEMP = node_new(id_glue)
1121 -- TEMP is a dummy node, which will be freed at the end of the callback.
1122 -- ithout this node, set_attr(kanji_skip, ...) somehow creates an "orphaned" attribute list.
1125 kanji_skip = node_new(id_glue); set_attr(kanji_skip, attr_icflag, KANJI_SKIP)
1126 local s = skip_table_to_spec(KSK)
1127 setfield(kanji_skip, 'spec', s)
1128 kanjiskip_jfm_flag = (getfield(s, 'width') == 1073741823)
1132 xkanji_skip = node_new(id_glue); set_attr(xkanji_skip, attr_icflag, XKANJI_SKIP)
1133 local s = skip_table_to_spec(XSK)
1134 setfield(xkanji_skip, 'spec', s)
1135 xkanjiskip_jfm_flag = (getfield(s, 'width') == 1073741823)
1139 -- the current list is to be line-breaked:
1140 -- hbox from \parindent is skipped.
1141 local lp, par_indented, lpi, lps = head, 'boxbdd', getid(head), getsubtype(head)
1142 while lp and ((lpi==id_whatsit and lps~=sid_user)
1143 or ((lpi==id_hlist) and (lps==3))) do
1144 if (lpi==id_hlist) and (lps==3) then
1145 Np.char, par_indented = 'parbdd', 'parbdd'
1146 Np.width = getfield(lp, 'width')
1148 lp=node_next(lp); lpi, lps = getid(lp), getsubtype(lp) end
1149 return lp, node_tail(head), par_indented, TEMP
1151 return head, nil, 'boxbdd', TEMP
1156 local ensure_tex_attr = ltjb.ensure_tex_attr
1157 local function cleanup(mode, TEMP)
1158 -- adjust attr_icflag for avoiding error
1159 if tex.getattribute(attr_icflag)~=0 then ensure_tex_attr(attr_icflag, 0) end
1160 node_free(kanji_skip);
1161 node_free(xkanji_skip); node_free(TEMP)
1164 local h = node_next(head)
1165 if getid(h) == id_penalty and getfield(h, 'penalty') == 10000 then
1167 if getid(h) == id_glue and getsubtype(h) == 15 and not node_next(h) then
1174 -------------------- 外部から呼ばれる関数
1177 function main(ahead, mode, dir)
1178 if not ahead then return ahead end
1180 local lp, last, par_indented, TEMP = init_var(mode,dir)
1181 lp = calc_np(last, lp)
1183 handle_list_head(par_indented)
1184 lp = calc_np(last,lp); while Np do
1186 local pid, pm = Np.id, Np.met
1188 if pid == id_jglyph then
1189 handle_np_jachar(mode)
1191 if pid==id_hlist then handle_np_ja_hlist()
1192 else handle_np_jachar() end
1194 if Nq.id==id_hlist then handle_nq_ja_hlist()
1195 else handle_nq_jachar() end
1197 lp = calc_np(last,lp)
1199 handle_list_tail(mode)
1201 return cleanup(mode, TEMP)
1206 local IHB = luatexja.userid_table.IHB
1207 local BPAR = luatexja.userid_table.BPAR
1208 local node_prev = (Dnode ~= node) and Dnode.getprev or node.prev
1209 local node_write = Dnode.write
1212 function create_inhibitglue_node()
1213 local tn = node_new(id_whatsit, sid_user)
1214 setfield(tn, 'user_id', IHB)
1215 setfield(tn, 'type', 100)
1216 setfield(tn, 'value', 1)
1220 -- Node for indicating beginning of a paragraph
1221 -- (for ltjsclasses)
1222 function create_beginpar_node()
1223 local tn = node_new(id_whatsit, sid_user)
1224 setfield(tn, 'user_id', BPAR)
1225 setfield(tn, 'type', 100)
1226 setfield(tn, 'value', 1)
1230 local function whatsit_callback(Np, lp, Nq)
1231 if Np and Np.nuc then return Np
1232 elseif Np and getfield(lp, 'user_id') == BPAR then
1233 Np.first = lp; Np.nuc = lp; Np.last = lp
1238 local function whatsit_after_callback(s, Nq, Np)
1239 if not s and getfield(Nq.nuc, 'user_id') == BPAR then
1240 local x, y = node_prev(Nq.nuc), Nq.nuc
1241 Nq.first, Nq.nuc, Nq.last = x, x, x
1244 Nq.class = fast_find_char_class('parbdd', Np.met)
1246 Nq.met = Np.met; Nq.pre = 0; Nq.post = 0; Nq.xspc = 0
1247 Nq.auto_xspc = false
1249 head = node_remove(head, y)
1255 luatexbase.add_to_callback("luatexja.jfmglue.whatsit_getinfo", whatsit_callback,
1256 "luatexja.beginpar.np_info", 1)
1257 luatexbase.add_to_callback("luatexja.jfmglue.whatsit_after", whatsit_after_callback,
1258 "luatexja.beginpar.np_info_after", 1)