OSDN Git Service

a3b4a512a742aa000ce676293beec3a9bbed96a3
[luatex-ja/luatexja.git] / src / ltj-jfmglue.lua
1 --
2 -- luatexja/ltj-jfmglue.lua
3 --
4 luatexbase.provides_module({
5   name = 'luatexja.jfmglue',
6   date = '2015/05/03',
7   description = 'Insertion process of JFM glues and kanjiskip',
8 })
9 module('luatexja.jfmglue', package.seeall)
10 local err, warn, info, log = luatexbase .errwarinf(_NAME)
11
12 luatexja.load_module('base');      local ltjb = luatexja.base
13 luatexja.load_module('stack');     local ltjs = luatexja.stack
14 luatexja.load_module('jfont');     local ltjf = luatexja.jfont
15 luatexja.load_module('direction'); local ltjd = luatexja.direction
16 luatexja.load_module('setwidth');      local ltjw = luatexja.setwidth
17 local pairs = pairs
18
19 local Dnode = node.direct or node
20
21 local nullfunc = function(n) return n end
22 local to_node = (Dnode ~= node) and Dnode.tonode or nullfunc
23 local to_direct = (Dnode ~= node) and Dnode.todirect or nullfunc
24
25 local setfield = (Dnode ~= node) and Dnode.setfield or function(n, i, c) n[i] = c end
26 local getfield = (Dnode ~= node) and Dnode.getfield or function(n, i) return n[i] end
27 local getid = (Dnode ~= node) and Dnode.getid or function(n) return n.id end
28 local getfont = (Dnode ~= node) and Dnode.getfont or function(n) return n.font end
29 local getlist = (Dnode ~= node) and Dnode.getlist or function(n) return n.head end
30 local getchar = (Dnode ~= node) and Dnode.getchar or function(n) return n.char end
31 local getsubtype = (Dnode ~= node) and Dnode.getsubtype or function(n) return n.subtype end
32
33 local has_attr = Dnode.has_attribute
34 local set_attr = Dnode.set_attribute
35 local insert_before = Dnode.insert_before
36 local insert_after = Dnode.insert_after
37 local node_next = (Dnode ~= node) and Dnode.getnext or node.next
38 local round = tex.round
39 local ltjd_make_dir_whatsit = ltjd.make_dir_whatsit
40 local ltjf_font_metric_table = ltjf.font_metric_table
41 local ltjf_find_char_class = ltjf.find_char_class
42 local node_new = Dnode.new
43 local node_copy = Dnode.copy
44 local node_remove = Dnode.remove
45 local node_tail = Dnode.tail
46 local node_free = Dnode.free
47 local node_end_of_math = Dnode.end_of_math
48
49 local id_glyph = node.id('glyph')
50 local id_hlist = node.id('hlist')
51 local id_vlist = node.id('vlist')
52 local id_rule = node.id('rule')
53 local id_ins = node.id('ins')
54 local id_mark = node.id('mark')
55 local id_adjust = node.id('adjust')
56 local id_disc = node.id('disc')
57 local id_whatsit = node.id('whatsit')
58 local id_math = node.id('math')
59 local id_glue = node.id('glue')
60 local id_kern = node.id('kern')
61 local id_penalty = node.id('penalty')
62
63 local id_glue_spec = node.id('glue_spec')
64 local id_jglyph    = 512 -- Japanese character
65 local id_box_like  = 256 -- vbox, shifted hbox
66 local id_pbox      = 257 -- already processed nodes (by \unhbox)
67 local id_pbox_w    = 258 -- cluster which consists of a whatsit
68 local sid_user = node.subtype('user_defined')
69 local lang_ja = luatexja.lang_ja
70
71 local sid_start_link = node.subtype('pdf_start_link')
72 local sid_start_thread = node.subtype('pdf_start_thread')
73 local sid_end_link = node.subtype('pdf_end_link')
74 local sid_end_thread = node.subtype('pdf_end_thread')
75
76 local ITALIC       = luatexja.icflag_table.ITALIC
77 local PACKED       = luatexja.icflag_table.PACKED
78 local KINSOKU      = luatexja.icflag_table.KINSOKU
79 local FROM_JFM     = luatexja.icflag_table.FROM_JFM
80 local PROCESSED    = luatexja.icflag_table.PROCESSED
81 local IC_PROCESSED = luatexja.icflag_table.IC_PROCESSED
82 local BOXBDD       = luatexja.icflag_table.BOXBDD
83 local PROCESSED_BEGIN_FLAG = luatexja.icflag_table.PROCESSED_BEGIN_FLAG
84
85 local attr_icflag = luatexbase.attributes['ltj@icflag']
86 local kanji_skip
87 local xkanji_skip
88 local table_current_stack
89 local list_dir
90 local capsule_glyph
91 local tex_dir
92 local attr_ablshift
93 local set_np_xspc_jachar
94 local set_np_xspc_jachar_hbox
95
96 local ltjs_orig_char_table = ltjs.orig_char_table
97
98 local function get_attr_icflag(p)
99    return (has_attr(p, attr_icflag) or 0)%PROCESSED_BEGIN_FLAG
100 end
101
102 -------------------- Helper functions
103
104 -- This function is called only for acquiring `special' characters.
105 local function fast_find_char_class(c,m)
106    return m.chars[c] or 0
107 end
108
109 -- 文字クラスの決定
110 local slow_find_char_class
111 do
112    local start_time_measure = ltjb.start_time_measure
113    local stop_time_measure = ltjb.stop_time_measure
114    slow_find_char_class = function (c, m, oc)
115       local cls = ltjf_find_char_class(oc, m)
116       if oc~=c and c and cls==0 then
117          return ltjf_find_char_class(c, m)
118       else
119          return cls
120       end
121    end
122 end
123
124 local zero_glue = node_new(id_glue)
125 spec_zero_glue = to_node(node_new(id_glue_spec))
126   -- must be public, since mentioned from other sources
127 local spec_zero_glue = to_direct(spec_zero_glue)
128 setfield(spec_zero_glue, 'width', 0)
129 setfield(spec_zero_glue, 'stretch', 0)
130 setfield(spec_zero_glue, 'shrink', 0)
131 setfield(spec_zero_glue, 'stretch_order', 0)
132 setfield(spec_zero_glue, 'shrink_order', 0)
133 setfield(zero_glue, 'spec', spec_zero_glue)
134
135 local function skip_table_to_spec(n)
136    local g, st = node_new(id_glue_spec), ltjs.fast_get_stack_skip(n)
137    setfield(g, 'width', st.width)
138    setfield(g, 'stretch', st.stretch)
139    setfield(g, 'shrink', st.shrink)
140    setfield(g, 'stretch_order', st.stretch_order)
141    setfield(g, 'shrink_order', st.shrink_order)
142    return g
143 end
144
145
146 -- penalty 値の計算
147 local function add_penalty(p,e)
148    local pp = getfield(p, 'penalty')
149    if pp>=10000 then
150       if e<=-10000 then setfield(p, 'penalty', 0) end
151    elseif pp<=-10000 then
152       if e>=10000 then  setfield(p, 'penalty', 0) end
153    else
154       pp = pp + e
155       if pp>=10000 then      setfield(p, 'penalty', 10000)
156       elseif pp<=-10000 then setfield(p, 'penalty', -10000)
157       else                   setfield(p, 'penalty', pp) end
158    end
159 end
160
161 -- 「異なる JFM」の間の調整方法
162 diffmet_rule = math.two_paverage
163 function math.two_add(a,b) return a+b end
164 function math.two_average(a,b) return (a+b)*0.5 end
165 function math.two_paverage(a,b) return (a+b)/2 end
166 function math.two_pleft(a,b) return a end
167 function math.two_pright(a,b) return b end
168
169 local head -- the head of current list
170
171 local Np, Nq, Bp
172 local widow_Bp, widow_Np -- \jcharwidowpenalty 挿入位置管理用
173
174 local non_ihb_flag -- JFM グルー挿入抑止用 flag
175 -- false: \inhibitglue 指定時 true: それ以外
176
177 -------------------- hlist 内の文字の検索
178
179 local first_char, last_char, find_first_char
180 do
181 local ltjd_glyph_from_packed = ltjd.glyph_from_packed
182 local function check_box(box_ptr, box_end)
183    local p = box_ptr; local found_visible_node = false
184    if not p then
185       find_first_char = false; last_char = nil
186       return true
187    end
188    while p and p~=box_end do
189       local pid = getid(p)
190       if pid==id_kern and getsubtype(p)==2 then
191          p = node_next(node_next(node_next(p))); pid = getid(p) -- p must be glyph_node
192        end
193       if pid==id_glyph then
194          repeat
195             if find_first_char then
196                first_char = p; find_first_char = false
197             end
198             last_char = p; found_visible_node = true; p=node_next(p)
199             if (not p) or p==box_end then
200                return found_visible_node
201             end
202          until getid(p)~=id_glyph
203          pid = getid(p) -- p must be non-nil
204       end
205       if pid==id_kern then
206          local pa = get_attr_icflag(p)
207          if pa==IC_PROCESSED then
208             -- do nothing
209          elseif getsubtype(p)==2 then
210             p = node_next(node_next(p));
211             -- Note that another node_next will be executed outside this if-statement.
212          else
213             found_visible_node = true
214             find_first_char = false; last_char = nil
215          end
216       elseif pid==id_hlist then
217          if PACKED == get_attr_icflag(p) then
218             local s = ltjd_glyph_from_packed(p)
219             if find_first_char then
220                first_char = s; find_first_char = false
221             end
222             last_char = s; found_visible_node = true
223          else
224             if getfield(p, 'shift')==0 then
225                last_char = nil
226                if check_box(getlist(p), nil) then found_visible_node = true end
227             else
228                find_first_char = false; last_char = nil
229             end
230          end
231       elseif pid==id_math then
232          if find_first_char then
233             first_char = p; find_first_char = false
234          end
235          last_char = p; found_visible_node = true
236       elseif pid==id_rule and get_attr_icflag(p)==PACKED then
237          -- do nothing
238       elseif not (pid==id_ins   or pid==id_mark
239                   or pid==id_adjust or pid==id_whatsit
240                   or pid==id_penalty) then
241          found_visible_node = true
242          find_first_char = false; last_char = nil
243       end
244       p = node_next(p)
245    end
246    return found_visible_node
247 end
248
249 function check_box_high(Nx, box_ptr, box_end)
250    first_char = nil;  last_char = nil;  find_first_char = true
251    if check_box(box_ptr, box_end) then
252       local first_char = first_char
253       if first_char then
254          if getid(first_char)==id_glyph then
255             if getfield(first_char, 'lang') == lang_ja then
256                set_np_xspc_jachar_hbox(Nx, first_char)
257             else
258                set_np_xspc_alchar(Nx, getchar(first_char),first_char, 1)
259             end
260          else -- math_node
261             set_np_xspc_alchar(Nx, -1,first_char)
262          end
263       end
264    end
265    return last_char
266 end
267 end
268 -------------------- Np の計算と情報取得
269
270 luatexbase.create_callback("luatexja.jfmglue.whatsit_getinfo", "data",
271                            function (Np, lp, Nq)
272                               if Np.nuc then return Np
273                               else
274                                  return Np  -- your code
275                               end
276                            end)
277 luatexbase.create_callback("luatexja.jfmglue.whatsit_after", "data",
278                            function (stat, Nq, Np) return false end)
279
280 -- calc next Np
281 local calc_np 
282 do
283
284 local traverse = Dnode.traverse
285 local function check_next_ickern(lp)
286    if lp and getid(lp) == id_kern and ITALIC == get_attr_icflag(lp) then
287       set_attr(lp, attr_icflag, IC_PROCESSED)
288       Np.last = lp; return node_next(lp)
289    else
290       Np.last = Np.nuc; return lp
291    end
292 end
293
294 local function calc_np_pbox(lp, last)
295    local first, lpa, nc = (not Np.first), KINSOKU, nil
296    Np.first = Np.first or lp; Np.id = id_pbox
297    set_attr(lp, attr_icflag, get_attr_icflag(lp));
298    while lp ~=last and (lpa>=PACKED) and (lpa<BOXBDD) do
299       if getid(lp)==id_hlist or getid(lp)==id_vlist then
300          head, lp, nc = ltjd_make_dir_whatsit(head, lp, list_dir, 'jfm pbox')
301          Np.first = first and nc or Np.first
302       else
303          nc, lp = lp, node_next(lp)
304       end
305       first, lpa = false, (lp and has_attr(lp, attr_icflag) or 0)
306      -- get_attr_icflag() ではいけない!
307    end
308    Np.nuc = nc
309    lp = check_next_ickern(lp)
310    Np.last_char = check_box_high(Np, Np.first, lp)
311    return lp
312 end
313
314 local ltjw_apply_ashift_math = ltjw.apply_ashift_math
315 local ltjw_apply_ashift_disc = ltjw.apply_ashift_disc
316 local min, max = math.min, math.max
317 local function calc_np_aux_glyph_common(lp)
318    Np.nuc = lp
319    Np.first= (Np.first or lp)
320    if getfield(lp, 'lang') == lang_ja then
321       Np.id = id_jglyph
322       local m, cls = set_np_xspc_jachar(Np, lp)
323       local npi, npf
324       lp, head, npi, npf = capsule_glyph(lp, m, cls, head, tex_dir, lp)
325       Np.first = (Np.first~=Np.nuc) and Np.first or npf or npi
326       Np.nuc = npi
327       return true, check_next_ickern(lp);
328    else
329       Np.id = id_glyph
330       set_np_xspc_alchar(Np, getchar(lp), lp, 1)
331       -- loop
332       local first_glyph, last_glyph = lp
333       set_attr(lp, attr_icflag, PROCESSED); Np.last = lp
334       local y_adjust = has_attr(lp,attr_ablshift) or 0
335       local node_depth = getfield(lp, 'depth') + min(y_adjust, 0)
336       local adj_depth = (y_adjust>0) and (getfield(lp, 'depth') + y_adjust) or 0
337       setfield(lp, 'yoffset', getfield(lp, 'yoffset') - y_adjust)
338       lp = node_next(lp)
339       for lx in traverse(lp) do
340          local lai = get_attr_icflag(lx)
341          if lx==last or  lai>=PACKED then
342             lp=lx; break
343          else
344             local lid = getid(lx)
345             if lid==id_glyph and getfield(lx, 'lang') ~= lang_ja then
346                -- 欧文文字
347                last_glyph = lx; set_attr(lx, attr_icflag, PROCESSED); Np.last = lx
348                y_adjust = has_attr(lx,attr_ablshift) or 0
349                node_depth = max(getfield(lx, 'depth') + min(y_adjust, 0), node_depth)
350                adj_depth = (y_adjust>0) and max(getfield(lx, 'depth') + y_adjust, adj_depth) or adj_depth
351                setfield(lx, 'yoffset', getfield(lx, 'yoffset') - y_adjust)
352             elseif lid==id_kern then
353                local ls = getsubtype(lx)
354                if ls==2 then -- アクセント用の kern
355                   set_attr(lx, attr_icflag, PROCESSED)
356                   lx = node_next(lx) -- lx: アクセント本体
357                   setfield(lx, 'yoffset', getfield(lx, 'yoffset') - (has_attr(lx,attr_ablshift) or 0))
358                   lx = node_next(node_next(lx))
359                elseif ls==0  then
360                   Np.last = lx
361                elseif (ls==1 and lai==ITALIC) then
362                   Np.last = lx; set_attr(lx, attr_icflag, IC_PROCESSED)
363                else
364                   lp=lx; break
365                end
366             else
367                lp=lx; break
368             end
369          end
370       end
371       local r
372       if adj_depth>node_depth then
373             r = node_new(id_rule)
374             setfield(r, 'width', 0); setfield(r, 'height', 0)
375             setfield(r, 'depth',adj_depth); setfield(r, 'dir', tex_dir)
376             set_attr(r, attr_icflag, PROCESSED)
377       end
378       if last_glyph then
379          Np.last_char = last_glyph
380          if r then insert_after(head, first_glyph, r) end
381       else
382          local npn = Np.nuc
383          Np.last_char = npn
384          if r then
385             local nf, nc = getfont(npn), getchar(npn)
386             local ct = (font.getfont(nf) or font.fonts[nf] ).characters[nc]
387             if not ct then -- variation selector
388                node_free(r)
389             elseif (ct.left_protruding or 0) == 0 then
390                head = insert_before(head, npn, r)
391                Np.first = (Np.first==npn) and r or npn
392             elseif (ct.right_protruding or 0) == 0 then
393                insert_after(head, npn, r); Np.last, lp = r, r
394             else
395                ltjb.package_warning_no_line(
396                   'luatexja',
397                   'Check depth of glyph node ' .. tostring(npn) .. '(font=' .. nf
398                      .. ', char=' .. nc .. '),    because its \\lpcode is ' .. tostring(ct.left_protruding)
399                      .. ' and its \\rpcode is ' .. tostring(ct.right_protruding)
400                ); node_free(r)
401             end
402          end
403       end
404       return true, lp
405    end
406 end
407 local calc_np_auxtable = {
408    [id_glyph] = calc_np_aux_glyph_common,
409    [id_hlist] = function(lp)
410       local op, flag
411       head, lp, op, flag = ltjd_make_dir_whatsit(head, lp, list_dir, 'jfm hlist')
412       set_attr(op, attr_icflag, PROCESSED)
413       Np.first = Np.first or op; Np.last = op; Np.nuc = op;
414       if (flag or getfield(op, 'shift')~=0) then
415          Np.id = id_box_like
416       else
417          Np.id = id_hlist
418          Np.last_char = check_box_high(Np, getlist(op), nil)
419       end
420       return true, lp
421    end,
422    [id_vlist] =  function(lp)
423       local op
424       head, lp, op = ltjd_make_dir_whatsit(head, lp, list_dir, 'jfm:' .. getid(lp))
425       Np.first = Np.first or op; Np.last = op; Np.nuc = op;
426       Np.id = id_box_like;
427       return true, lp
428    end,
429    box_like = function(lp)
430       Np.first = Np.first or lp; Np.last = lp; Np.nuc = lp;
431       Np.id = id_box_like;
432       return true, node_next(lp)
433    end,
434    skip = function(lp)
435       set_attr(lp, attr_icflag, PROCESSED)
436       return false, node_next(lp)
437    end,
438    [id_whatsit] = function(lp)
439       local lps = getsubtype(lp)
440       if lps==sid_user then
441          if getfield(lp, 'user_id')==luatexja.userid_table.IHB then
442             local lq = node_next(lp);
443             head = node_remove(head, lp); node_free(lp); non_ihb_flag = false
444             return false, lq;
445          else
446             set_attr(lp, attr_icflag, PROCESSED)
447             luatexbase.call_callback("luatexja.jfmglue.whatsit_getinfo",
448                                      Np, lp, Nq)
449             if Np.nuc then
450                Np.id = id_pbox_w; Np.first = Np.nuc; Np.last = Np.nuc;
451                return true, node_next(lp)
452             else
453                return false, node_next(lp)
454             end
455          end
456       else
457          -- we do special treatment for these whatsit nodes.
458          if lps == sid_start_link or lps == sid_start_thread then
459             Np.first = lp
460          elseif lps == sid_end_link or lps == sid_end_thread then
461             Np.first, Nq.last = nil, lp;
462          end
463          set_attr(lp, attr_icflag, PROCESSED)
464          return false, node_next(lp)
465       end
466    end,
467    [id_math] = function(lp)
468       Np.first, Np.nuc = (Np.first or lp), lp;
469       set_attr(lp, attr_icflag, PROCESSED)
470       set_np_xspc_alchar(Np, -1, lp)
471       local end_math  = node_end_of_math(lp)
472       ltjw_apply_ashift_math(lp, end_math, attr_ablshift)
473       set_attr(end_math, attr_icflag, PROCESSED)
474       Np.last, Np.id = end_math, id_math;
475       return true, node_next(end_math);
476    end,
477    [id_glue] = function(lp)
478       Np.first, Np.nuc, Np.last = (Np.first or lp), lp, lp;
479       Np.id = getid(lp); set_attr(lp, attr_icflag, PROCESSED)
480       return true, node_next(lp)
481    end,
482    [id_disc] = function(lp)
483       Np.first, Np.nuc, Np.last = (Np.first or lp), lp, lp;
484       Np.id = getid(lp); set_attr(lp, attr_icflag, PROCESSED)
485       ltjw_apply_ashift_disc(lp, (list_dir==dir_tate), tex_dir)
486       Np.last_char = check_box_high(Np, getfield(lp, 'replace'), nil)
487       return true, node_next(lp)
488    end,
489    [id_kern] = function(lp)
490       if getsubtype(lp)==2 then
491          Np.first = Np.first or lp
492          set_attr(lp, attr_icflag, PROCESSED); lp = node_next(lp)
493          set_attr(lp, attr_icflag, PROCESSED); lp = node_next(lp)
494          set_attr(lp, attr_icflag, PROCESSED); lp = node_next(lp)
495          set_attr(lp, attr_icflag, PROCESSED);
496          return calc_np_aux_glyph_common(lp)
497       else
498          Np.first = Np.first or lp
499          Np.id = id_kern; set_attr(lp, attr_icflag, PROCESSED)
500          Np.last = lp; return true, node_next(lp)
501       end
502    end,
503    [id_penalty] = function(lp)
504       Bp[#Bp+1] = lp; set_attr(lp, attr_icflag, PROCESSED)
505       return false, node_next(lp)
506    end,
507 }
508 calc_np_auxtable[id_rule]   = calc_np_auxtable.box_like
509 calc_np_auxtable[13]        = calc_np_auxtable.box_like
510 calc_np_auxtable[id_ins]    = calc_np_auxtable.skip
511 calc_np_auxtable[id_mark]   = calc_np_auxtable.skip
512 calc_np_auxtable[id_adjust] = calc_np_auxtable.skip
513
514 function calc_np(last, lp)
515    local k
516    -- We assume lp = node_next(Np.last)
517    Np, Nq, non_ihb_flag = Nq, Np, true
518    -- We clear `predefined' entries of Np before pairs() loop,
519    -- because using only pairs() loop is slower.
520    Np.post, Np.pre, Np.xspc = nil, nil, nil
521    Np.first, Np.id, Np.last, Np.met, Np.class= nil, nil, nil, nil
522    Np.auto_kspc, Np.auto_xspc, Np.char, Np.nuc = nil, nil, nil, nil
523    for k in pairs(Np) do Np[k] = nil end
524
525    for k = 1,#Bp do Bp[k] = nil end
526    while lp ~= last  do
527       local lpa = has_attr(lp, attr_icflag) or 0
528        -- unbox 由来ノードの検出
529       if lpa>=PACKED then
530          if lpa%PROCESSED_BEGIN_FLAG == BOXBDD then
531             local lq = node_next(lp)
532             head = node_remove(head, lp); node_free(lp); lp = lq
533          else
534             return calc_np_pbox(lp, last)
535          end -- id_pbox
536       else
537          k, lp = calc_np_auxtable[getid(lp)](lp)
538          if k then return lp end
539       end
540    end
541    Np=nil
542 end
543 end
544
545 -- extract informations from Np
546 -- We think that "Np is a Japanese character" if Np.met~=nil,
547 --            "Np is an alphabetic character" if Np.pre~=nil,
548 --            "Np is not a character" otherwise.
549 after_hlist = nil -- global
550 local after_alchar, extract_np
551 do
552   local PRE  = luatexja.stack_table_index.PRE
553   local POST = luatexja.stack_table_index.POST
554   local KCAT = luatexja.stack_table_index.KCAT
555   local XSP  = luatexja.stack_table_index.XSP
556   local dir_tate = luatexja.dir_table.dir_tate
557
558 -- 和文文字のデータを取得
559    local attr_jchar_class = luatexbase.attributes['ltj@charclass']
560    local attr_jchar_code = luatexbase.attributes['ltj@charcode']
561    local attr_autospc = luatexbase.attributes['ltj@autospc']
562    local attr_autoxspc = luatexbase.attributes['ltj@autoxspc']
563    --local ltjf_get_vert_glyph = ltjf.get_vert_glyph
564    function set_np_xspc_jachar(Nx, x)
565       local m = ltjf_font_metric_table[getfont(x)]
566       local c, c_glyph = ltjs_orig_char_table[x], getchar(x)
567       c = c or c_glyph
568       local cls = slow_find_char_class(c, m, c_glyph)
569       Nx.met, Nx.class, Nx.char = m, cls, c;
570       if cls~=0 then set_attr(x, attr_jchar_class, cls) end
571       if c~=c_glyph then set_attr(x, attr_jchar_code, c) end
572       Nx.pre  = table_current_stack[PRE + c]  or 0
573       Nx.post = table_current_stack[POST + c] or 0
574       Nx.xspc = table_current_stack[XSP  + c] or 3
575       Nx.kcat = table_current_stack[KCAT + c] or 0
576       Nx.auto_kspc, Nx.auto_xspc = (has_attr(x, attr_autospc)==1), (has_attr(x, attr_autoxspc)==1)
577       return m, cls
578    end
579    function set_np_xspc_jachar_hbox(Nx, x)
580       local m = ltjf_font_metric_table[getfont(x)]
581       local c = has_attr(x, attr_jchar_code) or getchar(x)
582       Nx.met, Nx.char  = m, c; Nx.class = has_attr(x, attr_jchar_class) or 0;
583       Nx.pre  = table_current_stack[PRE + c]  or 0
584       Nx.post = table_current_stack[POST + c] or 0
585       Nx.xspc = table_current_stack[XSP  + c] or 3
586       Nx.kcat = table_current_stack[KCAT + c] or 0
587       Nx.auto_kspc, Nx.auto_xspc = (has_attr(x, attr_autospc)==1), (has_attr(x, attr_autoxspc)==1)
588    end
589
590 -- 欧文文字のデータを取得
591    local floor = math.floor
592    function set_np_xspc_alchar(Nx, c,x, lig)
593       if c~=-1 then
594          local f = (lig ==1) and nullfunc or node_tail
595          local xc, xs = getfield(x, 'components'), getsubtype(x)
596          while xc and xs and xs%4>=2 do
597             x = f(xc); xc, xs = getfield(x, 'components'), getsubtype(x)
598          end
599          c = getchar(x)
600          Nx.pre  = table_current_stack[PRE + c]  or 0
601          Nx.post = table_current_stack[POST + c] or 0
602          Nx.xspc = table_current_stack[XSP  + c] or 3
603       else
604          Nx.pre, Nx.post = 0, 0
605          Nx.xspc = table_current_stack[XSP - 1] or 3
606       end
607       Nx.met = nil
608       Nx.auto_xspc = (has_attr(x, attr_autoxspc)==1)
609    end
610    local set_np_xspc_alchar = set_np_xspc_alchar
611
612    -- change the information for the next loop
613    -- (will be done if Nx is an alphabetic character or a hlist)
614    after_hlist = function (Nx)
615       local s = Nx.last_char
616       if s then
617          if getid(s)==id_glyph then
618             if getfield(s, 'lang') == lang_ja then
619                set_np_xspc_jachar_hbox(Nx, s)
620             else
621                set_np_xspc_alchar(Nx, getchar(s), s, 2)
622             end
623          else
624             set_np_xspc_alchar(Nx, -1, s)
625          end
626       else
627          Nx.pre, Nx.met = nil, nil
628       end
629    end
630
631    after_alchar = function (Nx)
632       local x = Nx.last_char
633       return set_np_xspc_alchar(Nx, getchar(x), x, 2)
634    end
635
636 end
637
638 -------------------- 最下層の処理
639
640 -- change penalties (or create a new penalty, if needed)
641 local function handle_penalty_normal(post, pre, g)
642    local a = (pre or 0) + (post or 0)
643    if #Bp == 0 then
644       if (a~=0 and not(g and getid(g)==id_kern)) then
645          local p = node_new(id_penalty)
646          if a<-10000 then a = -10000 elseif a>10000 then a = 10000 end
647          setfield(p, 'penalty', a)
648          head = insert_before(head, Np.first, p)
649          Bp[1]=p;
650          set_attr(p, attr_icflag, KINSOKU)
651       end
652    else for _, v in pairs(Bp) do add_penalty(v,a) end
653    end
654 end
655
656 local function handle_penalty_always(post, pre, g)
657    local a = (pre or 0) + (post or 0)
658    if #Bp == 0 then
659       if not (g and getid(g)==id_glue) or a~=0 then
660          local p = node_new(id_penalty)
661          if a<-10000 then a = -10000 elseif a>10000 then a = 10000 end
662          setfield(p, 'penalty', a)
663          head = insert_before(head, Np.first, p)
664          Bp[1]=p
665          set_attr(p, attr_icflag, KINSOKU)
666       end
667    else for _, v in pairs(Bp) do add_penalty(v,a) end
668    end
669 end
670
671 local function handle_penalty_suppress(post, pre, g)
672    local a = (pre or 0) + (post or 0)
673    if #Bp == 0 then
674       if g and getid(g)==id_glue then
675          local p = node_new(id_penalty)
676          setfield(p, 'penalty', 10000); head = insert_before(head, Np.first, p)
677          Bp[1]=p
678          set_attr(p, attr_icflag, KINSOKU)
679       end
680    else for _, v in pairs(Bp) do add_penalty(v,a) end
681    end
682 end
683
684 -- 和文文字間の JFM glue を node 化
685 local function new_jfm_glue(m, bc, ac)
686 -- bc, ac: char classes
687    local g = m.char_type[bc][ac]
688    if g then
689       if g[1] then
690          local f = node_new(id_glue)
691          set_attr(f, attr_icflag, g.priority)
692          setfield(f, 'spec', node_copy(g[2]))
693          return f, g.ratio, g.kanjiskip_natural, g.kanjiskip_stretch, g.kanjiskip_shrink
694       else
695          return node_copy(g[2]), g.ratio, false, false, false
696       end
697    end
698    return false, 0
699 end
700
701 -- Nq.last (kern w) .... (glue/kern g) Np.first
702 local function real_insert(g)
703    if g then
704       head  = insert_before(head, Np.first, g)
705       Np.first = g
706    end
707 end
708
709
710 -------------------- 和文文字間空白量の決定
711 local calc_ja_ja_aux
712 do
713    local bg_ag = 2*id_glue - id_glue
714    local bg_ak = 2*id_glue - id_kern
715    local bk_ag = 2*id_kern - id_glue
716    local bk_ak = 2*id_kern - id_kern
717
718    local function blend_diffmet(b, a, rb, ra)
719       return round(diffmet_rule((1-rb)*b+rb*a, (1-ra)*b+ra*a))
720    end
721    calc_ja_ja_aux = function (gb, ga, db, da)
722       if diffmet_rule ~= math.two_pleft and diffmet_rule ~= math.two_pright
723           and diffmet_rule ~= math.two_paverage then
724          db, da = 0, 1
725       end
726       if not gb then
727          if ga then
728             gb = node_new(id_kern); setfield(gb, 'kern', 0)
729          else return nil end
730       elseif not ga then
731          ga = node_new(id_kern); setfield(ga, 'kern', 0)
732       end
733
734       local k = 2*getid(gb) - getid(ga)
735       if k == bg_ag then
736          local bs, as = getfield(gb, 'spec'), getfield(ga, 'spec')
737          -- 両方とも glue.
738          setfield(bs, 'width', blend_diffmet(
739                      getfield(bs, 'width'), getfield(as, 'width'), db, da))
740          setfield(bs, 'stretch', blend_diffmet(
741                      getfield(bs, 'stretch'), getfield(as, 'stretch'), db, da))
742          setfield(bs, 'shrink', -blend_diffmet(
743                      -getfield(bs, 'shrink'), -getfield(as, 'shrink'), db, da))
744          node_free(ga)
745          return gb
746       elseif k == bk_ak then
747          -- 両方とも kern.
748          setfield(gb, 'kern', blend_diffmet(
749                      getfield(gb, 'kern'), getfield(ga, 'kern'), db, da))
750          node_free(ga)
751          return gb
752       elseif k == bk_ag then
753          local as = getfield(ga, 'spec')
754          -- gb: kern, ga: glue
755          setfield(as, 'width', blend_diffmet(
756                      getfield(gb, 'kern'), getfield(as, 'width'), db, da))
757          setfield(as, 'stretch', blend_diffmet(
758                      0, getfield(as, 'stretch'), db, da))
759          setfield(as, 'shrink', -blend_diffmet(
760                      0, -getfield(as, 'shrink'), db, da))
761          node_free(gb)
762          return ga, 0, 0, 0
763       else
764          local bs = getfield(gb, 'spec')
765          -- gb: glue, ga: kern
766          setfield(bs, 'width', blend_diffmet(
767                      getfield(bs, 'width'), getfield(ga, 'kern'), db, da))
768          setfield(bs, 'stretch', blend_diffmet(
769                      getfield(bs, 'stretch'), 0, db, da))
770          setfield(bs, 'shrink', -blend_diffmet(
771                      -getfield(bs, 'shrink'), 0, db, da))
772          node_free(ga)
773          return gb
774       end
775    end
776 end
777
778 local null_skip_table = {0, 0, 0}
779 -- get kanjiskip
780 local get_kanjiskip, kanjiskip_jfm_flag
781 local calc_ja_ja_glue
782 do
783    local KANJI_SKIP   = luatexja.icflag_table.KANJI_SKIP
784    local KANJI_SKIP_JFM   = luatexja.icflag_table.KANJI_SKIP_JFM
785
786    get_kanjiskip_low = function(flag, qm, bn, bp, bh)
787       if flag or (qm.with_kanjiskip and (bn or bp or bh)) then
788          if kanjiskip_jfm_flag then
789             local g = node_new(id_glue);
790             local gx = node_new(id_glue_spec);
791             setfield(gx, 'stretch_order', 0); setfield(gx, 'shrink_order', 0)
792             local bk = qm.kanjiskip or null_skip_table
793             setfield(gx, 'width', bn and (bn*bk[1]) or 0)
794             setfield(gx, 'stretch', bp and (bp*bk[2]) or 0)
795             setfield(gx, 'shrink', bh and (bh*bk[3]) or 0)
796             setfield(g, 'spec', gx)
797             set_attr(g, attr_icflag, KANJI_SKIP_JFM)
798             return g
799          elseif flag then
800             return node_copy(kanji_skip)
801          else
802             local g = node_new(id_glue);
803             local gx = node_new(id_glue_spec);
804             setfield(gx, 'stretch_order', 0); setfield(gx, 'shrink_order', 0)
805             local ks = getfield(kanji_skip, 'spec')
806             setfield(gx, 'width', bn and (bn*getfield(ks, 'width')) or 0)
807             setfield(gx, 'stretch', bp and (bp*getfield(ks, 'stretch')) or 0)
808             setfield(gx, 'shrink', bh and (bh*getfield(ks, 'shrink')) or 0)
809             setfield(g, 'spec', gx)
810             set_attr(g, attr_icflag, KANJI_SKIP_JFM)
811             return g
812          end
813       end
814    end
815    
816    get_kanjiskip = function()
817       if Np.auto_kspc or Nq.auto_kspc then
818          local pm, qm = Np.met, Nq.met
819          if (pm.char_type==qm.char_type) and (qm.var==pm.var) then
820             return get_kanjiskip_low(true, qm, 1, 1, 1)
821          else
822             local gb = get_kanjiskip_low(true, qm, 1, 1, 1)
823             local ga = get_kanjiskip_low(true, pm, 1, 1, 1)
824             return calc_ja_ja_aux(gb, ga, 0, 1)
825          end
826       else
827          local g = node_copy(zero_glue)
828          set_attr(g, attr_icflag, kanjiskip_jfm_flag and KANJI_SKIP_JFM or KANJI_SKIP)
829          return g
830       end
831    end
832
833    calc_ja_ja_glue = function ()
834       local qm, pm = Nq.met, Np.met
835       if (qm.char_type==pm.char_type) and (qm.var==pm.var) then
836          local g, _, kn, kp, kh = new_jfm_glue(qm, Nq.class, Np.class)
837          return g, (Np.auto_kspc or Nq.auto_kspc) and get_kanjiskip_low(false, qm, kn, kp, kh)
838       else
839          local npn, nqn = Np.nuc, Nq.nuc
840          local gb, db, bn, bp, bh 
841             = new_jfm_glue(qm, Nq.class,
842                            slow_find_char_class(Np.char,
843                                                 qm, getchar(npn)))
844          local ga, da, an, ap, ah 
845             = new_jfm_glue(pm,
846                            slow_find_char_class(Nq.char,
847                                                 pm, getchar(nqn)),
848                                                  Np.class)
849          local g = calc_ja_ja_aux(gb, ga, db, da)
850          local k
851          if (pm.char_type==qm.char_type) and (qm.var==pm.var) then
852             gb = get_kanjiskip_low(false, qm, bn, bp, bh)
853             ga = get_kanjiskip_low(false, pm, an, ap, ah)
854             k = calc_ja_ja_aux(gb, ga, db, da)
855          end
856          return g, k
857       end
858    end
859 end
860
861 -------------------- 和欧文間空白量の決定
862
863 -- get xkanjiskip
864 local get_xkanjiskip, xkanjiskip_jfm_flag
865 local get_xkanjiskip_normal, get_xkanjiskip_jfm
866 do
867    local XKANJI_SKIP   = luatexja.icflag_table.XKANJI_SKIP
868    local XKANJI_SKIP_JFM   = luatexja.icflag_table.XKANJI_SKIP_JFM
869
870    get_xkanjiskip_low = function(flag, qm, bn, bp, bh)
871       if flag or (qm.with_kanjiskip and (bn or bp or bh)) then
872          if xkanjiskip_jfm_flag then
873             local g = node_new(id_glue);
874             local gx = node_new(id_glue_spec);
875             setfield(gx, 'stretch_order', 0); setfield(gx, 'shrink_order', 0)
876             local bk = qm.xkanjiskip or null_skip_table
877             setfield(gx, 'width', bn and bk[1] or 0)
878             setfield(gx, 'stretch', bp and bk[2] or 0)
879             setfield(gx, 'shrink', bh and bk[3] or 0)
880             setfield(g, 'spec', gx)
881             set_attr(g, attr_icflag, XKANJI_SKIP_JFM)
882             return g
883          elseif flag then
884             return node_copy(xkanji_skip)
885          else
886             local g = node_new(id_glue);
887             local gx = node_new(id_glue_spec);
888             setfield(gx, 'stretch_order', 0); setfield(gx, 'shrink_order', 0)
889             local ks = getfield(xkanji_skip, 'spec')
890             setfield(gx, 'width', bn and getfield(ks, 'width') or 0)
891             setfield(gx, 'stretch', bp and getfield(ks, 'stretch') or 0)
892             setfield(gx, 'shrink', bh and getfield(ks, 'shrink') or 0)
893             setfield(g, 'spec', gx)
894             set_attr(g, attr_icflag, XKANJI_SKIP_JFM)
895             return g
896          end
897       end
898    end
899    
900    get_xkanjiskip = function(Nn)
901       if (Nq.xspc>=2) and (Np.xspc%2==1) and (Nq.auto_xspc or Np.auto_xspc) then
902          return get_xkanjiskip_low(true, Nn.met, 1, 1, 1)
903       else
904          local g = node_copy(zero_glue)
905          set_attr(g, attr_icflag, xkanjiskip_jfm_flag and XKANJI_SKIP_JFM or XKANJI_SKIP)
906          return g
907       end
908    end
909 end
910
911 -------------------- 隣接した「塊」間の処理
912
913 local function get_OA_skip(is_kanji)
914    local pm = Np.met
915    local g, _, kn, kp, kh = new_jfm_glue(
916       pm,
917       fast_find_char_class((Nq.id == id_math and -1 or 'jcharbdd'), pm), 
918       Np.class)
919    local k
920    if is_kanji==0 then
921       k = (Np.auto_kspc or Nq.auto_kspc) and get_kanjiskip_low(false, pm, kn, kp, kh)
922    elseif is_kanji==1 then
923       k = ((Nq.xspc>=2) and (Np.xspc%2==1) and (Nq.auto_xspc or Np.auto_xspc))
924          and get_xkanjiskip_low(false, pm, kn, kp, kh)
925    end
926    return g, k
927 end
928 local function get_OB_skip(is_kanji)
929    local qm = Nq.met
930    local g, _, kn, kp, kh = new_jfm_glue(
931       qm, Nq.class,
932       fast_find_char_class((Np.id == id_math and -1 or'jcharbdd'), qm))
933    local k
934    if is_kanji==0 then
935       k = (Np.auto_kspc or Nq.auto_kspc) and get_kanjiskip_low(false, qm, kn, kp, kh)
936    elseif is_kanji==1 then
937       k = ((Nq.xspc>=2) and (Np.xspc%2==1) and (Nq.auto_xspc or Np.auto_xspc))
938          and get_xkanjiskip_low(false, qm, kn, kp, kh)
939    end
940    return g, k
941 end
942
943 -- (anything) .. jachar
944 local function handle_np_jachar(mode)
945    local qid = Nq.id
946    if qid==id_jglyph or ((qid==id_pbox or qid==id_pbox_w) and Nq.met) then
947       local g, k
948       if non_ihb_flag then g, k = calc_ja_ja_glue() end -- M->K
949       if not g then g = get_kanjiskip() end
950       handle_penalty_normal(Nq.post, Np.pre, g); 
951       real_insert(g); real_insert(k)
952    elseif Nq.met then  -- qid==id_hlist
953       local g, k
954       if non_ihb_flag then g, k = get_OA_skip(0) end -- O_A->K
955       if not g then g = get_kanjiskip() end
956       handle_penalty_normal(0, Np.pre, g); real_insert(g); real_insert(k)
957    elseif Nq.pre then
958       local g, k
959       if non_ihb_flag then g, k = get_OA_skip(1) end -- O_A->X
960       if not g then g = get_xkanjiskip(Np) end
961       handle_penalty_normal((qid==id_hlist and 0 or Nq.post), Np.pre, g); 
962       real_insert(g); real_insert(k)
963    else
964       local g = non_ihb_flag and (get_OA_skip()) -- O_A
965       if qid==id_glue then handle_penalty_normal(0, Np.pre, g)
966       elseif qid==id_kern then handle_penalty_suppress(0, Np.pre, g)
967       else handle_penalty_always(0, Np.pre, g)
968       end
969       real_insert(g)
970    end
971    if mode and Np.kcat%2~=1 then
972       widow_Np.first, widow_Bp, Bp = Np.first, Bp, widow_Bp
973    end
974 end
975
976
977 -- jachar .. (anything)
978 local function handle_nq_jachar()
979     if Np.pre then
980       local g = non_ihb_flag and get_OB_skip(1) or get_xkanjiskip(Nq) -- O_B->X
981       handle_penalty_normal(Nq.post, (Np.id==id_hlist and 0 or Np.pre), g); real_insert(g)
982    else
983       local g =non_ihb_flag and  (get_OB_skip()) -- O_B
984       if Np.id==id_glue then handle_penalty_normal(Nq.post, 0, g)
985       elseif Np.id==id_kern then handle_penalty_suppress(Nq.post, 0, g)
986       else handle_penalty_always(Nq.post, 0, g)
987       end
988       real_insert(g)
989    end
990 end
991
992 -- (anything) .. (和文文字で始まる hlist)
993 local function handle_np_ja_hlist()
994    local qid = Nq.id
995    if qid==id_jglyph or ((qid==id_pbox or Nq.id == id_pbox_w) and Nq.met) then
996       local g = non_ihb_flag and get_OB_skip(0) or get_kanjiskip() -- O_B->K
997       handle_penalty_normal(Nq.post, 0, g); real_insert(g)
998    elseif Nq.met then  -- Nq.id==id_hlist
999       local g = get_kanjiskip() -- K
1000       handle_penalty_suppress(0, 0, g); real_insert(g)
1001    elseif Nq.pre then
1002       local g = get_xkanjiskip(Np) -- X
1003       handle_penalty_suppress(0, 0, g); real_insert(g)
1004    end
1005 end
1006
1007 -- (和文文字で終わる hlist) .. (anything)
1008 local function handle_nq_ja_hlist()
1009    if Np.pre then
1010       local g = get_xkanjiskip(Nq) -- X
1011       handle_penalty_suppress(0, 0, g); real_insert(g)
1012    end
1013 end
1014
1015
1016 -- Nq が前側のクラスタとなることによる修正
1017 do
1018    local adjust_nq_aux = {
1019       [id_glyph] = function() after_alchar(Nq) end, -- after_alchar(Nq)
1020       [id_hlist]  = function() after_hlist(Nq) end,
1021       [id_pbox]  = function() after_hlist(Nq) end,
1022       [id_disc]  = function() after_hlist(Nq) end,
1023       [id_pbox_w]  = function()
1024                         luatexbase.call_callback("luatexja.jfmglue.whatsit_after",
1025                                                  false, Nq, Np)
1026                      end,
1027    }
1028
1029    function adjust_nq()
1030       local x = adjust_nq_aux[Nq.id]
1031       if x then x()  end
1032    end
1033 end
1034
1035
1036 -------------------- 開始・終了時の処理
1037 do
1038
1039 -- リスト末尾の処理
1040 local JWP  = luatexja.stack_table_index.JWP
1041 local function handle_list_tail(mode)
1042    adjust_nq(); Np = Nq
1043    if mode then
1044       -- the current list is to be line-breaked.
1045       -- Insert \jcharwidowpenalty
1046       Bp = widow_Bp; Np = widow_Np
1047       if Np.first then
1048          handle_penalty_normal(0, table_current_stack[JWP] or 0)
1049       end
1050    else
1051       -- the current list is the contents of a hbox
1052       local npi, pm = Np.id, Np.met
1053       if npi == id_jglyph or (npi==id_pbox and pm) then
1054          local g = new_jfm_glue(pm, Np.class, fast_find_char_class('boxbdd', pm))
1055          if g then
1056             set_attr(g, attr_icflag, BOXBDD)
1057             head = insert_after(head, Np.last, g)
1058          end
1059       end
1060    end
1061 end
1062
1063 -- リスト先頭の処理
1064 local function handle_list_head(par_indented)
1065    local npi, pm = Np.id, Np.met
1066    if npi ==  id_jglyph or (npi==id_pbox and pm) then
1067       if non_ihb_flag then
1068          local g = new_jfm_glue(pm, fast_find_char_class(par_indented, pm), Np.class)
1069          if g then
1070             set_attr(g, attr_icflag, BOXBDD)
1071             if getid(g)==id_glue and #Bp==0 then
1072                local h = node_new(id_penalty)
1073                setfield(h, 'penalty', 10000); set_attr(h, attr_icflag, BOXBDD)
1074             end
1075             head = insert_before(head, Np.first, g)
1076          end
1077       end
1078    end
1079 end
1080
1081 -- initialize
1082 -- return value: (the initial cursor lp), (last node)
1083 local init_var
1084 do
1085    local KANJI_SKIP   = luatexja.icflag_table.KANJI_SKIP
1086    local XKANJI_SKIP   = luatexja.icflag_table.XKANJI_SKIP
1087    local KSK  = luatexja.stack_table_index.KSK
1088    local XSK  = luatexja.stack_table_index.XSK
1089    local dir_yoko = luatexja.dir_table.dir_yoko
1090    local dir_tate = luatexja.dir_table.dir_tate
1091    local attr_yablshift = luatexbase.attributes['ltj@yablshift']
1092    local attr_tablshift = luatexbase.attributes['ltj@tablshift']
1093    local table_pool = {
1094       {}, {}, {first=nil},
1095       { auto_kspc=nil, auto_xspc=nil, char=nil, class=nil,
1096         first=nil, id=nil, last=nil, met=nil, nuc=nil,
1097         post=nil, pre=nil, xspc=nil, }, 
1098       { auto_kspc=nil, auto_xspc=nil, char=nil, class=nil,
1099         first=nil, id=nil, last=nil, met=nil, nuc=nil,
1100         post=nil, pre=nil, xspc=nil, },
1101    }
1102    init_var = function (mode,dir)
1103       -- 1073741823: max_dimen
1104       Bp, widow_Bp, widow_Np, Np, Nq
1105          = table_pool[1], table_pool[2], table_pool[3], table_pool[4], table_pool[5]
1106       for i=1,5 do for j,_ in pairs(table_pool[i]) do table_pool[i][j]=nil end end
1107       table_current_stack = ltjs.table_current_stack
1108
1109       list_dir, tex_dir = (ltjs.list_dir or dir_yoko), (dir or 'TLT')
1110       local is_dir_tate = list_dir==dir_tate
1111       capsule_glyph = is_dir_tate and ltjw.capsule_glyph_tate or ltjw.capsule_glyph_yoko
1112       attr_ablshift = is_dir_tate and attr_tablshift or attr_yablshift
1113       local TEMP = node_new(id_glue) 
1114       -- TEMP is a dummy node, which will be freed at the end of the callback. 
1115       -- ithout this node, set_attr(kanji_skip, ...) somehow creates an "orphaned"  attribute list.
1116
1117       do
1118          kanji_skip = node_new(id_glue); set_attr(kanji_skip, attr_icflag, KANJI_SKIP)
1119          local s = skip_table_to_spec(KSK)
1120          setfield(kanji_skip, 'spec', s)
1121          kanjiskip_jfm_flag = (getfield(s, 'width') == 1073741823)
1122       end
1123
1124       do
1125          xkanji_skip = node_new(id_glue); set_attr(xkanji_skip, attr_icflag, XKANJI_SKIP)
1126          local s = skip_table_to_spec(XSK)
1127          setfield(xkanji_skip, 'spec', s)
1128          xkanjiskip_jfm_flag = (getfield(s, 'width') == 1073741823)
1129       end
1130
1131       if mode then
1132          -- the current list is to be line-breaked:
1133          -- hbox from \parindent is skipped.
1134          local lp, par_indented, lpi, lps  = head, 'boxbdd', getid(head), getsubtype(head)
1135          while lp and ((lpi==id_whatsit and lps~=sid_user)
1136                        or ((lpi==id_hlist) and (lps==3))) do
1137             if (lpi==id_hlist) and (lps==3) then
1138                Np.char, par_indented = 'parbdd', 'parbdd'
1139                Np.width = getfield(lp, 'width')
1140             end
1141             lp=node_next(lp); lpi, lps = getid(lp), getsubtype(lp) end
1142          return lp, node_tail(head), par_indented, TEMP
1143       else
1144          return head, nil, 'boxbdd', TEMP
1145       end
1146    end
1147 end
1148
1149 local ensure_tex_attr = ltjb.ensure_tex_attr
1150 local function cleanup(mode, TEMP)
1151    -- adjust attr_icflag for avoiding error
1152    if tex.getattribute(attr_icflag)~=0 then ensure_tex_attr(attr_icflag, 0) end
1153    node_free(kanji_skip); 
1154    node_free(xkanji_skip); node_free(TEMP)
1155    
1156    if mode then
1157       local h = node_next(head)
1158       if getid(h) == id_penalty and getfield(h, 'penalty') == 10000 then
1159          h = node_next(h)
1160          if getid(h) == id_glue and getsubtype(h) == 15 and not node_next(h) then
1161             return false
1162          end
1163       end
1164    end
1165    return head
1166 end
1167 -------------------- 外部から呼ばれる関数
1168
1169 -- main interface
1170 function main(ahead, mode, dir)
1171    if not ahead then return ahead end
1172    head = ahead;
1173    local lp, last, par_indented, TEMP = init_var(mode,dir)
1174    lp = calc_np(last, lp)
1175    if Np then
1176       handle_list_head(par_indented)
1177       lp = calc_np(last,lp); while Np do
1178          adjust_nq();
1179          local pid, pm = Np.id, Np.met
1180          -- 挿入部
1181          if pid == id_jglyph then
1182             handle_np_jachar(mode)
1183          elseif pm then
1184             if pid==id_hlist then handle_np_ja_hlist()
1185             else handle_np_jachar() end
1186          elseif Nq.met then
1187             if Nq.id==id_hlist then handle_nq_ja_hlist()
1188             else handle_nq_jachar() end
1189          end
1190          lp = calc_np(last,lp)
1191       end
1192       handle_list_tail(mode)
1193    end
1194    return cleanup(mode, TEMP)
1195 end
1196 end
1197
1198 do
1199    local IHB  = luatexja.userid_table.IHB
1200    local BPAR = luatexja.userid_table.BPAR
1201    local node_prev = (Dnode ~= node) and Dnode.getprev or node.prev
1202    local node_write = Dnode.write
1203
1204    -- \inhibitglue
1205    function create_inhibitglue_node()
1206       local tn = node_new(id_whatsit, sid_user)
1207       setfield(tn, 'user_id', IHB)
1208       setfield(tn, 'type', 100)
1209       setfield(tn, 'value', 1)
1210       node_write(tn)
1211    end
1212
1213    -- Node for indicating beginning of a paragraph
1214    -- (for ltjsclasses)
1215    function create_beginpar_node()
1216       local tn = node_new(id_whatsit, sid_user)
1217       setfield(tn, 'user_id', BPAR)
1218       setfield(tn, 'type', 100)
1219       setfield(tn, 'value', 1)
1220       node_write(tn)
1221    end
1222
1223    local function whatsit_callback(Np, lp, Nq)
1224       if Np and Np.nuc then return Np
1225       elseif Np and getfield(lp, 'user_id') == BPAR then
1226          Np.first = lp; Np.nuc = lp; Np.last = lp
1227          return Np
1228       end
1229    end
1230
1231     local function whatsit_after_callback(s, Nq, Np)
1232        if not s and getfield(Nq.nuc, 'user_id') == BPAR then
1233          local x, y = node_prev(Nq.nuc), Nq.nuc
1234          Nq.first, Nq.nuc, Nq.last = x, x, x
1235          if Np then
1236             if Np.met then
1237                Nq.class = fast_find_char_class('parbdd', Np.met)
1238             end
1239             Nq.met = Np.met; Nq.pre = 0; Nq.post = 0; Nq.xspc = 0
1240             Nq.auto_xspc = false
1241          end
1242          head = node_remove(head, y)
1243          node_free(y)
1244       end
1245       return s
1246    end
1247
1248    luatexbase.add_to_callback("luatexja.jfmglue.whatsit_getinfo", whatsit_callback,
1249                               "luatexja.beginpar.np_info", 1)
1250    luatexbase.add_to_callback("luatexja.jfmglue.whatsit_after", whatsit_after_callback,
1251                               "luatexja.beginpar.np_info_after", 1)
1252
1253 end