OSDN Git Service

unhbox and unvbox.
[luatex-ja/luatexja.git] / src / ltj-direction.lua
1 --
2 -- src/ltj-direction.lua
3 --
4
5 luatexja.load_module('base');      local ltjb = luatexja.base
6 luatexja.load_module('stack');     local ltjs = luatexja.stack
7 luatexja.load_module('rmlgbm');    local ltjr = luatexja.rmlgbm
8 luatexja.direction = {}
9
10 local attr_dir = luatexbase.attributes['ltj@dir']
11 local attr_icflag = luatexbase.attributes['ltj@icflag']
12
13 local Dnode = node.direct or node
14 local nullfunc = function (n) return n end
15 local to_node = (Dnode ~= node) and Dnode.tonode or nullfunc
16 local to_direct = (Dnode ~= node) and Dnode.todirect or nullfunc
17 local has_attr = Dnode.has_attribute
18 local set_attr = Dnode.set_attribute
19 local insert_before = Dnode.insert_before
20 local insert_after = Dnode.insert_after
21 local getid = (Dnode ~= node) and Dnode.getid or function(n) return n.id end
22 local getsubtype = (Dnode ~= node) and Dnode.getsubtype or function(n) return n.subtype end
23 local getlist = (Dnode ~= node) and Dnode.getlist or function(n) return n.head end
24 local setfield = (Dnode ~= node) and Dnode.setfield or function(n, i, c) n[i] = c end
25 local getfield = (Dnode ~= node) and Dnode.getfield or function(n, i) return n[i] end
26 local node_new = Dnode.new
27 local node_tail = Dnode.tail
28 local node_free = Dnode.free
29 local node_remove = Dnode.remove
30 local node_next = (Dnode ~= node) and Dnode.getnext or node.next
31 local traverse = Dnode.traverse
32 local traverse_id = Dnode.traverse_id
33 local start_time_measure, stop_time_measure 
34    = ltjb.start_time_measure, ltjb.stop_time_measure
35
36 local id_kern = node.id('kern')
37 local id_hlist = node.id('hlist')
38 local id_vlist = node.id('vlist')
39 local id_whatsit = node.id('whatsit')
40 local sid_save = node.subtype('pdf_save')
41 local sid_restore = node.subtype('pdf_restore')
42 local sid_matrix = node.subtype('pdf_setmatrix')
43 local sid_user = node.subtype('user_defined')
44
45 local tex_getcount = tex.getcount
46 local tex_set_attr = tex.setattribute
47 local PROCESSED    = luatexja.icflag_table.PROCESSED
48 local PROCESSED_BEGIN_FLAG = luatexja.icflag_table.PROCESSED_BEGIN_FLAG
49 local PACKED       = luatexja.icflag_table.PACKED
50 local STCK = luatexja.userid_table.STCK
51 local DIR  = luatexja.userid_table.DIR
52 local dir_tate = luatexja.dir_table.dir_tate
53 local dir_yoko = luatexja.dir_table.dir_yoko
54 local dir_dtou = luatexja.dir_table.dir_dtou
55 local dir_node_auto   = luatexja.dir_table.dir_node_auto
56 local dir_node_manual = luatexja.dir_table.dir_node_manual
57
58
59 local get_dir_count
60 -- \tate, \yoko
61 do
62    function get_dir_count()
63       return tex_getcount('ltj@dir@count')
64    end
65    luatexja.direction.get_dir_count = get_dir_count 
66
67    local node_next = node.next
68    local node_set_attr = node.set_attribute
69    local function set_list_direction(v, name)
70       local lv, w = tex.nest[tex.nest.ptr], tex.lists.page_head
71       if lv.mode == 1 and w then
72          if w.id==id_whatsit and w.subtype==sid_user
73          and w.user_id==DIR then
74             node_set_attr(w, attr_dir, v)
75          end
76       else
77          local w = node_next(lv.head)
78          if w then
79             w = to_direct(w)
80             if getid(w)==id_whatsit and getsubtype(w)==sid_user
81             and getfield(w, 'user_id')==DIR then
82                set_attr(w, attr_dir, v)
83             else
84               ltjb.package_error(
85                  'luatexja',
86                  "Use `\\" .. name .. "' at top of list",
87                  'Direction change command by LuaTeX-ja is available\n'
88                     .. 'only when the current list is null.')
89             end
90          else
91             local w = node_new(id_whatsit, sid_user)
92             setfield(w, 'next', hd)
93             setfield(w, 'user_id', DIR)
94             setfield(w, 'type', 110)
95             set_attr(w, attr_dir, v)
96             Dnode.write(w)
97          end
98          tex_set_attr('global', attr_icflag, 0)
99       end
100       tex_set_attr('global', attr_dir, 0)
101    end
102    luatexja.direction.set_list_direction = set_list_direction
103 end
104
105 function luatexja.direction.freeze_list_dir()
106    local w = to_direct(tex.nest[tex.nest.ptr].tail)
107    set_attr(w, attr_dir, -has_attr(w, attr_dir))
108 end
109
110 -- ボックスに dir whatsit を追加
111 local function create_dir_whatsit(hd, gc, new_dir)
112       local w = node_new(id_whatsit, sid_user)
113       setfield(w, 'next', hd)
114       setfield(w, 'user_id', DIR)
115       setfield(w, 'type', 110)
116       set_attr(w, attr_dir, new_dir)
117       tex_set_attr('global', attr_dir, 0)  
118       set_attr(w, attr_icflag, PROCESSED_BEGIN_FLAG)
119       set_attr(hd, attr_icflag, (has_attr(hd, attr_icflag) or 0) + PROCESSED_BEGIN_FLAG)
120       tex_set_attr('global', attr_icflag, 0)
121       return w
122 end
123
124 -- hpack_filter, vpack_filter, post_line_break_filter
125 -- の結果を組方向を明示するため,先頭に dir_node を設置
126 do
127    local function create_dir_whatsit_hpack(h, gc)
128       local hd = to_direct(h)
129       if gc=='fin_row' or gc == 'preamble'  then
130          if hd  then
131             set_attr(hd, attr_icflag, PROCESSED_BEGIN_FLAG)
132             tex_set_attr('global', attr_icflag, 0)
133          end
134          return h
135       else
136          return to_node(create_dir_whatsit(hd, gc, ltjs.list_dir))
137       end
138    end
139
140    luatexbase.add_to_callback('hpack_filter', 
141                               create_dir_whatsit_hpack, 'ltj.create_dir_whatsit', 10000)
142
143    local wh = {}
144    local id_glue, sid_parskip = node.id('glue'), 3
145    local function create_dir_whatsit_vbox(h, gc)
146       local hd = to_direct(h)
147       ltjs.list_dir = get_dir_count()
148       -- remove dir whatsit
149       for x in traverse_id(id_whatsit, hd) do
150          if getsubtype(x)==sid_user and getfield(x, 'user_id')==DIR then
151             wh[#wh+1]=x
152          end
153       end
154       if hd==wh[1] then
155          ltjs.list_dir =has_attr(hd,attr_dir)
156          local x = node_next(hd)
157          if getid(x)==id_glue and getsubtype(x)==sid_parskip then
158             node_remove(hd,x); node_free(x)
159          end
160       end
161       for i=1,#wh do  hd = node_remove(hd, wh[i]); node_free(wh[i]); wh[i] = nil end
162       if gc=='fin_row' or gc == 'preamble'  then
163          if hd  then
164             set_attr(hd, attr_icflag, PROCESSED_BEGIN_FLAG)
165             tex_set_attr('global', attr_icflag, 0)
166          end
167          return to_node(hd)
168       elseif gc=='vtop' then
169          local w = create_dir_whatsit(hd, gc, ltjs.list_dir)
170          -- move  dir whatsit after hd
171          local n = getfield(hd, 'next')
172          setfield(hd, 'next', w); setfield(w, 'next', n)
173          return to_node(hd)
174       else
175          return to_node(create_dir_whatsit(hd, gc, ltjs.list_dir))
176       end
177    end
178    luatexbase.add_to_callback('vpack_filter', 
179                               create_dir_whatsit_vbox, 'ltj.create_dir_whatsit', 1)
180
181    local function create_dir_whatsit_parbox(h, gc)
182       stop_time_measure('tex_linebreak')
183       -- start 側は ltj-debug.lua に
184       local new_dir, hd = ltjs.list_dir, to_direct(h)
185       for line in traverse_id(id_hlist, hd) do
186          set_attr(line, attr_dir, new_dir)
187       end
188       tex_set_attr('global', attr_dir, 0)
189       return to_node(create_dir_whatsit(hd, gc, new_dir))
190    end
191    luatexbase.add_to_callback('post_linebreak_filter', 
192                               create_dir_whatsit_parbox, 'ltj.create_dir_whatsit', 10000)
193
194 end
195
196 -- dir_node に包む方法を書いたテーブル
197 local dir_node_aux
198 do
199    local get_h =function (w,h,d) return h end
200    local get_d =function (w,h,d) return d end
201    local get_h_d =function (w,h,d) return h+d end
202    local get_h_d_neg =function (w,h,d) return -h-d end
203    local get_h_neg =function (w,h,d) return -h end
204    local get_d_neg =function (w,h,d) return -d end
205    local get_w_half =function (w,h,d) return 0.5*w end
206    local get_w_neg_half =function (w,h,d) return -0.5*w end
207    local get_w_neg =function (w,h,d) return -w end
208    local get_w =function (w,h,d) return w end
209    local zero = function() return 0 end
210    dir_node_aux = {
211       [dir_yoko] = { -- yoko を 
212          [dir_tate] = { -- tate 中で組む
213             width  = get_h_d,
214             height = get_w_half,
215             depth  = get_w_half,
216             [id_hlist] = {
217                { 'whatsit', sid_save },
218                { 'rotate', '0 1 -1 0' },
219                { 'kern', get_w_neg_half },
220                { 'box' , get_h },
221                { 'kern', get_w_neg_half },
222                { 'whatsit', sid_restore },
223             },
224             [id_vlist] = {
225                { 'whatsit', sid_save },
226                { 'rotate', '0 1 -1 0' },
227                { 'kern' , zero },
228                { 'box' , get_w_neg },
229                { 'kern', get_h_d_neg},
230                { 'whatsit', sid_restore },
231             },
232          },
233          [dir_dtou] = { -- dtou 中で組む
234             width  = get_h_d,
235             height = get_w,
236             depth  = zero,
237             [id_hlist] = {
238                { 'whatsit', sid_save },
239                { 'rotate', '0 -1 1 0' },
240                { 'kern', get_w_neg },
241                { 'box', get_d_neg },
242                { 'whatsit', sid_restore },
243             },
244             [id_vlist] = {
245                { 'whatsit', sid_save },
246                { 'rotate', '0 -1 1 0' },
247                { 'kern', get_h_d_neg },
248                { 'box', zero },
249                { 'whatsit', sid_restore },
250             },
251          },
252       },
253       [dir_tate] = { -- tate を
254          [dir_yoko] = { -- yoko 中で組む
255             width  = get_h_d,
256             height = get_w,
257             depth  = zero,
258             [id_hlist] = {
259                { 'whatsit', sid_save },
260                { 'rotate', '0 -1 1 0' },
261                { 'kern', get_w_neg },
262                { 'box' , get_d_neg },
263                { 'whatsit', sid_restore },
264             },
265             [id_vlist] = {
266                { 'whatsit', sid_save },
267                { 'rotate', '0 -1 1 0' },
268                { 'kern', get_h_d_neg },
269                { 'box', zero },
270                { 'whatsit', sid_restore },
271             },
272          },
273          [dir_dtou] = { -- dtou 中で組む
274             width  = get_w,
275             height = get_d,
276             depth  = get_h,
277             [id_hlist] = {
278                { 'whatsit', sid_save },
279                { 'rotate', '-1 0 0 -1' },
280                { 'kern', get_w_neg },
281                { 'box', zero },
282                { 'whatsit', sid_restore },
283             },
284             [id_vlist] = {
285                { 'whatsit', sid_save },
286                { 'rotate', '-1 0 0 -1' },
287                { 'kern', get_h_d_neg },
288                { 'box', get_w_neg },
289                { 'whatsit', sid_restore },
290             },
291          },
292       },
293       [dir_dtou] = { -- dtou を
294          [dir_yoko] = { -- yoko 中で組む
295             width  = get_h_d,
296             height = get_w, 
297             depth  = zero,
298             [id_hlist] = {
299                { 'whatsit', sid_save },
300                { 'rotate', '0 1 -1 0' },
301                { 'kern', zero },
302                { 'box', get_h },
303                { 'kern', get_w_neg },
304                { 'whatsit', sid_restore },
305             },
306             [id_vlist] = {
307                { 'kern', zero },
308                { 'whatsit', sid_save },
309                { 'rotate', '0 1 -1 0' },
310                { 'box', get_w_neg },
311                { 'kern', get_h_d_neg },
312                { 'whatsit', sid_restore },
313             },
314          },
315          [dir_tate] = { -- tate 中で組む
316             width  = get_w,
317             height = get_d,
318             depth  = get_h,
319             [id_hlist] = {
320                { 'whatsit', sid_save },
321                { 'rotate', '-1 0 0 -1' },
322                { 'kern', get_w_neg },
323                { 'box', zero },
324                { 'whatsit', sid_restore },
325             },
326             [id_vlist] = {
327                { 'whatsit', sid_save },
328                { 'rotate', ' -1 0 0 -1' },
329                { 'kern', get_h_d_neg }, 
330                { 'box', get_w_neg },
331                { 'whatsit', sid_restore },
332             },
333          },
334       },
335    }
336 end
337
338 -- b に DIR whatsit があればその内容を attr_dir にうつす (1st ret val)
339 -- 2nd ret val はその DIR whatsit
340 local function get_box_dir(b, default)
341    local dir = has_attr(b, attr_dir) or 0
342    local bh = getfield(b,'head') 
343    -- b は insert node となりうるので getlist() は使えない
344    local c
345    for i=1,2 do
346       if bh and getid(bh)==id_whatsit
347       and getsubtype(bh)==sid_user and getfield(bh, 'user_id')==DIR then
348          c = bh
349          if dir==0 then
350             dir = has_attr(bh, attr_dir)
351             set_attr(b, attr_dir, dir)
352             tex_set_attr('global', attr_dir, 0)
353          end
354       end
355       bh = node_next(bh)
356    end
357    return (dir==0 and default or dir), c
358 end
359
360 function luatexja.direction.check_dir(reg_num)
361    local list_dir = get_dir_count()
362    local b = tex.getbox(reg_num)
363    if b then
364       local box_dir = get_box_dir(to_direct(b), dir_yoko)
365       if box_dir%dir_node_auto ~= list_dir%dir_node_auto then
366          ltjb.package_error(
367                  'luatexja',
368                  "Incompatible direction list can't be unboxed",
369                  'I refuse to unbox a box in differrent direction.')
370       end
371    end
372 end
373
374 -- dir_node に包まれている「本来の中身」を取り出し,
375 -- dir_node を全部消去
376 local function unwrap_dir_node(b, head, box_dir)
377    -- b: dir_node, head: the head of list, box_dir: 
378    -- return values are (new head), (next of b), (contents), (dir of contents)
379    local bh = getlist(b)
380    local bc = node_next(node_next(node_next(bh)))
381    local nh, nb
382    node_remove(bh, bc); 
383    Dnode.flush_list(bh)
384    if head then
385       nh = insert_before(head, b, bc)
386       nh, nb = node_remove(nh, b)
387       setfield(b, 'next', nil)
388       setfield(b, 'head', nil)
389       node_free(b)
390    end
391    local shift_old, b_dir, wh = nil, get_box_dir(bc, 0)
392    if wh then
393       Dnode.flush_list(getfield(wh, 'value'))
394       setfield(wh, 'value', nil)
395    end
396    -- recalc. info
397    local info = dir_node_aux[b_dir][box_dir%dir_node_auto][getid(bc)]
398    for _,v in ipairs(info) do 
399       if v[1]=='box' then
400          shift_old = v[2](
401             getfield(bc,'width'), getfield(bc, 'height'), getfield(bc, 'depth'))
402          break
403       end
404    end
405    setfield(bc, 'shift', getfield(bc, 'shift') - shift_old)
406    return nh, nb, bc, b_dir
407 end
408
409 -- is_manual: 寸法変更に伴うものか?
410 local function create_dir_node(b, b_dir, new_dir, is_manual)
411    local info = dir_node_aux[b_dir][new_dir]
412    local w = getfield(b, 'width')
413    local h = getfield(b, 'height')
414    local d = getfield(b, 'depth')
415    local db = node_new(getid(b))
416    set_attr(db, attr_dir, 
417             new_dir + (is_manual and dir_node_manual or dir_node_auto))
418    tex_set_attr('global', attr_dir, 0)
419    set_attr(db, attr_icflag, PROCESSED)
420    set_attr(b, attr_icflag, PROCESSED)
421    setfield(db, 'dir', getfield(b, 'dir'))
422    setfield(db, 'shift', 0)
423    setfield(db, 'width',  info.width(w,h,d))
424    setfield(db, 'height', info.height(w,h,d))
425    setfield(db, 'depth',  info.depth(w,h,d))
426    return db
427 end
428
429 -- 異方向のボックスの処理
430 local make_dir_whatsit
431 do
432    make_dir_whatsit = function (head, b, new_dir, origin)
433       -- head: list head, b: box
434       -- origin: コール元 (for debug)
435       -- return value: (new head), (next of b), (new b), (is_b_dir_node)
436       -- (new b): b か dir_node に被せられた b
437       local bh = getlist(b)
438       local box_dir, dn =  get_box_dir(b, ltjs.list_dir)
439       -- 既に b の中身にあるwhatsit
440       if box_dir==new_dir then
441          return head, node_next(b), b, false
442       elseif  box_dir%dir_node_auto == new_dir  then
443          local bc = node_next(node_next(node_next(bh)))
444          local _, dnc = get_box_dir(b, 0)
445          if dnc then -- free all other dir_node
446             Dnode.flush_list(getfield(dnc, 'value'))
447             setfield(dnc, 'value', nil)
448          end
449          set_attr(b, attr_dir, box_dir%dir_node_auto + dir_node_auto)
450          return head, node_next(b), b, true
451       else
452          local nh, nb, ret, flag
453          if box_dir>= dir_node_auto then -- unwrap
454             local b_dir
455             head, nb, b, b_dir = unwrap_dir_node(b, head, box_dir)
456             bh = getlist(b)
457             if b_dir==new_dir then -- no need to create new dir_node
458                return head, nb, b, false 
459             else box_dir = b_dir end
460          end
461             local db
462             local dnh = getfield(dn, 'value')
463             for x in traverse(dnh) do
464                if has_attr(x, attr_dir)%dir_node_auto == new_dir then
465                   setfield(dn, 'value', to_node(node_remove(dnh, x)))
466                   db=x; break
467                end
468             end
469             Dnode.flush_list(dnh)
470             db = db or create_dir_node(b, box_dir, new_dir, false)
471             local w = getfield(b, 'width')
472             local h = getfield(b, 'height')
473             local d = getfield(b, 'depth')
474             nh, nb =  insert_before(head, b, db), nil
475             nh, nb = node_remove(nh, b)
476             local db_head, db_tail  = nil
477             for _,v in ipairs(dir_node_aux[box_dir][new_dir][getid(b)]) do
478                local cmd, arg, nn = v[1], v[2]
479                if cmd=='kern' then
480                   nn = node_new(id_kern)
481                   setfield(nn, 'kern', arg(w, h, d))
482                elseif cmd=='whatsit' then
483                   nn = node_new(id_whatsit, arg)
484                elseif cmd=='rotate' then
485                   nn = node_new(id_whatsit, sid_matrix)
486                   setfield(nn, 'data', arg)
487                elseif cmd=='box' then
488                   nn = b; setfield(b, 'next', nil)
489                   setfield(nn, 'shift', getfield(nn, 'shift') + arg(w,h,d))
490                end
491                if db_head then
492                   insert_after(db_head, db_tail, nn)
493                   db_tail = nn
494                else
495                   db_head, db_tail = nn, nn
496                end
497             --end
498             setfield(db, 'head', db_head)
499             ret, flag = db, true
500          end
501          return nh, nb, ret, flag
502       end
503    end
504    local function process_dir_node(head, gc)
505       start_time_measure('direction_vpack')
506       local h = to_direct(head)
507       local x, new_dir = h, ltjs.list_dir or dir_yoko
508       while x do
509          local xid = getid(x)
510          if (xid==id_hlist and has_attr(x, attr_icflag)%PROCESSED_BEGIN_FLAG~=PACKED) 
511          or xid==id_vlist then
512             h, x = make_dir_whatsit(h, x, new_dir, 'process_dir_node:' .. gc)
513          else
514             x = node_next(x)
515          end
516       end
517       stop_time_measure('direction_vpack')
518       return to_node(h)
519    end
520    luatexja.direction.make_dir_whatsit = make_dir_whatsit
521    luatexbase.add_to_callback('vpack_filter',
522                               process_dir_node, 'ltj.dir_whatsit', 10001)
523 end
524
525 -- \wd, \ht, \dp の代わり
526 do
527    local getbox, setdimen = tex.getbox, tex.setdimen
528    local function get_box_dim_common(key, s, l_dir)
529       local s_dir, wh = get_box_dir(s, dir_yoko)
530       if s_dir ~= l_dir then
531          local not_found = true
532          for x in traverse(getfield(wh, 'value')) do
533             if l_dir == has_attr(x, attr_dir)%dir_node_auto then
534                setdimen('ltj@tempdima', getfield(x, key))
535                not_found = false; break
536             end
537          end
538          if not_found then
539             local w = getfield(s, 'width')
540             local h = getfield(s, 'height')
541             local d = getfield(s, 'depth')
542             setdimen('ltj@tempdima', 
543                          dir_node_aux[s_dir][l_dir][key](w,h,d))
544          end
545       else
546          setdimen('ltj@tempdima', getfield(s, key))
547       end
548    end
549    local function get_box_dim(key, n)
550       local gt = tex.globaldefs; tex.globaldefs = 0
551       local s = getbox(n)
552       if s then
553          local l_dir = get_dir_count()
554          s = to_direct(s)
555          local b_dir = has_attr(s, attr_dir) or 0
556          if b_dir<dir_node_auto then
557             get_box_dim_common(key, s, l_dir)
558          elseif b_dir%dir_node_auto==l_dir then
559             setdimen('ltj@tempdima', getfield(s, key))
560          else
561             get_box_dim_common(key, 
562                                node_next(node_next(node_next(getlist(s)))), l_dir)
563          end
564       else
565          setdimen('ltj@tempdima', 0)
566       end
567       tex.globaldefs = gt
568    end
569    luatexja.direction.get_box_dim = get_box_dim
570
571    -- return value: (changed dimen of box itself?)
572    local function set_box_dim_common(key, s, l_dir)
573       local s_dir, wh = get_box_dir(s, dir_yoko)
574       if s_dir ~= l_dir then
575          if not wh then
576             wh = create_dir_whatsit(getlist(s), 'set_box_dim', s_dir)
577             setfield(s, 'head', wh)
578          end
579          local db
580          local dnh = getfield(wh, 'value')
581          for x in traverse(dnh) do
582             if has_attr(x, attr_dir)%dir_node_auto==l_dir then
583                db = x; break
584             end
585          end
586          if not db then
587             db = create_dir_node(s, s_dir, l_dir, true)
588             setfield(db, 'next', dnh)
589             setfield(wh, 'value',to_node(db))
590          end
591          setfield(db, key, tex.getdimen('ltj@tempdima'))
592          return false
593       else
594          setfield(s, key, tex.getdimen('ltj@tempdima'))
595          if wh then
596             -- change dimension of dir_nodes which are created "automatically"
597                local bw, bh, bd 
598                   = getfield(s,'width'), getfield(s, 'height'), getfield(s, 'depth')
599             for x in traverse(getfield(wh, 'value')) do
600                local x_dir = has_attr(x, attr_dir)
601                if x_dir<dir_node_manual then
602                   local info = dir_node_aux[s_dir][x_dir%dir_node_auto]
603                   setfield(x, 'width',  info.width(bw,bh,bd))
604                   setfield(x, 'height', info.height(bw,bh,bd))
605                   setfield(x, 'depth',  info.depth(bw,bh,bd))
606                end
607             end
608          end
609          return true
610       end
611    end
612    local function set_box_dim(key)
613       local n = tex_getcount('ltj@tempcnta')
614       local s = getbox(n)
615       if s then
616          local l_dir = get_dir_count()
617          s = to_direct(s)
618          local b_dir = has_attr(s, attr_dir) or 0
619          if b_dir<dir_node_auto then
620             set_box_dim_common(key, s, l_dir)
621          elseif b_dir%dir_node_auto == l_dir then
622             setfield(s, key, tex.getdimen('ltj@tempdima'))
623             if b_dir<dir_node_manual then
624                set_attr(s, attr_dir, b_dir%dir_node_auto + dir_node_manual)
625             end
626          else
627             local sid, sl = getid(s), getlist(s)
628             local b = node_next(node_next(node_next(sl)))
629             local info = dir_node_aux[get_box_dir(b)][b_dir%dir_node_auto]
630             local shift_old
631             for _,v in ipairs(info[sid]) do 
632                if v[1]=='box' then
633                   shift_old = v[2](
634                      getfield(b,'width'), getfield(b, 'height'), getfield(b, 'depth'))
635                   break
636                end
637             end
638            if set_box_dim_common(key, b, l_dir) then
639                local bw, bh, bd 
640                   = getfield(b,'width'), getfield(b, 'height'), getfield(b, 'depth')
641                -- re-calculate shift
642                for i,v in ipairs(info[sid]) do 
643                   if getid(sl)==id_kern then
644                      setfield(sl, 'kern', v[2](bw,bh,bd) )
645                   elseif getid(sl)==sid then
646                      local d = getfield(sl, 'shift')
647                      setfield(sl, 'shift', 
648                               getfield(sl, 'shift') - shift_old + v[2](bw,bh,bd) )
649                   end
650                   sl = node_next(sl)
651                end
652                -- re-calculate dimension of s, if s is created "automatically"
653                if b_dir<dir_node_manual then
654                   setfield(s, 'width',  info.width(bw,bh,bd))
655                   setfield(s, 'height', info.height(bw,bh,bd))
656                   setfield(s, 'depth',  info.depth(bw,bh,bd))
657                end
658             end
659          end
660       end
661    end
662    luatexja.direction.set_box_dim = set_box_dim
663 end
664
665 -- \ifydir, \iftdir, \ifddir
666 do
667    local cat_lp = luatexbase.catcodetables['latex-package']
668    local getbox = tex.getbox
669    local function dir_conditional(n, mode)
670       local s = getbox(n)
671       local res = false
672       if s then
673          s = to_direct(s)
674          local b_dir = get_box_dir(s, dir_yoko)
675          if b_dir<dir_node_auto then
676             res = (b_dir==mode)
677          else
678             local b_dir = get_box_dir(
679                node_next(node_next(node_next(getlist(s)))), dir_yoko)
680             res = (b_dir==mode)
681          end
682       end
683       tex.sprint(cat_lp, '\\if' .. tostring(res))
684    end
685    luatexja.direction.dir_conditional = dir_conditional
686 end
687
688 -- 縦書き用字形への変換テーブル
689 local font_vert_table = {} -- key: fontnumber
690 do
691    local font_vert_basename = {} -- key: basename
692    local function add_feature_table(tname, src, dest)
693       for i,v in pairs(src) do
694          if type(v.slookups)=='table' then
695             local s = v.slookups[tname]
696             if s and not dest[i] then
697                dest[i] = s
698             end
699          end
700       end
701    end
702
703    local function prepare_vert_data(n, id)
704       -- test if already loaded
705       if type(id)=='number' then -- sometimes id is an integer
706          font_vert_table[n] = font_vert_table[id]; return
707       elseif (not id) or font_vert_table[n]  then return
708       end
709       local fname = id.filename
710       local bname = file.basename(fname)
711       if not fname then
712          font_vert_table[n] = {}; return
713       elseif font_vert_basename[bname] then
714          font_vert_table[n] = font_vert_basename[bname]; return
715       end
716       local vtable = {}
717       local a = id.resources.sequences
718       if a then
719          local s = id.shared.rawdata.descriptions
720          for i,v in pairs(a) do
721             if v.features.vert or v.features.vrt2 then
722                add_feature_table(v.subtables[1], s, vtable)
723             end
724          end
725       end
726       font_vert_basename[bname] = vtable
727       font_vert_table[n] = vtable
728    end
729    -- 縦書き用字形への変換
730    function luatexja.direction.get_vert_glyph(n, chr)
731       local fn = font_vert_table[n]
732       return fn and fn[chr] or chr
733    end
734    luatexbase.add_to_callback('luatexja.define_font',
735                               function (res, name, size, id)
736                                  prepare_vert_data(id, res)
737                               end,
738                               'prepare_vert_data', 1)
739
740    local function a (n, dat) font_vert_table[n] = dat end
741    luatexja.rmlgbm.vert_addfunc = a
742
743 end
744
745 -- PACKED の hbox から文字を取り出す
746 -- luatexja.jfmglue.check_box などで使用
747 do
748    local function glyph_from_packed(h)
749       local b = getlist(h)
750       return (getid(b)==id_kern) 
751          and node_next(node_next(node_next(node_next(b)))) or b
752    end
753    luatexja.direction.glyph_from_packed = glyph_from_packed
754 end
755
756 -- adjust and insertion
757 local id_adjust = node.id('adjust')
758 function luatexja.direction.check_adjust_direction()
759    local list_dir = tex_getcount('ltj@adjdir@count')
760    local a = tex.nest[tex.nest.ptr].tail
761    local ad = to_direct(a)
762    if a and getid(ad)==id_adjust then
763       local adj_dir = get_box_dir(ad)
764       if list_dir~=adj_dir then
765          ltjb.package_error(
766                  'luatexja',
767                  'Direction Incompatible',
768                  "\\vadjust's argument and outer vlist must have same direction.")
769          Dnode.last_node()
770       end
771    end
772 end