OSDN Git Service

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