OSDN Git Service

ltj-jfont.lua: braced font specification
[luatex-ja/luatexja.git] / src / ltj-jfmglue.lua
1 --
2 -- luatexja/ltj-jfmglue.lua
3 --
4 luatexbase.provides_module({
5   name = 'luatexja.jfmglue',
6   date = '2015/05/03',
7   description = 'Insertion process of JFM glues and kanjiskip',
8 })
9 module('luatexja.jfmglue', package.seeall)
10 local err, warn, info, log = luatexbase .errwarinf(_NAME)
11
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
17 local pairs = pairs
18
19 local Dnode = node.direct or node
20
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
24
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
32
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
48
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')
62
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
70
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')
75
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
84
85 local attr_icflag = luatexbase.attributes['ltj@icflag']
86 local kanji_skip
87 local xkanji_skip
88 local table_current_stack
89 local list_dir
90 local capsule_glyph
91 local tex_dir
92 local attr_ablshift
93 local set_np_xspc_jachar
94 local set_np_xspc_jachar_hbox
95
96 local ltjs_orig_char_table = ltjs.orig_char_table
97
98 local function get_attr_icflag(p)
99    return (has_attr(p, attr_icflag) or 0)%PROCESSED_BEGIN_FLAG
100 end
101
102 -------------------- Helper functions
103
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
107 end
108
109 -- 文字クラスの決定
110 local slow_find_char_class
111 do
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)
118       else
119          return cls
120       end
121    end
122 end
123
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)
134
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)
142    return g
143 end
144
145
146 -- penalty 値の計算
147 local function add_penalty(p,e)
148    local pp = getfield(p, 'penalty')
149    if pp>=10000 then
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
153    else
154       pp = pp + e
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
158    end
159 end
160
161 -- 「異なる JFM」の間の調整方法
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
168
169 local head -- the head of current list
170
171 local Np, Nq, Bp
172 local widow_Bp, widow_Np -- \jcharwidowpenalty 挿入位置管理用
173
174 local non_ihb_flag -- JFM グルー挿入抑止用 flag
175 -- false: \inhibitglue 指定時 true: それ以外
176
177 -------------------- hlist 内の文字の検索
178
179 local first_char, last_char, find_first_char
180 do
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
184    if not p then
185       find_first_char = false; last_char = nil
186       return true
187    end
188    while p and p~=box_end do
189       local pid = getid(p)
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
192        end
193       if pid==id_glyph then
194          repeat
195             if find_first_char then
196                first_char = p; find_first_char = false
197             end
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
201             end
202          until getid(p)~=id_glyph
203          pid = getid(p) -- p must be non-nil
204       end
205       if pid==id_kern then
206          local pa = get_attr_icflag(p)
207          if pa==IC_PROCESSED then
208             -- do nothing
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.
212          else
213             found_visible_node = true
214             find_first_char = false; last_char = nil
215          end
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
221             end
222             last_char = s; found_visible_node = true
223          else
224             if getfield(p, 'shift')==0 then
225                last_char = nil
226                if check_box(getlist(p), nil) then found_visible_node = true end
227             else
228                find_first_char = false; last_char = nil
229             end
230          end
231       elseif pid==id_math then
232          if find_first_char then
233             first_char = p; find_first_char = false
234          end
235          last_char = p; found_visible_node = true
236       elseif pid==id_rule and get_attr_icflag(p)==PACKED then
237          -- do nothing
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
243       end
244       p = node_next(p)
245    end
246    return found_visible_node
247 end
248
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
253       if first_char then
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)
257             else
258                set_np_xspc_alchar(Nx, getchar(first_char),first_char, 1)
259             end
260          else -- math_node
261             set_np_xspc_alchar(Nx, -1,first_char)
262          end
263       end
264    end
265    return last_char
266 end
267 end
268 -------------------- Np の計算と情報取得
269
270 luatexbase.create_callback("luatexja.jfmglue.whatsit_getinfo", "data",
271                            function (Np, lp, Nq)
272                               if Np.nuc then return Np
273                               else
274                                  return Np  -- your code
275                               end
276                            end)
277 luatexbase.create_callback("luatexja.jfmglue.whatsit_after", "data",
278                            function (stat, Nq, Np) return false end)
279
280 -- calc next Np
281 local calc_np 
282 do
283
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)
289    else
290       Np.last = Np.nuc; return lp
291    end
292 end
293
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
304          lp = node_next(lp)
305          nc, lp = lp, node_next(lp)
306       else
307          nc, lp = lp, node_next(lp)
308       end
309       first, lpa = false, (lp and has_attr(lp, attr_icflag) or 0)
310      -- get_attr_icflag() ではいけない!
311    end
312    Np.nuc = nc
313    lp = check_next_ickern(lp)
314    Np.last_char = check_box_high(Np, Np.first, lp)
315    return lp
316 end
317
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)
322    Np.nuc = lp
323    Np.first= (Np.first or lp)
324    if getfield(lp, 'lang') == lang_ja then
325       Np.id = id_jglyph
326       local m, mc, cls = set_np_xspc_jachar(Np, lp)
327       local npi, npf
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
330       Np.nuc = npi
331       return true, check_next_ickern(lp);
332    else
333       Np.id = id_glyph
334       set_np_xspc_alchar(Np, getchar(lp), lp, 1)
335       -- loop
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)
342       lp = node_next(lp)
343       for lx in traverse(lp) do
344          local lai = get_attr_icflag(lx)
345          if lx==last or  lai>=PACKED then
346             lp=lx; break
347          else
348             local lid = getid(lx)
349             if lid==id_glyph and getfield(lx, 'lang') ~= lang_ja then
350                -- 欧文文字
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))
363                elseif ls==0  then
364                   Np.last = lx
365                elseif (ls==1 and lai==ITALIC) then
366                   Np.last = lx; set_attr(lx, attr_icflag, IC_PROCESSED)
367                else
368                   lp=lx; break
369                end
370             else
371                lp=lx; break
372             end
373          end
374       end
375       local r
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)
381       end
382       if last_glyph then
383          Np.last_char = last_glyph
384          if r then insert_after(head, first_glyph, r) end
385       else
386          local npn = Np.nuc
387          Np.last_char = npn
388          if r then
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
392                node_free(r)
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
398             else
399                ltjb.package_warning_no_line(
400                   'luatexja',
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)
404                ); node_free(r)
405             end
406          end
407       end
408       return true, lp
409    end
410 end
411 local calc_np_auxtable = {
412    [id_glyph] = calc_np_aux_glyph_common,
413    [id_hlist] = function(lp)
414       local op, flag
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
419          Np.id = id_box_like
420       else
421          Np.id = id_hlist
422          Np.last_char = check_box_high(Np, getlist(op), nil)
423       end
424       return true, lp
425    end,
426    [id_vlist] =  function(lp)
427       local op
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;
430       Np.id = id_box_like;
431       return true, lp
432    end,
433    box_like = function(lp)
434       Np.first = Np.first or lp; Np.last = lp; Np.nuc = lp;
435       Np.id = id_box_like;
436       return true, node_next(lp)
437    end,
438    skip = function(lp)
439       set_attr(lp, attr_icflag, PROCESSED)
440       return false, node_next(lp)
441    end,
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
448             return false, lq;
449          else
450             set_attr(lp, attr_icflag, PROCESSED)
451             luatexbase.call_callback("luatexja.jfmglue.whatsit_getinfo",
452                                      Np, lp, Nq)
453             if Np.nuc then
454                Np.id = id_pbox_w; Np.first = Np.nuc; Np.last = Np.nuc;
455                return true, node_next(lp)
456             else
457                return false, node_next(lp)
458             end
459          end
460       else
461          -- we do special treatment for these whatsit nodes.
462          if lps == sid_start_link or lps == sid_start_thread then
463             Np.first = lp
464          elseif lps == sid_end_link or lps == sid_end_thread then
465             Np.first, Nq.last = nil, lp;
466          end
467          set_attr(lp, attr_icflag, PROCESSED)
468          return false, node_next(lp)
469       end
470    end,
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);
480    end,
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)
485    end,
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)
492    end,
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)
501       else
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)
505       end
506    end,
507    [id_penalty] = function(lp)
508       Bp[#Bp+1] = lp; set_attr(lp, attr_icflag, PROCESSED)
509       return false, node_next(lp)
510    end,
511 }
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
517
518 function calc_np(last, lp)
519    local k
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
528
529    for k = 1,#Bp do Bp[k] = nil end
530    while lp ~= last  do
531       local lpa = has_attr(lp, attr_icflag) or 0
532        -- unbox 由来ノードの検出
533       if lpa>=PACKED then
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
537          else
538             return calc_np_pbox(lp, last)
539          end -- id_pbox
540       else
541          k, lp = calc_np_auxtable[getid(lp)](lp)
542          if k then return lp end
543       end
544    end
545    Np=nil
546 end
547 end
548
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
555 do
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
561
562 -- 和文文字のデータを取得
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)
571       c = c or c_glyph
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)
582       return m, mc, cls
583    end
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)
594    end
595
596 -- 欧文文字のデータを取得
597    local floor = math.floor
598    function set_np_xspc_alchar(Nx, c,x, lig)
599       if c~=-1 then
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)
604          end
605          c = getchar(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
609       else
610          Nx.pre, Nx.post = 0, 0
611          Nx.xspc = table_current_stack[XSP - 1] or 3
612       end
613       Nx.met = nil
614       Nx.auto_xspc = (has_attr(x, attr_autoxspc)==1)
615    end
616    local set_np_xspc_alchar = set_np_xspc_alchar
617
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
622       if s then
623          if getid(s)==id_glyph then
624             if getfield(s, 'lang') == lang_ja then
625                set_np_xspc_jachar_hbox(Nx, s)
626             else
627                set_np_xspc_alchar(Nx, getchar(s), s, 2)
628             end
629          else
630             set_np_xspc_alchar(Nx, -1, s)
631          end
632       else
633          Nx.pre, Nx.met = nil, nil
634       end
635    end
636
637    after_alchar = function (Nx)
638       local x = Nx.last_char
639       return set_np_xspc_alchar(Nx, getchar(x), x, 2)
640    end
641
642 end
643
644 -------------------- 最下層の処理
645
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)
649    if #Bp == 0 then
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)
655          Bp[1]=p;
656          set_attr(p, attr_icflag, KINSOKU)
657       end
658    else for _, v in pairs(Bp) do add_penalty(v,a) end
659    end
660 end
661
662 local function handle_penalty_always(post, pre, g)
663    local a = (pre or 0) + (post or 0)
664    if #Bp == 0 then
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)
670          Bp[1]=p
671          set_attr(p, attr_icflag, KINSOKU)
672       end
673    else for _, v in pairs(Bp) do add_penalty(v,a) end
674    end
675 end
676
677 local function handle_penalty_suppress(post, pre, g)
678    local a = (pre or 0) + (post or 0)
679    if #Bp == 0 then
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)
683          Bp[1]=p
684          set_attr(p, attr_icflag, KINSOKU)
685       end
686    else for _, v in pairs(Bp) do add_penalty(v,a) end
687    end
688 end
689
690 -- 和文文字間の JFM glue を node 化
691 local function new_jfm_glue(mc, bc, ac)
692 -- bc, ac: char classes
693    local g = mc[bc][ac]
694    if g then
695       if g[1] then
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
700       else
701          return node_copy(g[2]), g.ratio, false, false, false
702       end
703    end
704    return false, 0
705 end
706
707 -- Nq.last (kern w) .... (glue/kern g) Np.first
708 local function real_insert(g)
709    if g then
710       head  = insert_before(head, Np.first, g)
711       Np.first = g
712    end
713 end
714
715
716 -------------------- 和文文字間空白量の決定
717 local calc_ja_ja_aux
718 do
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
723
724    local function blend_diffmet(b, a, rb, ra)
725       return round(diffmet_rule((1-rb)*b+rb*a, (1-ra)*b+ra*a))
726    end
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
730          db, da = 0, 1
731       end
732       if not gb then
733          if ga then
734             gb = node_new(id_kern); setfield(gb, 'kern', 0)
735          else return nil end
736       elseif not ga then
737          ga = node_new(id_kern); setfield(ga, 'kern', 0)
738       end
739
740       local k = 2*getid(gb) - getid(ga)
741       if k == bg_ag then
742          local bs, as = getfield(gb, 'spec'), getfield(ga, 'spec')
743          -- 両方とも glue.
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))
750          node_free(ga)
751          return gb
752       elseif k == bk_ak then
753          -- 両方とも kern.
754          setfield(gb, 'kern', blend_diffmet(
755                      getfield(gb, 'kern'), getfield(ga, 'kern'), db, da))
756          node_free(ga)
757          return gb
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))
767          node_free(gb)
768          return ga, 0, 0, 0
769       else
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))
778          node_free(ga)
779          return gb
780       end
781    end
782 end
783
784 local null_skip_table = {0, 0, 0}
785 -- get kanjiskip
786 local get_kanjiskip, kanjiskip_jfm_flag
787 local calc_ja_ja_glue
788 do
789    local KANJI_SKIP   = luatexja.icflag_table.KANJI_SKIP
790    local KANJI_SKIP_JFM   = luatexja.icflag_table.KANJI_SKIP_JFM
791
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)
804             return g
805          elseif flag then
806             return node_copy(kanji_skip)
807          else
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)
817             return g
818          end
819       end
820    end
821    
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)
827          else
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)
831          end
832       else
833          local g = node_copy(zero_glue)
834          set_attr(g, attr_icflag, kanjiskip_jfm_flag and KANJI_SKIP_JFM or KANJI_SKIP)
835          return g
836       end
837    end
838
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)
845       else
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,
850                                                 qm, getchar(npn)))
851          local ga, da, an, ap, ah 
852             = new_jfm_glue(pmc,
853                            slow_find_char_class(Nq.char,
854                                                 pm, getchar(nqn)),
855                                                  Np.class)
856          local g = calc_ja_ja_aux(gb, ga, db, da)
857          local k
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)
862          end
863          return g, k
864       end
865    end
866 end
867
868 -------------------- 和欧文間空白量の決定
869
870 -- get xkanjiskip
871 local get_xkanjiskip, xkanjiskip_jfm_flag
872 local get_xkanjiskip_normal, get_xkanjiskip_jfm
873 do
874    local XKANJI_SKIP   = luatexja.icflag_table.XKANJI_SKIP
875    local XKANJI_SKIP_JFM   = luatexja.icflag_table.XKANJI_SKIP_JFM
876
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)
889             return g
890          elseif flag then
891             return node_copy(xkanji_skip)
892          else
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)
902             return g
903          end
904       end
905    end
906    
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)
910       else
911          local g = node_copy(zero_glue)
912          set_attr(g, attr_icflag, xkanjiskip_jfm_flag and XKANJI_SKIP_JFM or XKANJI_SKIP)
913          return g
914       end
915    end
916 end
917
918 -------------------- 隣接した「塊」間の処理
919
920 local function get_OA_skip(is_kanji)
921    local pm = Np.met
922    local g, _, kn, kp, kh = new_jfm_glue(
923       pm.char_type,
924       fast_find_char_class((Nq.id == id_math and -1 or 'jcharbdd'), pm), 
925       Np.class)
926    local k
927    if is_kanji==0 then
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)
932    end
933    return g, k
934 end
935 local function get_OB_skip(is_kanji)
936    local qm = Nq.met
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))
940    local k
941    if is_kanji==0 then
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)
946    end
947    return g, k
948 end
949
950 -- (anything) .. jachar
951 local function handle_np_jachar(mode)
952    local qid = Nq.id
953    if qid==id_jglyph or ((qid==id_pbox or qid==id_pbox_w) and Nq.met) then
954       local g, k
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
960       local g, k
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)
964    elseif Nq.pre then
965       local g, 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)
970    else
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)
975       end
976       real_insert(g)
977    end
978    if mode and Np.kcat%2~=1 then
979       widow_Np.first, widow_Bp, Bp = Np.first, Bp, widow_Bp
980    end
981 end
982
983
984 -- jachar .. (anything)
985 local function handle_nq_jachar()
986     if Np.pre then
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)
989    else
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)
994       end
995       real_insert(g)
996    end
997 end
998
999 -- (anything) .. (和文文字で始まる hlist)
1000 local function handle_np_ja_hlist()
1001    local qid = Nq.id
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)
1008    elseif Nq.pre then
1009       local g = get_xkanjiskip(Np) -- X
1010       handle_penalty_suppress(0, 0, g); real_insert(g)
1011    end
1012 end
1013
1014 -- (和文文字で終わる hlist) .. (anything)
1015 local function handle_nq_ja_hlist()
1016    if Np.pre then
1017       local g = get_xkanjiskip(Nq) -- X
1018       handle_penalty_suppress(0, 0, g); real_insert(g)
1019    end
1020 end
1021
1022
1023 -- Nq が前側のクラスタとなることによる修正
1024 do
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",
1032                                                  false, Nq, Np)
1033                      end,
1034    }
1035
1036    function adjust_nq()
1037       local x = adjust_nq_aux[Nq.id]
1038       if x then x()  end
1039    end
1040 end
1041
1042
1043 -------------------- 開始・終了時の処理
1044 do
1045
1046 -- リスト末尾の処理
1047 local JWP  = luatexja.stack_table_index.JWP
1048 local function handle_list_tail(mode)
1049    adjust_nq(); Np = Nq
1050    if mode then
1051       -- the current list is to be line-breaked.
1052       -- Insert \jcharwidowpenalty
1053       Bp = widow_Bp; Np = widow_Np
1054       if Np.first then
1055          handle_penalty_normal(0, table_current_stack[JWP] or 0)
1056       end
1057    else
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))
1062          if g then
1063             set_attr(g, attr_icflag, BOXBDD)
1064             head = insert_after(head, Np.last, g)
1065          end
1066       end
1067    end
1068 end
1069
1070 -- リスト先頭の処理
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)
1076          if g then
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)
1081             end
1082             head = insert_before(head, Np.first, g)
1083          end
1084       end
1085    end
1086 end
1087
1088 -- initialize
1089 -- return value: (the initial cursor lp), (last node)
1090 local init_var
1091 do
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, },
1108    }
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
1115
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.
1123
1124       do
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)
1129       end
1130
1131       do
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)
1136       end
1137
1138       if mode then
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')
1147             end
1148             lp=node_next(lp); lpi, lps = getid(lp), getsubtype(lp) end
1149          return lp, node_tail(head), par_indented, TEMP
1150       else
1151          return head, nil, 'boxbdd', TEMP
1152       end
1153    end
1154 end
1155
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)
1162    
1163    if mode then
1164       local h = node_next(head)
1165       if getid(h) == id_penalty and getfield(h, 'penalty') == 10000 then
1166          h = node_next(h)
1167          if getid(h) == id_glue and getsubtype(h) == 15 and not node_next(h) then
1168             return false
1169          end
1170       end
1171    end
1172    return head
1173 end
1174 -------------------- 外部から呼ばれる関数
1175
1176 -- main interface
1177 function main(ahead, mode, dir)
1178    if not ahead then return ahead end
1179    head = ahead;
1180    local lp, last, par_indented, TEMP = init_var(mode,dir)
1181    lp = calc_np(last, lp)
1182    if Np then
1183       handle_list_head(par_indented)
1184       lp = calc_np(last,lp); while Np do
1185          adjust_nq();
1186          local pid, pm = Np.id, Np.met
1187          -- 挿入部
1188          if pid == id_jglyph then
1189             handle_np_jachar(mode)
1190          elseif pm then
1191             if pid==id_hlist then handle_np_ja_hlist()
1192             else handle_np_jachar() end
1193          elseif Nq.met then
1194             if Nq.id==id_hlist then handle_nq_ja_hlist()
1195             else handle_nq_jachar() end
1196          end
1197          lp = calc_np(last,lp)
1198       end
1199       handle_list_tail(mode)
1200    end
1201    return cleanup(mode, TEMP)
1202 end
1203 end
1204
1205 do
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
1210
1211    -- \inhibitglue
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)
1217       node_write(tn)
1218    end
1219
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)
1227       node_write(tn)
1228    end
1229
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
1234          return Np
1235       end
1236    end
1237
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
1242          if Np then
1243             if Np.met then
1244                Nq.class = fast_find_char_class('parbdd', Np.met)
1245             end
1246             Nq.met = Np.met; Nq.pre = 0; Nq.post = 0; Nq.xspc = 0
1247             Nq.auto_xspc = false
1248          end
1249          head = node_remove(head, y)
1250          node_free(y)
1251       end
1252       return s
1253    end
1254
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)
1259
1260 end