OSDN Git Service

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