OSDN Git Service

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