OSDN Git Service

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