OSDN Git Service

luatexja-adjust now works again
[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       if getid(lp)==id_hlist or getid(lp)==id_vlist then
300          head, lp, nc = ltjd_make_dir_whatsit(head, lp, list_dir, 'jfm pbox')
301          Np.first = first and nc or Np.first
302       else
303          nc, lp = lp, node_next(lp)
304       end
305       first, lpa = false, (lp and has_attr(lp, attr_icflag) or 0)
306      -- get_attr_icflag() ではいけない!
307    end
308    Np.nuc = nc
309    lp = check_next_ickern(lp)
310    Np.last_char = check_box_high(Np, Np.first, lp)
311    return lp
312 end
313
314 local ltjw_apply_ashift_math = ltjw.apply_ashift_math
315 local ltjw_apply_ashift_disc = ltjw.apply_ashift_disc
316 local min, max = math.min, math.max
317 local function calc_np_aux_glyph_common(lp)
318    Np.nuc = lp
319    Np.first= (Np.first or lp)
320    if getfield(lp, 'lang') == lang_ja then
321       Np.id = id_jglyph
322       local m, cls = set_np_xspc_jachar(Np, lp)
323       local npi, npf
324       lp, head, npi, npf = capsule_glyph(lp, m, cls, head, tex_dir, lp)
325       Np.first = (Np.first~=Np.nuc) and Np.first or npf or npi
326       Np.nuc = npi
327       return true, check_next_ickern(lp);
328    else
329       Np.id = id_glyph
330       set_np_xspc_alchar(Np, getchar(lp), lp, 1)
331       -- loop
332       local first_glyph, last_glyph = lp
333       set_attr(lp, attr_icflag, PROCESSED); Np.last = lp
334       local y_adjust = has_attr(lp,attr_ablshift) or 0
335       local node_depth = getfield(lp, 'depth') + min(y_adjust, 0)
336       local adj_depth = (y_adjust>0) and (getfield(lp, 'depth') + y_adjust) or 0
337       setfield(lp, 'yoffset', getfield(lp, 'yoffset') - y_adjust)
338       lp = node_next(lp)
339       for lx in traverse(lp) do
340          local lai = get_attr_icflag(lx)
341          if lx==last or  lai>=PACKED then
342             lp=lx; break
343          else
344             local lid = getid(lx)
345             if lid==id_glyph and getfield(lx, 'lang') ~= lang_ja then
346                -- 欧文文字
347                last_glyph = lx; set_attr(lx, attr_icflag, PROCESSED); Np.last = lx
348                y_adjust = has_attr(lx,attr_ablshift) or 0
349                node_depth = max(getfield(lx, 'depth') + min(y_adjust, 0), node_depth)
350                adj_depth = (y_adjust>0) and max(getfield(lx, 'depth') + y_adjust, adj_depth) or adj_depth
351                setfield(lx, 'yoffset', getfield(lx, 'yoffset') - y_adjust)
352             elseif lid==id_kern then
353                local ls = getsubtype(lx)
354                if ls==2 then -- アクセント用の kern
355                   set_attr(lx, attr_icflag, PROCESSED)
356                   lx = node_next(lx) -- lx: アクセント本体
357                   setfield(lx, 'yoffset', getfield(lx, 'yoffset') - (has_attr(lx,attr_ablshift) or 0))
358                   lx = node_next(node_next(lx))
359                elseif ls==0  then
360                   Np.last = lx
361                elseif (ls==1 and lai==ITALIC) then
362                   Np.last = lx; set_attr(lx, attr_icflag, IC_PROCESSED)
363                else
364                   lp=lx; break
365                end
366             else
367                lp=lx; break
368             end
369          end
370       end
371       local r
372       if adj_depth>node_depth then
373             r = node_new(id_rule)
374             setfield(r, 'width', 0); setfield(r, 'height', 0)
375             setfield(r, 'depth',adj_depth); setfield(r, 'dir', tex_dir)
376             set_attr(r, attr_icflag, PROCESSED)
377       end
378       if last_glyph then
379          Np.last_char = last_glyph
380          if r then insert_after(head, first_glyph, r) end
381       else
382          local npn = Np.nuc
383          Np.last_char = npn
384          if r then
385             local nf, nc = getfont(npn), getchar(npn)
386             local ct = (font.getfont(nf) or font.fonts[nf] ).characters[nc]
387             if not ct then -- variation selector
388                node_free(r)
389             elseif (ct.left_protruding or 0) == 0 then
390                head = insert_before(head, npn, r)
391                Np.first = (Np.first==npn) and r or npn
392             elseif (ct.right_protruding or 0) == 0 then
393                insert_after(head, npn, r); Np.last, lp = r, r
394             else
395                ltjb.package_warning_no_line(
396                   'luatexja',
397                   'Check depth of glyph node ' .. tostring(npn) .. '(font=' .. nf
398                      .. ', char=' .. nc .. '),    because its \\lpcode is ' .. tostring(ct.left_protruding)
399                      .. ' and its \\rpcode is ' .. tostring(ct.right_protruding)
400                ); node_free(r)
401             end
402          end
403       end
404       return true, lp
405    end
406 end
407 local calc_np_auxtable = {
408    [id_glyph] = calc_np_aux_glyph_common,
409    [id_hlist] = function(lp)
410       local op, flag
411       head, lp, op, flag = ltjd_make_dir_whatsit(head, lp, list_dir, 'jfm hlist')
412       set_attr(op, attr_icflag, PROCESSED)
413       Np.first = Np.first or op; Np.last = op; Np.nuc = op;
414       if (flag or getfield(op, 'shift')~=0) then
415          Np.id = id_box_like
416       else
417          Np.id = id_hlist
418          Np.last_char = check_box_high(Np, getlist(op), nil)
419       end
420       return true, lp
421    end,
422    [id_vlist] =  function(lp)
423       local op
424       head, lp, op = ltjd_make_dir_whatsit(head, lp, list_dir, 'jfm:' .. getid(lp))
425       Np.first = Np.first or op; Np.last = op; Np.nuc = op;
426       Np.id = id_box_like;
427       return true, lp
428    end,
429    box_like = function(lp)
430       Np.first = Np.first or lp; Np.last = lp; Np.nuc = lp;
431       Np.id = id_box_like;
432       return true, node_next(lp)
433    end,
434    skip = function(lp)
435       set_attr(lp, attr_icflag, PROCESSED)
436       return false, node_next(lp)
437    end,
438    [id_whatsit] = function(lp)
439       local lps = getsubtype(lp)
440       if lps==sid_user then
441          if getfield(lp, 'user_id')==luatexja.userid_table.IHB then
442             local lq = node_next(lp);
443             head = node_remove(head, lp); node_free(lp); non_ihb_flag = false
444             return false, lq;
445          else
446             set_attr(lp, attr_icflag, PROCESSED)
447             luatexbase.call_callback("luatexja.jfmglue.whatsit_getinfo",
448                                      Np, lp, Nq)
449             if Np.nuc then
450                Np.id = id_pbox_w; Np.first = Np.nuc; Np.last = Np.nuc;
451                return true, node_next(lp)
452             else
453                return false, node_next(lp)
454             end
455          end
456       else
457          -- we do special treatment for these whatsit nodes.
458          if lps == sid_start_link or lps == sid_start_thread then
459             Np.first = lp
460          elseif lps == sid_end_link or lps == sid_end_thread then
461             Np.first, Nq.last = nil, lp;
462          end
463          set_attr(lp, attr_icflag, PROCESSED)
464          return false, node_next(lp)
465       end
466    end,
467    [id_math] = function(lp)
468       Np.first, Np.nuc = (Np.first or lp), lp;
469       set_attr(lp, attr_icflag, PROCESSED)
470       set_np_xspc_alchar(Np, -1, lp)
471       local end_math  = node_end_of_math(lp)
472       ltjw_apply_ashift_math(lp, end_math, attr_ablshift)
473       set_attr(end_math, attr_icflag, PROCESSED)
474       Np.last, Np.id = end_math, id_math;
475       return true, node_next(end_math);
476    end,
477    [id_glue] = function(lp)
478       Np.first, Np.nuc, Np.last = (Np.first or lp), lp, lp;
479       Np.id = getid(lp); set_attr(lp, attr_icflag, PROCESSED)
480       return true, node_next(lp)
481    end,
482    [id_disc] = function(lp)
483       Np.first, Np.nuc, Np.last = (Np.first or lp), lp, lp;
484       Np.id = getid(lp); set_attr(lp, attr_icflag, PROCESSED)
485       ltjw_apply_ashift_disc(lp, (list_dir==dir_tate), tex_dir)
486       Np.last_char = check_box_high(Np, getfield(lp, 'replace'), nil)
487       return true, node_next(lp)
488    end,
489    [id_kern] = function(lp)
490       if getsubtype(lp)==2 then
491          Np.first = Np.first or lp
492          set_attr(lp, attr_icflag, PROCESSED); lp = node_next(lp)
493          set_attr(lp, attr_icflag, PROCESSED); lp = node_next(lp)
494          set_attr(lp, attr_icflag, PROCESSED); lp = node_next(lp)
495          set_attr(lp, attr_icflag, PROCESSED);
496          return calc_np_aux_glyph_common(lp)
497       else
498          Np.first = Np.first or lp
499          Np.id = id_kern; set_attr(lp, attr_icflag, PROCESSED)
500          Np.last = lp; return true, node_next(lp)
501       end
502    end,
503    [id_penalty] = function(lp)
504       Bp[#Bp+1] = lp; set_attr(lp, attr_icflag, PROCESSED)
505       return false, node_next(lp)
506    end,
507 }
508 calc_np_auxtable[id_rule]   = calc_np_auxtable.box_like
509 calc_np_auxtable[13]        = calc_np_auxtable.box_like
510 calc_np_auxtable[id_ins]    = calc_np_auxtable.skip
511 calc_np_auxtable[id_mark]   = calc_np_auxtable.skip
512 calc_np_auxtable[id_adjust] = calc_np_auxtable.skip
513
514 function calc_np(last, lp)
515    local k
516    -- We assume lp = node_next(Np.last)
517    Np, Nq, non_ihb_flag = Nq, Np, true
518    -- We clear `predefined' entries of Np before pairs() loop,
519    -- because using only pairs() loop is slower.
520    Np.post, Np.pre, Np.xspc = nil, nil, nil
521    Np.first, Np.id, Np.last, Np.met, Np.class= nil, nil, nil, nil
522    Np.auto_kspc, Np.auto_xspc, Np.char, Np.nuc = nil, nil, nil, nil
523    for k in pairs(Np) do Np[k] = nil end
524
525    for k = 1,#Bp do Bp[k] = nil end
526    while lp ~= last  do
527       local lpa = has_attr(lp, attr_icflag) or 0
528        -- unbox 由来ノードの検出
529       if lpa>=PACKED then
530          if lpa%PROCESSED_BEGIN_FLAG == BOXBDD then
531             local lq = node_next(lp)
532             head = node_remove(head, lp); node_free(lp); lp = lq
533          else
534             return calc_np_pbox(lp, last)
535          end -- id_pbox
536       else
537          k, lp = calc_np_auxtable[getid(lp)](lp)
538          if k then return lp end
539       end
540    end
541    Np=nil
542 end
543 end
544
545 -- extract informations from Np
546 -- We think that "Np is a Japanese character" if Np.met~=nil,
547 --            "Np is an alphabetic character" if Np.pre~=nil,
548 --            "Np is not a character" otherwise.
549 after_hlist = nil -- global
550 local after_alchar, extract_np
551 do
552   local PRE  = luatexja.stack_table_index.PRE
553   local POST = luatexja.stack_table_index.POST
554   local KCAT = luatexja.stack_table_index.KCAT
555   local XSP  = luatexja.stack_table_index.XSP
556   local dir_tate = luatexja.dir_table.dir_tate
557
558 -- 和文文字のデータを取得
559    local attr_jchar_class = luatexbase.attributes['ltj@charclass']
560    local attr_jchar_code = luatexbase.attributes['ltj@charcode']
561    local attr_autospc = luatexbase.attributes['ltj@autospc']
562    local attr_autoxspc = luatexbase.attributes['ltj@autoxspc']
563    --local ltjf_get_vert_glyph = ltjf.get_vert_glyph
564    function set_np_xspc_jachar(Nx, x)
565       local m = ltjf_font_metric_table[getfont(x)]
566       local c, c_glyph = ltjs_orig_char_table[x], getchar(x)
567       c = c or c_glyph
568       local cls = slow_find_char_class(c, m, c_glyph)
569       Nx.met, Nx.class, Nx.char = m, cls, c;
570       if cls~=0 then set_attr(x, attr_jchar_class, cls) end
571       if c~=c_glyph then set_attr(x, attr_jchar_code, c) end
572       Nx.pre  = table_current_stack[PRE + c]  or 0
573       Nx.post = table_current_stack[POST + c] or 0
574       Nx.xspc = table_current_stack[XSP  + c] or 3
575       Nx.kcat = table_current_stack[KCAT + c] or 0
576       Nx.auto_kspc, Nx.auto_xspc = (has_attr(x, attr_autospc)==1), (has_attr(x, attr_autoxspc)==1)
577       return m, cls
578    end
579    function set_np_xspc_jachar_hbox(Nx, x)
580       local m = ltjf_font_metric_table[getfont(x)]
581       local c = has_attr(x, attr_jchar_code) or getchar(x)
582       Nx.met, Nx.char  = m, c; Nx.class = has_attr(x, attr_jchar_class) or 0;
583       Nx.pre  = table_current_stack[PRE + c]  or 0
584       Nx.post = table_current_stack[POST + c] or 0
585       Nx.xspc = table_current_stack[XSP  + c] or 3
586       Nx.kcat = table_current_stack[KCAT + c] or 0
587       Nx.auto_kspc, Nx.auto_xspc = (has_attr(x, attr_autospc)==1), (has_attr(x, attr_autoxspc)==1)
588    end
589
590 -- 欧文文字のデータを取得
591    local floor = math.floor
592    function set_np_xspc_alchar(Nx, c,x, lig)
593       if c~=-1 then
594          local f = (lig ==1) and nullfunc or node_tail
595          local xc, xs = getfield(x, 'components'), getsubtype(x)
596          while xc and xs and xs%4>=2 do
597             x = f(xc); xc, xs = getfield(x, 'components'), getsubtype(x)
598          end
599          c = getchar(x)
600          Nx.pre  = table_current_stack[PRE + c]  or 0
601          Nx.post = table_current_stack[POST + c] or 0
602          Nx.xspc = table_current_stack[XSP  + c] or 3
603       else
604          Nx.pre, Nx.post = 0, 0
605          Nx.xspc = table_current_stack[XSP - 1] or 3
606       end
607       Nx.met = nil
608       Nx.auto_xspc = (has_attr(x, attr_autoxspc)==1)
609    end
610    local set_np_xspc_alchar = set_np_xspc_alchar
611
612    -- change the information for the next loop
613    -- (will be done if Nx is an alphabetic character or a hlist)
614    after_hlist = function (Nx)
615       local s = Nx.last_char
616       if s then
617          if getid(s)==id_glyph then
618             if getfield(s, 'lang') == lang_ja then
619                set_np_xspc_jachar_hbox(Nx, s)
620             else
621                set_np_xspc_alchar(Nx, getchar(s), s, 2)
622             end
623          else
624             set_np_xspc_alchar(Nx, -1, s)
625          end
626       else
627          Nx.pre, Nx.met = nil, nil
628       end
629    end
630
631    after_alchar = function (Nx)
632       local x = Nx.last_char
633       return set_np_xspc_alchar(Nx, getchar(x), x, 2)
634    end
635
636 end
637
638 -------------------- 最下層の処理
639
640 -- change penalties (or create a new penalty, if needed)
641 local function handle_penalty_normal(post, pre, g)
642    local a = (pre or 0) + (post or 0)
643    if #Bp == 0 then
644       if (a~=0 and not(g and getid(g)==id_kern)) then
645          local p = node_new(id_penalty)
646          if a<-10000 then a = -10000 elseif a>10000 then a = 10000 end
647          setfield(p, 'penalty', a)
648          head = insert_before(head, Np.first, p)
649          Bp[1]=p;
650          set_attr(p, attr_icflag, KINSOKU)
651       end
652    else for _, v in pairs(Bp) do add_penalty(v,a) end
653    end
654 end
655
656 local function handle_penalty_always(post, pre, g)
657    local a = (pre or 0) + (post or 0)
658    if #Bp == 0 then
659       if not (g and getid(g)==id_glue) or a~=0 then
660          local p = node_new(id_penalty)
661          if a<-10000 then a = -10000 elseif a>10000 then a = 10000 end
662          setfield(p, 'penalty', a)
663          head = insert_before(head, Np.first, p)
664          Bp[1]=p
665          set_attr(p, attr_icflag, KINSOKU)
666       end
667    else for _, v in pairs(Bp) do add_penalty(v,a) end
668    end
669 end
670
671 local function handle_penalty_suppress(post, pre, g)
672    local a = (pre or 0) + (post or 0)
673    if #Bp == 0 then
674       if g and getid(g)==id_glue then
675          local p = node_new(id_penalty)
676          setfield(p, 'penalty', 10000); head = insert_before(head, Np.first, p)
677          Bp[1]=p
678          set_attr(p, attr_icflag, KINSOKU)
679       end
680    else for _, v in pairs(Bp) do add_penalty(v,a) end
681    end
682 end
683
684 -- 和文文字間の JFM glue を node 化
685 local function new_jfm_glue(m, bc, ac, ks)
686 -- bc, ac: char classes
687    local g = m.char_type[bc][ac]
688    if g then
689       if g[1] then
690          local f = node_new(id_glue)
691          set_attr(f, attr_icflag, g.priority)
692          local fs = node_copy(g[2])
693          setfield(f, 'spec', node_copy(g[2]))
694          return f, g.ratio, g.ksp_natural, g.ksp_stretch, g.ksp_shrink
695       else
696          return node_copy(g[2]), g.ratio, false, false, false
697       end
698    end
699    return nil, 0
700 end
701
702 -- Nq.last (kern w) .... (glue/kern g) Np.first
703 local function real_insert(g)
704    if g then
705       head  = insert_before(head, Np.first, g)
706       Np.first = g
707    end
708 end
709
710
711 -------------------- 和文文字間空白量の決定
712 local function blend_diffmet(b, a, rb, ra)
713    return round(diffmet_rule((1-rb)*b+rb*a, (1-ra)*b+ra*a))
714 end
715
716 local null_skip_table = {0, 0, 0}
717 -- get kanjiskip
718 local get_kanjiskip
719 local get_kanjiskip_normal, get_kanjiskip_jfm
720 do
721    local KANJI_SKIP   = luatexja.icflag_table.KANJI_SKIP
722    local KANJI_SKIP_JFM   = luatexja.icflag_table.KANJI_SKIP_JFM
723    get_kanjiskip_normal = function (flag, bn, bp, bh)
724       if Np.auto_kspc or Nq.auto_kspc then
725          if flag then
726             return node_copy(kanji_skip)
727          elseif bn or bp or bh then
728             local g = node_new(id_glue);
729             local gx = node_copy(getfield(kanji_skip, 'spec'))
730             setfield(g, 'spec', gx)
731             setfield(gx, 'width', bn and getfield(gx, 'width') or 0)
732             setfield(gx, 'stretch', bp and getfield(gx, 'stretch') or 0)
733             setfield(gx, 'shrink', bh and getfield(gx, 'shrink') or 0)
734             if (getfield(gx, 'width')==0) and (getfield(gx, 'stretch')==0)
735                and (getfield(gx, 'shrink')==0) then
736                   node_free(g)
737             else
738                set_attr(g, attr_icflag, KANJI_SKIP)
739                return g
740             end
741          end
742       elseif flag then
743          local g = node_copy(zero_glue)
744          set_attr(g, attr_icflag, KANJI_SKIP)
745          return g
746       end
747    end
748
749    get_kanjiskip_jfm = function (flag, bn, bp, bh, an, ap, ah)
750       if Np.auto_kspc or Nq.auto_kspc then
751          local g = node_new(id_glue);
752          local gx = node_new(id_glue_spec);
753          setfield(gx, 'stretch_order', 0); setfield(gx, 'shrink_order', 0)
754          local pm, qm = Np.met, Nq.met
755          local bk = qm.kanjiskip or null_skip_table
756          if (pm.char_type==qm.char_type) and (qm.var==pm.var) then
757             setfield(gx, 'width', bn and bk[1] or 0)
758             setfield(gx, 'stretch', bp and bk[2] or 0)
759             setfield(gx, 'shrink', bh and bk[3] or 0)
760          else
761             local ak = pm.kanjiskip or null_skip_table
762             setfield(gx, 'width', blend_diffmet(bn and bk[1] or 0, an and ak[1] or 0, 0, 1))
763             setfield(gx, 'stretch', blend_diffmet(bp and bk[2] or 0, ap and ak[2] or 0, 0, 1))
764             setfield(gx, 'shrink', -blend_diffmet(bh and -bk[3] or 0, ah and -ak[3]or 0, 0, 1))
765          end
766          setfield(g, 'spec', gx)
767          if (not flag) and (getfield(gx, 'width')==0) and (getfield(gx, 'stretch')==0)
768          and (getfield(gx, 'shrink')==0) then
769             node_free(g)
770          else
771             set_attr(g, attr_icflag, KANJI_SKIP_JFM)
772             return g
773          end
774       elseif flag then
775          local g =  node_copy(zero_glue)
776          set_attr(g, attr_icflag, KANJI_SKIP_JFM)
777          return g
778       end
779    end
780 end
781
782 local calc_ja_ja_aux
783 do
784    local bg_ag = 2*id_glue - id_glue
785    local bg_ak = 2*id_glue - id_kern
786    local bk_ag = 2*id_kern - id_glue
787    local bk_ak = 2*id_kern - id_kern
788
789    calc_ja_ja_aux = function (gb, ga, db, da, 
790                              bn, bp, bh, an, ap, ah)
791       if diffmet_rule ~= math.two_pleft and diffmet_rule ~= math.two_pright
792           and diffmet_rule ~= math.two_paverage then
793          db, da = 0, 1
794       end
795       if not gb then
796          if ga then
797             gb = node_new(id_kern); setfield(gb, 'kern', 0)
798          else return nil end
799       elseif not ga then
800          ga = node_new(id_kern); setfield(ga, 'kern', 0)
801       end
802
803       local k = 2*getid(gb) - getid(ga)
804       if k == bg_ag then
805          local bs, as = getfield(gb, 'spec'), getfield(ga, 'spec')
806          -- 両方とも glue.
807          setfield(bs, 'width', blend_diffmet(
808                      getfield(bs, 'width'), getfield(as, 'width'), db, da))
809          setfield(bs, 'stretch', blend_diffmet(
810                      getfield(bs, 'stretch'), getfield(as, 'stretch'), db, da))
811          setfield(bs, 'shrink', -blend_diffmet(
812                      -getfield(bs, 'shrink'), -getfield(as, 'shrink'), db, da))
813          node_free(ga)
814          return gb
815       elseif k == bk_ak then
816          -- 両方とも kern.
817          setfield(gb, 'kern', blend_diffmet(
818                      getfield(gb, 'kern'), getfield(ga, 'kern'), db, da))
819          node_free(ga)
820          return gb
821       elseif k == bk_ag then
822          local as = getfield(ga, 'spec')
823          -- gb: kern, ga: glue
824          setfield(as, 'width', blend_diffmet(
825                      getfield(gb, 'kern'), getfield(as, 'width'), db, da))
826          setfield(as, 'stretch', blend_diffmet(
827                      0, getfield(as, 'stretch'), db, da))
828          setfield(as, 'shrink', -blend_diffmet(
829                      0, -getfield(as, 'shrink'), db, da))
830          node_free(gb)
831          return ga, 0, 0, 0
832       else
833          local bs = getfield(gb, 'spec')
834          -- gb: glue, ga: kern
835          setfield(bs, 'width', blend_diffmet(
836                      getfield(bs, 'width'), getfield(ga, 'kern'), db, da))
837          setfield(bs, 'stretch', blend_diffmet(
838                      getfield(bs, 'stretch'), 0, db, da))
839          setfield(bs, 'shrink', -blend_diffmet(
840                      -getfield(bs, 'shrink'), 0, db, da))
841          node_free(ga)
842          return gb
843       end
844    end
845 end
846
847 local function calc_ja_ja_glue()
848    local qm, pm = Nq.met, Np.met
849    if (qm.char_type==pm.char_type) and (qm.var==pm.var) then
850       local g, _, kn, kp, kh = new_jfm_glue(qm, Nq.class, Np.class, ks)
851       local k = get_kanjiskip(false, kn, kp, kh)
852       return g, k
853    else
854       local npn, nqn = Np.nuc, Nq.nuc
855       local gb, db, bn, bp, bh = new_jfm_glue(qm, Nq.class,
856                                   slow_find_char_class(Np.char,
857                                                        qm, getchar(npn)), ks)
858       local ga, da, an, ap, ah = new_jfm_glue(pm,
859                                   slow_find_char_class(Nq.char,
860                                                        pm, getchar(nqn)),
861                                   Np.class, ks)
862       local g = calc_ja_ja_aux(gb, ga, db, da, bn, bp, bh, an, ap, ah);
863       local k = get_kanjiskip(false, bn, bp, bh, an, ap, ah)
864       return g, k
865    end
866 end
867
868 -------------------- 和欧文間空白量の決定
869
870 -- get xkanjiskip
871 local get_xkanjiskip
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    get_xkanjiskip_normal = function ()
877       if (Nq.xspc>=2) and (Np.xspc%2==1) and (Nq.auto_xspc or Np.auto_xspc) then
878          return node_copy(xkanji_skip)
879       else
880          local g = node_copy(zero_glue)
881          set_attr(g, attr_icflag, XKANJI_SKIP)
882          return g
883       end
884    end
885    get_xkanjiskip_jfm = function (Nn)
886       local g
887       if (Nq.xspc>=2) and (Np.xspc%2==1) and (Nq.auto_xspc or Np.auto_xspc) then
888          g = node_new(id_glue)
889          local gx = node_new(id_glue_spec);
890          setfield(gx, 'stretch_order', 0); setfield(gx, 'shrink_order', 0)
891          local bk = Nn.met.xkanjiskip or null_skip_table
892          setfield(gx, 'width', bk[1])
893          setfield(gx, 'stretch', bk[2])
894          setfield(gx, 'shrink', bk[3])
895          setfield(g, 'spec', gx)
896       else
897          g = node_copy(zero_glue)
898       end
899       set_attr(g, attr_icflag, XKANJI_SKIP_JFM)
900       return g
901    end
902 end
903
904 -------------------- 隣接した「塊」間の処理
905
906 local function get_OA_skip()
907    local pm = Np.met
908    local g, _, kn, kp, kh = new_jfm_glue(
909       pm,
910       fast_find_char_class((Nq.id == id_math and -1 or 'jcharbdd'), pm), 
911       Np.class, getfield(xkanji_skip,'spec'))
912    return g
913 end
914 local function get_OB_skip()
915    local qm = Nq.met
916    return new_jfm_glue(qm, Nq.class,
917                        fast_find_char_class((Np.id == id_math and -1 or'jcharbdd'), qm), 
918                        getfield(xkanji_skip,'spec'))
919 end
920
921 -- (anything) .. jachar
922 local function handle_np_jachar(mode)
923    local qid = Nq.id
924    if qid==id_jglyph or ((qid==id_pbox or qid==id_pbox_w) and Nq.met) then
925       local g, k
926       if non_ihb_flag then g, k = calc_ja_ja_glue() end -- M->K
927       if not g then g = get_kanjiskip(true, true, true, true) end
928       handle_penalty_normal(Nq.post, Np.pre, g); 
929       real_insert(g); real_insert(k)
930    elseif Nq.met then  -- qid==id_hlist
931       local g = non_ihb_flag and get_OA_skip(get_kanjiskip) or get_kanjiskip(true, true, true, true) -- O_A->K
932       handle_penalty_normal(0, Np.pre, g); real_insert(g)
933    elseif Nq.pre then
934       local g = non_ihb_flag and get_OA_skip(get_kanjiskip) or get_xkanjiskip(Np) -- O_A->X
935       handle_penalty_normal((qid==id_hlist and 0 or Nq.post), Np.pre, g); real_insert(g)
936    else
937       local g = non_ihb_flag and get_OA_skip() -- O_A
938       if qid==id_glue then handle_penalty_normal(0, Np.pre, g)
939       elseif qid==id_kern then handle_penalty_suppress(0, Np.pre, g)
940       else handle_penalty_always(0, Np.pre, g)
941       end
942       real_insert(g)
943    end
944    if mode and Np.kcat%2~=1 then
945       widow_Np.first, widow_Bp, Bp = Np.first, Bp, widow_Bp
946    end
947 end
948
949
950 -- jachar .. (anything)
951 local function handle_nq_jachar()
952     if Np.pre then
953       local g = non_ihb_flag and get_OB_skip() or get_xkanjiskip(Nq) -- O_B->X
954       handle_penalty_normal(Nq.post, (Np.id==id_hlist and 0 or Np.pre), g); real_insert(g)
955    else
956       local g =non_ihb_flag and  get_OB_skip() -- O_B
957       if Np.id==id_glue then handle_penalty_normal(Nq.post, 0, g)
958       elseif Np.id==id_kern then handle_penalty_suppress(Nq.post, 0, g)
959       else handle_penalty_always(Nq.post, 0, g)
960       end
961       real_insert(g)
962    end
963 end
964
965 -- (anything) .. (和文文字で始まる hlist)
966 local function handle_np_ja_hlist()
967    local qid = Nq.id
968    if qid==id_jglyph or ((qid==id_pbox or Nq.id == id_pbox_w) and Nq.met) then
969       local g = non_ihb_flag and get_OB_skip() or get_kanjiskip(true, true, true, true) -- O_B->K
970       handle_penalty_normal(Nq.post, 0, g); real_insert(g)
971    elseif Nq.met then  -- Nq.id==id_hlist
972       local g = get_kanjiskip(true, true, true, true) -- K
973       handle_penalty_suppress(0, 0, g); real_insert(g)
974    elseif Nq.pre then
975       local g = get_xkanjiskip(Np) -- X
976       handle_penalty_suppress(0, 0, g); real_insert(g)
977    end
978 end
979
980 -- (和文文字で終わる hlist) .. (anything)
981 local function handle_nq_ja_hlist()
982    if Np.pre then
983       local g = get_xkanjiskip(Nq) -- X
984       handle_penalty_suppress(0, 0, g); real_insert(g)
985    end
986 end
987
988
989 -- Nq が前側のクラスタとなることによる修正
990 do
991    local adjust_nq_aux = {
992       [id_glyph] = function() after_alchar(Nq) end, -- after_alchar(Nq)
993       [id_hlist]  = function() after_hlist(Nq) end,
994       [id_pbox]  = function() after_hlist(Nq) end,
995       [id_disc]  = function() after_hlist(Nq) end,
996       [id_pbox_w]  = function()
997                         luatexbase.call_callback("luatexja.jfmglue.whatsit_after",
998                                                  false, Nq, Np)
999                      end,
1000    }
1001
1002    function adjust_nq()
1003       local x = adjust_nq_aux[Nq.id]
1004       if x then x()  end
1005    end
1006 end
1007
1008
1009 -------------------- 開始・終了時の処理
1010 do
1011
1012 -- リスト末尾の処理
1013 local JWP  = luatexja.stack_table_index.JWP
1014 local function handle_list_tail(mode)
1015    adjust_nq(); Np = Nq
1016    if mode then
1017       -- the current list is to be line-breaked.
1018       -- Insert \jcharwidowpenalty
1019       Bp = widow_Bp; Np = widow_Np
1020       if Np.first then
1021          handle_penalty_normal(0, table_current_stack[JWP] or 0)
1022       end
1023    else
1024       -- the current list is the contents of a hbox
1025       local npi, pm = Np.id, Np.met
1026       if npi == id_jglyph or (npi==id_pbox and pm) then
1027          local g = new_jfm_glue(pm, Np.class, fast_find_char_class('boxbdd', pm))
1028          if g then
1029             set_attr(g, attr_icflag, BOXBDD)
1030             head = insert_after(head, Np.last, g)
1031          end
1032       end
1033    end
1034 end
1035
1036 -- リスト先頭の処理
1037 local function handle_list_head(par_indented)
1038    local npi, pm = Np.id, Np.met
1039    if npi ==  id_jglyph or (npi==id_pbox and pm) then
1040       if non_ihb_flag then
1041          local g = new_jfm_glue(pm, fast_find_char_class(par_indented, pm), Np.class)
1042          if g then
1043             set_attr(g, attr_icflag, BOXBDD)
1044             if getid(g)==id_glue and #Bp==0 then
1045                local h = node_new(id_penalty)
1046                setfield(h, 'penalty', 10000); set_attr(h, attr_icflag, BOXBDD)
1047             end
1048             head = insert_before(head, Np.first, g)
1049          end
1050       end
1051    end
1052 end
1053
1054 -- initialize
1055 -- return value: (the initial cursor lp), (last node)
1056 local init_var
1057 do
1058    local KANJI_SKIP   = luatexja.icflag_table.KANJI_SKIP
1059    local XKANJI_SKIP   = luatexja.icflag_table.XKANJI_SKIP
1060    local KSK  = luatexja.stack_table_index.KSK
1061    local XSK  = luatexja.stack_table_index.XSK
1062    local dir_yoko = luatexja.dir_table.dir_yoko
1063    local dir_tate = luatexja.dir_table.dir_tate
1064    local attr_yablshift = luatexbase.attributes['ltj@yablshift']
1065    local attr_tablshift = luatexbase.attributes['ltj@tablshift']
1066    local table_pool = {
1067       {}, {}, {first=nil},
1068       { auto_kspc=nil, auto_xspc=nil, char=nil, class=nil,
1069         first=nil, id=nil, last=nil, met=nil, nuc=nil,
1070         post=nil, pre=nil, xspc=nil, }, 
1071       { auto_kspc=nil, auto_xspc=nil, char=nil, class=nil,
1072         first=nil, id=nil, last=nil, met=nil, nuc=nil,
1073         post=nil, pre=nil, xspc=nil, },
1074    }
1075    init_var = function (mode,dir)
1076       -- 1073741823: max_dimen
1077       Bp, widow_Bp, widow_Np, Np, Nq
1078          = table_pool[1], table_pool[2], table_pool[3], table_pool[4], table_pool[5]
1079       for i=1,5 do for j,_ in pairs(table_pool[i]) do table_pool[i][j]=nil end end
1080       table_current_stack = ltjs.table_current_stack
1081
1082       list_dir, tex_dir = (ltjs.list_dir or dir_yoko), (dir or 'TLT')
1083       local is_dir_tate = list_dir==dir_tate
1084       capsule_glyph = is_dir_tate and ltjw.capsule_glyph_tate or ltjw.capsule_glyph_yoko
1085       attr_ablshift = is_dir_tate and attr_tablshift or attr_yablshift
1086       local TEMP = node_new(id_glue) 
1087       -- TEMP is a dummy node, which will be freed at the end of the callback. 
1088       -- ithout this node, set_attr(kanji_skip, ...) somehow creates an "orphaned"  attribute list.
1089
1090       do
1091          kanji_skip = node_new(id_glue); set_attr(kanji_skip, attr_icflag, KANJI_SKIP)
1092          local s = skip_table_to_spec(KSK)
1093          setfield(kanji_skip, 'spec', s)
1094          get_kanjiskip = (getfield(kanji_skip, 'width') == 1073741823)
1095             and get_kanjiskip_jfm or get_kanjiskip_normal
1096       end
1097
1098       do
1099          xkanji_skip = node_new(id_glue); set_attr(xkanji_skip, attr_icflag, XKANJI_SKIP)
1100          local s = skip_table_to_spec(XSK)
1101          setfield(xkanji_skip, 'spec', s)
1102          get_xkanjiskip = (getfield(s, 'width') == 1073741823)
1103             and get_xkanjiskip_jfm or get_xkanjiskip_normal
1104       end
1105
1106       if mode then
1107          -- the current list is to be line-breaked:
1108          -- hbox from \parindent is skipped.
1109          local lp, par_indented, lpi, lps  = head, 'boxbdd', getid(head), getsubtype(head)
1110          while lp and ((lpi==id_whatsit and lps~=sid_user)
1111                        or ((lpi==id_hlist) and (lps==3))) do
1112             if (lpi==id_hlist) and (lps==3) then
1113                Np.char, par_indented = 'parbdd', 'parbdd'
1114                Np.width = getfield(lp, 'width')
1115             end
1116             lp=node_next(lp); lpi, lps = getid(lp), getsubtype(lp) end
1117          return lp, node_tail(head), par_indented, TEMP
1118       else
1119          return head, nil, 'boxbdd', TEMP
1120       end
1121    end
1122 end
1123
1124 local ensure_tex_attr = ltjb.ensure_tex_attr
1125 local function cleanup(mode, TEMP)
1126    -- adjust attr_icflag for avoiding error
1127    if tex.getattribute(attr_icflag)~=0 then ensure_tex_attr(attr_icflag, 0) end
1128    node_free(kanji_skip); 
1129    node_free(xkanji_skip); node_free(TEMP)
1130    
1131    if mode then
1132       local h = node_next(head)
1133       if getid(h) == id_penalty and getfield(h, 'penalty') == 10000 then
1134          h = node_next(h)
1135          if getid(h) == id_glue and getsubtype(h) == 15 and not node_next(h) then
1136             return false
1137          end
1138       end
1139    end
1140    return head
1141 end
1142 -------------------- 外部から呼ばれる関数
1143
1144 -- main interface
1145 function main(ahead, mode, dir)
1146    if not ahead then return ahead end
1147    head = ahead;
1148    local lp, last, par_indented, TEMP = init_var(mode,dir)
1149    lp = calc_np(last, lp)
1150    if Np then
1151       handle_list_head(par_indented)
1152       lp = calc_np(last,lp); while Np do
1153          adjust_nq();
1154          local pid, pm = Np.id, Np.met
1155          -- 挿入部
1156          if pid == id_jglyph then
1157             handle_np_jachar(mode)
1158          elseif pm then
1159             if pid==id_hlist then handle_np_ja_hlist()
1160             else handle_np_jachar() end
1161          elseif Nq.met then
1162             if Nq.id==id_hlist then handle_nq_ja_hlist()
1163             else handle_nq_jachar() end
1164          end
1165          lp = calc_np(last,lp)
1166       end
1167       handle_list_tail(mode)
1168    end
1169    return cleanup(mode, TEMP)
1170 end
1171 end
1172
1173 do
1174    local IHB  = luatexja.userid_table.IHB
1175    local BPAR = luatexja.userid_table.BPAR
1176    local node_prev = (Dnode ~= node) and Dnode.getprev or node.prev
1177    local node_write = Dnode.write
1178
1179    -- \inhibitglue
1180    function create_inhibitglue_node()
1181       local tn = node_new(id_whatsit, sid_user)
1182       setfield(tn, 'user_id', IHB)
1183       setfield(tn, 'type', 100)
1184       setfield(tn, 'value', 1)
1185       node_write(tn)
1186    end
1187
1188    -- Node for indicating beginning of a paragraph
1189    -- (for ltjsclasses)
1190    function create_beginpar_node()
1191       local tn = node_new(id_whatsit, sid_user)
1192       setfield(tn, 'user_id', BPAR)
1193       setfield(tn, 'type', 100)
1194       setfield(tn, 'value', 1)
1195       node_write(tn)
1196    end
1197
1198    local function whatsit_callback(Np, lp, Nq)
1199       if Np and Np.nuc then return Np
1200       elseif Np and getfield(lp, 'user_id') == BPAR then
1201          Np.first = lp; Np.nuc = lp; Np.last = lp
1202          return Np
1203       end
1204    end
1205
1206     local function whatsit_after_callback(s, Nq, Np)
1207        if not s and getfield(Nq.nuc, 'user_id') == BPAR then
1208          local x, y = node_prev(Nq.nuc), Nq.nuc
1209          Nq.first, Nq.nuc, Nq.last = x, x, x
1210          if Np then
1211             if Np.met then
1212                Nq.class = fast_find_char_class('parbdd', Np.met)
1213             end
1214             Nq.met = Np.met; Nq.pre = 0; Nq.post = 0; Nq.xspc = 0
1215             Nq.auto_xspc = false
1216          end
1217          head = node_remove(head, y)
1218          node_free(y)
1219       end
1220       return s
1221    end
1222
1223    luatexbase.add_to_callback("luatexja.jfmglue.whatsit_getinfo", whatsit_callback,
1224                               "luatexja.beginpar.np_info", 1)
1225    luatexbase.add_to_callback("luatexja.jfmglue.whatsit_after", whatsit_after_callback,
1226                               "luatexja.beginpar.np_info_after", 1)
1227
1228 end