OSDN Git Service

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